Repository navigation
refactor(metadata-protocol): move the flow save's retired-spelling replay verdict out of protocol.ts into protocol-flow-save-replay.ts (#25467 cut 18) - #26317
Conversation
…play verdict out of protocol.ts (#25467 cut 18) Move the body of flowSaveReplaysRetired unedited into protocol-flow-save-replay.ts, behind a FlowSaveReplayHost that hands over getOverlayRepo as a closure. The class keeps the method, its docblock and declared signature; its body becomes a host literal plus a .call. FLOW_FALLBACK_VERDICT_NOT_REPLAYED gains export in place, and ALL_CONVERSIONS leaves protocol.ts' spec import with its last reader. Claude-Session: https://claude.ai/code/session_016SP1spj613tyGCgxoD1An6 Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
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 86ac882137fa2b624420a75045f8864b228587d0 && git checkout 86ac882137fa2b624420a75045f8864b228587d0
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 3249e204ad1b74ccb23f8295ab5132759418f872 5a7ad6a7b26f2a147007a93bfdfca81950d06a51 && git checkout -B drift-repro 3249e204ad1b74ccb23f8295ab5132759418f872 && git merge --no-ff 5a7ad6a7b26f2a147007a93bfdfca81950d06a51
node scripts/docs-audit/affected-docs.mjs --json 3249e204ad1b74ccb23f8295ab5132759418f872
|
席位核对 · ACCEPT ·
|
| item | reading |
|---|---|
| Shape | Draft, base main, Part of #25467 (cut 18), skip-changeset (no published behaviour moves), mergeable clean. |
| Files | 2 files, +123/−54. flowSaveReplaysRetired moves into protocol-flow-save-replay.ts (#23050 shape B). The class member stays as a delegating stub whose host is a getOverlayRepo closure, so the one call site at the flow fallback is unchanged. Not governed. |
| Fix | A pure move. Seat probe: of the 54 removed protocol.ts lines, 52 reappear in the new module, identical once the class indent is stripped. The other two are the order's: the @objectstack/spec import loses ALL_CONVERSIONS (measured by AST walk as its last reader), and FLOW_FALLBACK_VERDICT_NOT_REPLAYED gains export in place. The module imports that constant as a value from ./protocol.js, which is only read at call time; protocol-authoring-gate.ts is the precedent. Dev: AST compare 642 tokens, 0 differ (the negative control gives 1). .d.ts is raw-identical (08914e65…) before and after the merge, and the positive control moves it. Export tables are equal at 78 names. |
| Order questions | Neither a private-static read nor an error constructor is in the body, so no PRODUCERS row is owed. The durability --list and the hand gates are identical at both ends. The reconcile-call-sites pin counts writes, and the moved body only reads (repo.headAt), so no pin edit is owed. |
| Serial | git merge-tree is clean against #26305 (since merged). Against #26244 (draft) it conflicts only at the import block (:166–168). That resolution is a one-line union spelled out in the PR body, and it is #26244's to make when it next merges main. |
| Evidence | 36 check runs succeeded and 16 were expected skips on this head, 0 failed. The suite ran 21,558 tests at both ends, with 0 lost, 0 added and 0 changed. Derived gates: 51 of 51 exit 0, and --ran reconciles. |
| Carried | Cut 19 goes to the same thread once this lands: resolveWritePackageScope (about 40 lines, reads engine only). |
| 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 18 of the
protocol.tssplit (#24586 ruling A, recorded in6270097840). Cuts 1–17 landed as #25494, #25518, #25538, #25589, #25601, #25623, #25642, #25719, #25731, #25740, #25797, #25939, #25972, #26014, #26104, #26168 and #26264. The claim is6425711631. The scope is the cut 18 proposal of the cut 17 report (6423385238), as the server seat approved it in its dispatch order. The member passed the AST screen and moved unedited.One
ObjectStackProtocolImplementationmember moves unedited into a new module,packages/metadata-protocol/src/protocol-flow-save-replay.ts(#23050 shape B):flowSaveReplaysRetired: the flow save's retired-spelling replay verdict ([Decision] The metadata save door silently rewrites a retired flow spelling (ADR-0087, 2026-08-02) while #23543's ruling says every authoring door refuses it: keep healing, refuse only new legacy, or heal on read and refuse at save? #23903 ruling B′). It answers whether a flow save may keep the full conversion chain, because every retired spelling its body carries is one that a stored row already carries.protocol.tsloses 46 net lines (29,290 to 29,244). 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. It was restarted frommain0a1061a61cafter cut 17 merged. The branch names no issue, so the claim comment is the identity record. It holds two commits:e6ddd60ba6;main8faba89e69(5a7ad6a7b2).That merge touches no file of
packages/metadata-protocol/: the package'ssrc/tree is23f9f0ccat both ends. It does change the roottsconfig.jsonand two other packages'package.json, so the workspace was rebuilt in full and the package re-measured after it.Serial with the file's in-flight PRs
Measured just before this PR was opened: of the 23 open PRs, three touch
packages/metadata-protocol/src/. Each was checked withgit merge-treeagainst this branch's head (5a7ad6a7b2).#26138 edits seven
protocol.*.test.tsfiles, none of which names the moved member,FLOW_FALLBACK_VERDICT_NOT_REPLAYEDor the new module. Its merge-tree reports one conflict, inpackages/spec/src/ui/visibility-alias-keys-retirement.test.ts, which #26138 has withmainitself (a merge-tree againstorigin/mainalone reports it too).#26305 (head
1267e3cb2b) adds 26 lines toprotocol.ts, at:27(imports),:7145and:7540. The merge-tree is clean (exit 0).#26244 (draft, head
c33b95d89f) editsprotocol.ts(+5 / −2). Its hunks are the@objectstack/spec/contractsimport lines at:166–167and a host literal at:23178. The merge-tree conflicts inprotocol.ts, at the import block only. #26244 rewrites lines 166–167. This cut rewrites the adjacent line 168, droppingALL_CONVERSIONSfrom the@objectstack/specimport. Git refuses adjacent edits as one conflict. The resolution is the union of both sides:The
:23178hunk merges clean. The same merge-tree also conflicts inpackages/spec/src/type-alias-convention.pin.test.ts. That conflict is between #26244 andmainitself: a merge-tree of #26244 withorigin/mainalone shows the same conflict. It is not this cut's.Whichever PR lands second takes the three-line resolution above. The
ALL_CONVERSIONSdrop is owed, not optional: the moved body was its last reader inprotocol.ts, so keeping it would leave an unused import.The order's screen (private statics,
new Error()The member passed both checks:
ObjectStackProtocolImplementationidentifier and none onthis.constructor.new Error(and no other error constructor, so noPRODUCERSrow is owed. It has notry, nocatchand no log line.What moves
flowSaveReplaysRetired:6599The proposal said 55 body lines.
The shape (#23050 shape B, as cuts 1–17 apply it)
[#25467 cut 18].FlowSaveReplayHosttypes its one member as the class member itself,ObjectStackProtocolImplementation['getOverlayRepo']. The function types itsthisasPick<FlowSaveReplayHost, 'getOverlayRepo'>. The stub annotates its literal asThisParameterType<typeof flowSaveReplaysRetired>. No member changes visibility.this, withascasts, parentheses and!stripped, and barethischecked too. The body readsgetOverlayRepoonly. It is handed over as a closure that calls the method on the protocol at call time. A test that setsprotocol.getOverlayRepo = ...on the instance (protocol.driver-text-disclosure.test.tsdoes) still reaches the body.Module-level declarations, by reader
Measured by an AST walk over the body's identifiers, against
protocol.ts's top-level declarations and import bindings. Property names are excluded.protocol.tshas itprotocol.tsFLOW_FALLBACK_VERDICT_NOT_REPLAYEDconstat:3358saveMetaItem, base:20930)exportin place; the new module imports it at runtimeALL_CONVERSIONS@objectstack/specimport,:168@objectstack/specapplyConversionsToFlow,ConversionNotice@objectstack/specimportitemDiscriminator@objectstack/metadata-coreimportMetadataWriteFaceFLOW_FALLBACK_VERDICT_NOT_REPLAYEDis not re-exported fromindex.ts, so the package's export table and.d.tsdo not change (see below). The new module's runtime import of it from./protocol.jsfollows the cycle that earlier modules already use (protocol-publish-batch.ts,protocol-stored-overlay-reads.ts). It reads the constant only at call time.The one new statement in
protocol.tsis the import of the new module.The durability
--listcheck-durability-degradation-log-levelexits 0 at both ends with identical output. Its--listis identical once[@:][0-9]+is stripped: 159 lines, and no entry changes file. The body has nocatchand makes no engine call. Its two reads arerepo.headAt(...)on the overlay repository.The published
.d.tsThe series bar is ruling
6348891160, sorted-union identity, measured first.Base
0a1061a61cand heade6ddd60ba6were 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 restored fromHEADunder a trap. Afterwardsgit diff HEADwas empty,git statuswas clean, and both touched blobs equalledHEAD's.The readings were taken on the pre-merge tree and again after merging
main(head5a7ad6a7b2, closure rebuilt by a fullpnpm build, 119/119). They 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. The member stays aprivate NAME;line, and the newly exportedFLOW_FALLBACK_VERDICT_NOT_REPLAYEDdoes not appear.46ccd7e5c4db673652513d1801ec9c9f06bf34a0a7fb6ba97fb2531ef0cc544bat both ends (80 string-literal union runs sorted, using the cut 12 normaliser).index.jshashes8732ec7d7e0d5cd5…at base and74a6c78db7e9bcc6…at head. The.call(hostsite offlowSaveReplaysRetiredoccurs 0 times in the base bundles and once in the head bundles, ESM and CJS alike.function flowSaveReplaysRetired(appears 0 times at base and once at head.privatewas dropped fromflowSaveReplaysRetiredon disk (restored fromHEADunder a trap) and the package rebuilt. Both.d.tsfiles move tobd3b47c8b435f536…(normalised164c324a9516551e…) and printflowSaveReplaysRetired(request: { name: string; item?: unknown; packageId?: string | null; …. So the bar can see a change on this member.9f18a0177ac736ed…on all four. Neither the moved function,FlowSaveReplayHostnorFLOW_FALLBACK_VERDICT_NOT_REPLAYEDappears.No changeset:
skip-changeset, per ruling6348891160.Proof of move-only
Body, by AST token. The compare reads the TypeScript compiler's AST leaf tokens, skipping JSDoc, over the parameters (less the added
this), the return type and the body. It runs baseprotocol.tsat0a1061a61cagainst the head module:Negative control. One token was changed in an in-memory copy of the head module (
mode === 'draft' ? 'draft' : 'active'tomode !== 'draft' ? …, one occurrence):(request: {…}, mode: …): Promise<boolean> {). The module splits the two parameters onto their own lines. The token compare shows that this changes whitespace only.protocol.ts. 244 of the 246 base statements are byte-identical, leading comments included. The other two are the@objectstack/specimport, which losesALL_CONVERSIONS, andFLOW_FALLBACK_VERDICT_NOT_REPLAYED, which gainsexport. Head adds one statement, the import of the new module.protocol-flow-save-replay.tshas 115 lines.Readers of the moved lines
Every test and script that reads
protocol.tsby path (70 files) was grepped for the member name and for the identifiers its body uses:ALL_CONVERSIONS,applyConversionsToFlow,FLOW_FALLBACK_VERDICT_NOT_REPLAYED,getOverlayRepo,headAt,excludeConversionIds,includeRetired,retiredFromLoadPath, and the local names. So were all test files inpackages/andui/, anddocs/qa/.protocol.view-expansion-reconcile-call-sites.test.tsreadsprotocol.tsby path. It holds a census of stored-row WRITES (repo.put/delete/promoteDraft/restoreVersion(, rawsys_metadataengine writes). It also pins that a repository is bound only asconst repo = this.getOverlayRepo();. The moved body bindsrepothat way and calls onlyrepo.headAt(, a read. So the census loses no write, and no pin edit is owed. Six earlier seam modules already bindthis.getOverlayRepo()outsideprotocol.tson the same footing (protocol-drafts.ts,protocol-publish-batch.ts,protocol-save-door-refusals.tsand others). The pin passes at both ends, in the package suite.protocol.driver-text-disclosure.test.tsandprotocol.object-registry-write-through-spelling.test.tsmatch ongetOverlayRepo, but as an instance override or call, not the moved text.packages/rest/src/meta-flow-save-retired-spelling.test.tsnamesflowSaveReplaysRetiredin its header and drives it through the save door. It was run at head after the full build: 1 file, 10 tests passed.docs/qa/.check-adr-anchorsexits 0 with identical output at both ends. The body cites[#23903, ruling B′], an issue citation that moves with it, and no ADR.check-engine-double-contract). It exits 0 at both ends. TheupdateData/deleteDataseams keep their entries at new line numbers (13976 → 13930, 14001 → 13955).productionFilesis 3,739 at base and 3,741 at head. Of the +2, one is the new file and one is the gitignoredpackages/spec/src/migrations/registry.tsthat the 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-parityandcheck-durability-degradation-log-level.check-nul-bytesdiffers only in the scanned file count, +1. The base-end readings were taken in a clean detached worktree at0a1061a61c.check-governed-merges --branch claude/project-thread-ybjm3hfinds 0 of 2 paths in the register, so the change is NOT governed. At 177 changed lines it is under the 3,000-line human-merge bar.Verification
tsc --noEmitinpackages/metadata-protocolexits 0 before and after the merge.pnpm exec eslint --no-inline-config packages/metadata-protocolexits 0 with no output. It covers the whole touched package, which has nolintscript; these are the rootlintscript's flags.packages/metadata-protocolsuite, base and head, compared per test name and status. Each run used vitest--maxWorkers=2with the JSON reporter under the shared verify lock, and both legs gaveVERDICT command-exit 0. The runs are on the pre-merge tree; the merge changed no file of the package.0a1061a61c: 21,558 tests (21,539 passed, 0 failed, 19 skipped), 269 files.e6ddd60ba6: 21,558 tests (21,539 passed, 0 failed, 19 skipped), 269 files.src/swapped tree-only from0a1061a61c(the new module removed) and restored fromHEADunder a trap.check-platform-checklist --changed-from origin/mainon the merged tree: OK, with 1 anchored file in the change set, 29 anchors judged and 0 lost.dispatch-gates --commands --repo objectstack-ai/objectstackderives 51 gates on 2 changed paths from the merged tree5a7ad6a7b2; all 51 exit 0 after the fullpnpm build(119/119).dispatch-gates --ran: 51 derived, 51 run, 0 NOT-MEASURED, 0 UNRUN, exit 0. The derivation flags the tree as behindmain, but only forscripts/check-cli-test-child-env.mjsandscripts/sdui-manifest.sources/i18n.json; neither feeds one of these 51 families. Re-deriving against the newermaingives the same 51-line list, somainwas not merged again.Carry-forward
Unchanged from cut 17. These stay in
protocol.ts:probeRecord,getMetaItemCached,findReferencesToMeta,packagelessAddress,refuseUnmintableMetaType,metaTypeNamespaceExists,viewContainerNameCollisionRefusalwithoverlaidShippedContainerViewNames,reportPageRequiresAbsentAtLoad, the 7applyRegistryWriteThroughcallers,readCodeLayerForCarryForwardandensureObjectStorage.refusePackagedBaseRemoval,packagedBaseRefusal,runPublishSideEffects,promoteDraftForPublish,publishPackageDraftsanddiscardPackageDrafts.warnClosureReadUnavailable.🤖 Generated with Claude Code
https://claude.ai/code/session_016SP1spj613tyGCgxoD1An6