Repository navigation
refactor(metadata-protocol): add the protocol host split machinery and move one pilot member through it (#25467 E1) - #29488
Conversation
… tool with its closed rewrite set check protocol-host.ts declares ProtocolHost, the one object a body moved out of ObjectStackProtocolImplementation reaches the class through. scripts/host-split.mjs moves a member's body under exactly three rewrites (this.x -> host.x, a private static call -> host.fn(...), this.constructor -> host) and checks a moved body is the base body modulo that set, by AST identity over every token and by text. host-split.test.mjs pins the set, each refusal and fifteen negative controls. Claude-Session: https://claude.ai/code/session_01ADHkmR4MjTzeUv5x6tNkbk Co-authored-by: Claude <noreply@anthropic.com>
…hrough the host, and read moved bodies through it The pilot member's body moves to protocol-tenant-authored-write.ts; the method keeps its docblock and signature and becomes a stub. The class's static block builds the host. The read-verb fold pin, the lookupArtifactItem call-site pin and the read-seam census follow host.x(...) calls and fn(protocolHost(this), ...) stubs into every split module. Claude-Session: https://claude.ai/code/session_01ADHkmR4MjTzeUv5x6tNkbk Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ADHkmR4MjTzeUv5x6tNkbk Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ADHkmR4MjTzeUv5x6tNkbk Co-authored-by: Claude <noreply@anthropic.com>
…shared comment mask Claude-Session: https://claude.ai/code/session_01ADHkmR4MjTzeUv5x6tNkbk Co-authored-by: Claude <noreply@anthropic.com>
…sk they import Claude-Session: https://claude.ai/code/session_01ADHkmR4MjTzeUv5x6tNkbk Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 10 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 8 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 10 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 4420febf4869263598b7c68c186f561842734a80 && git checkout 4420febf4869263598b7c68c186f561842734a80
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 04e0c2c0c797ce5a9a0c926982be449eeb7523ea 710b8fe34ef34c431e12595fc7ef91d7ace3f97b && git checkout -B drift-repro 04e0c2c0c797ce5a9a0c926982be449eeb7523ea && git merge --no-ff 710b8fe34ef34c431e12595fc7ef91d7ace3f97b
node scripts/docs-audit/affected-docs.mjs --json 04e0c2c0c797ce5a9a0c926982be449eeb7523ea
|
|
domain:server#5 · os-steve · session_01ADHkmQKwKFCp1m6Pb6Q8c1 维护者速读
席位意见:与 E1 派发相符。 席位核对ACCEPT ·
|
Requested by Steve · project thread
Part of #25467
Clause-②: no
Metadata-protocol: none
维护者速读
protocol.ts的类体拆分搭好“宿主”机制:新增protocol-host.ts(被搬出的方法体经它访问类成员)和工具scripts/host-split.mjs(只允许三种封闭改写,并逐个 token 校验搬出的方法体与原方法体一致);只搬了一个试点方法tenantAuthoredWriteRefusal,没有成段搬移。src/protocol.ts(33,065 lines, +10,169 in 30 days) along itsObjectStackProtocolImplementationseams, one move-only slice per PR #25467 裁决 E 的第二步 E1:先把搬移规则、校验工具、两个按路径 pin 和读缝普查做成能穿过宿主的形态,E2 才能成段搬移而不让守卫失明。index.d.ts(ESM 与 CJS)与 main 构建出的逐字节相同。回滚即整体 revert。packages/metadata-protocol/src/protocol-host.ts;packages/metadata-protocol/scripts/host-split.mjs文件头(封闭改写集);protocol.ts里类的static {}块。pnpm --filter @objectstack/metadata-protocol exec vitest run scripts/host-split.test.mjs src/protocol.read-verb-canonical-fold.test.ts src/protocol.lookup-artifact-item-call-sites.test.ts,预期 3 个文件全绿。getMetaTypes()显式返回类型已由席位裁定移出 E1(保持推断,.d.ts逐字节不变),另立小步处理。What changes
Step E1 of ruling E (6537939895, item E1), claim 6578609040. The host split machinery, one pilot member, no section moves.
The host.
src/protocol-host.tsdeclaresProtocolHost: every class member a moved body reads, each typed AS that member (ObjectStackProtocolImplementation['x'], or(typeof ObjectStackProtocolImplementation)['x']for a static). The class builds the host in astatic {}block, the one place that can read its private members; a static block adds no member, so the published.d.tsdoes not move. A method is forwarded as a closure called at call time (a spy or test double on the instance is still the one a moved body reaches), a field as a getter (plus a setter when written), a private static as a closure to the static.protocolHost(this)builds one host per instance and caches it in a WeakMap.The closed rewrite set and its check.
packages/metadata-protocol/scripts/host-split.mjs(a tool, not a gate) has three commands:survey,move MEMBER MODULEandcheck BASE_REF MEMBER MODULE. Inside a moved body exactly three rewrites are allowed:this.xbecomeshost.x(an instance member);ObjectStackProtocolImplementation.fn(…)becomeshost.fn(…);this.constructorbecomeshost.checkproves AST identity modulo exactly that set by walking every token (operators and keywords included), and also proves the body text, comments included, equals the base body rewritten and re-indented; it checks the stub, the method's unchanged signature, the function's parameters and return type, and that thePicknames exactly the members read. Everything outside the set is a refusal: a barethisorthis[…],super, the method'sarguments, an identifier already namedhost, a private static read as a value or a private static field,this.constructornot followed by a static, an undeclared member, generators and destructured parameters.scripts/host-split.test.mjs(35 tests, collected by the package's vitest run) pins each rewrite kind, each refusal, and fifteen negative controls on the moved function plus three on the stub: a changed operator (??to||,===to!==),consttolet, a changed literal, a wrong rewrite target, an unrewritten member or static, an extra statement, a changed comment, aPicknaming one member too many or too few, a renamed host parameter, a retyped parameter, a changed return type, reordered stub arguments, a stub handing overthis, and an edited docblock. Writing these tests found one tool bug, fixed here:arguments.lengthescaped theargumentsrefusal.The pilot.
tenantAuthoredWriteRefusal(5 rewrite sites, all kind 1) moved tosrc/protocol-tenant-authored-write.ts.node scripts/host-split.mjs check 838a802bf3 tenantAuthoredWriteRefusal protocol-tenant-authored-write.tsreadsok: true, textIdentical: true. Ablation, withfolded.packageId === stampedchanged to!==in the module, then restored:The pins and the census read through the host, for every module. These are the forced test edits:
src/protocol.read-verb-canonical-fold.test.ts:delegateBodyfollows a stub (fn(protocolHost(this), …)or the per-member series'fn.call(host, …)) to the moved function in the module protocol.ts imports it from. A new assertion requires that the pilot is read through the split. Before the edit the pin was red on the pilot (expected [ 'tenantAuthoredWriteRefusal' ] to deeply equal []). Ablation, removing the fold from the pilot module's body (const folded = request;), then restored:src/protocol.lookup-artifact-item-call-sites.test.ts: the site scan covers protocol.ts plus everyprotocol-*.tsmodule (comments blanked through the sharedscripts/js-comment-mask.mjs), and a new test pins that the module list includes the host's and the pilot's. Ablation, planting an unrecordedlookupArtifactItem(call in the pilot module, then restored:maskCommentsfromscripts/js-comment-mask.mjs(check:comment-mask-adoption refused a private stripper), soturbo.json's@objectstack/metadata-protocol#testinputs andscripts/cross-package-test-inputs.mjsdeclare that file (check:cross-package-test-inputs). Two config lines outside the claim's file surface, forced by those gates.scripts/check-durability-degradation-log-level.mjs(HOST_SPLIT): HOST CALL now also acceptshost.x(…)wherehostis the function's first parameter, typed by a host interface declared in the module or imported by name from a split module, with members typed AS the class's instance or static members. STUB acceptsfn(protocolHost(this), …). There are 10 new self-test cases (flags, negative controls, BOUND pairs); the self-test reads 75 cases. Ablation, renaminghostParamandhostOf, then restored: 4 failures (the 3 E1 flags plus the deeper BOUND), eachexpected violation=true seams=1, got violation=false count=0 seams=0. The census reads 100 seams against main's 98. The two new lines areprotocol-packageless-overlay-migration.ts'smigratePackagelessOverlays()guardingstoredPairRow(), a helper that already took its module's own host as a first parameter namedhost. Both read "no invented answer", so no verdict or ledger row moves. This is recorded in the docblock, as fix(scripts): the read-seam census follows a moved protocol.ts body across its host split (#25467 census extension) #28058 recorded its 93 to 95.The published
.d.tsBuilt from main and from this head, twice: before the merge of main (main 838a802, sha256
ef70d5a4…004a8on both sides) and after it (main 7f277a8, sha256de90eb4e…ccf88on both sides, normaliser 283d37f67…7e2c0on both). Each timeindex.d.tsis byte-identical to main's, and ESM equals CJS. A trial merge of the later main 6af3d29 (which edits protocol.ts again), built and then discarded, also reads byte-identical (0e8c1a2c…ef526on both sides). The declaration-emit comment leak intogetMetaTypes()'s inferred return type struck this diff once: a[#28001]docblock printed insideexamples?. It was cleared the way E0 cleared it, by the length of a comment line above the leaking offset (one word in the pilot's import comment); a 1 to 8 character probe read raw-equal at 5 or more.Deviation the seat ruled:
getMetaTypes()explicit return type dropped from E1E0 carried this item into E1 with the condition "normaliser 2 hashes equal". The inferred return type prints as 793 lines of
.d.ts, and an explicit annotation prints as written, so the two meet only if that 793-line literal is pasted inline. Measured: that gives a byte-identical.d.tsand green tsc, but 793 duplicated lines whose drift is silent in the widening direction.Seat 5 ruled option C: the return type stays inferred, so
index.d.tsstays byte-identical (the E-series invariant), and every E2 cut reads the raw.d.tsdiff ofgetMetaTypes(). A named composed type with a type-equality pin against main's.d.ts(option B) becomes its own small step after E1 lands, filed by the seat. Theturbo.jsonandscripts/cross-package-test-inputs.mjslines above are accepted as forced.Serial
Re-read just before opening against main 6af3d29 (34 commits past this branch's one merge of main at 7f277a8;
git merge-treewith main is clean). Open PRs whose own diff touchespackages/metadata-protocol/src/protocol.ts: #28731 only, andgit merge-treewith this head is clean. No open PR touches the other files here.Gates
On 710b8fe:
node scripts/pm/dispatch-gates.mjs --commandsderived 82 commands, and each recordsexit 0.--ranreports 82 derived, 82 run, 0 NOT-MEASURED. The first round, on cf94e93's parent, caught the two items above: check:comment-mask-adoption and check:cross-package-test-inputs. check:dual-build-cjs-loads exited 3 there (unbuilt packages); they were built and it now exits 0. The package's typecheck and test, through os-verify-lock, read Test Files 305 passed, 4 skipped; Tests 22191 passed, 26 skipped. The 11 direct dependents' typecheck exits 0. Lint (eslint packages/metadata-protocolplus the three root scripts,--no-inline-config) exits 0. CI is not read.Generated by Claude Code