Skip to content

refactor(metadata-protocol): move the publish batch's seed apply and its pending-declaration collection out of protocol.ts into protocol-publish-batch.ts (#25467 cut 14) - #26014

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-ybjm3h
Oct 26, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-ybjm3h

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Leehom · project thread

Part of #25467
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6270097840

Cut 14 of the protocol.ts split (#24586 ruling A, recorded in 6270097840). Cuts 1–13 landed as #25494, #25518, #25538, #25589, #25601, #25623, #25642, #25719, #25731, #25740, #25797, #25939 and #25972. The claim is 6410996628; the scope is the cut 14 proposal of the cut 13 report, as the server seat approved it in its dispatch order, unnarrowed.

Two ObjectStackProtocolImplementation members move unedited into a new module, packages/metadata-protocol/src/protocol-publish-batch.ts (#23050 shape B): the publish batch's seed apply applySeedBodies and the collection of the batch's own pending declarations collectBatchPendingDeclarations. protocol.ts loses 132 net lines (29,620 to 29,488). The cut is move-only, and the card stays open for later cuts.

Branch claude/project-thread-ybjm3h is the harness-pinned branch for this thread, restarted from main 0479cd6a0a after cut 13 merged. It names no issue, so the claim comment is the identity record. It holds the move commit c2c08b1ae and two merges of main (4003f5015 with 05f7758f0, 721669cb1 with a4eae0649). Neither merge touches packages/metadata-protocol/: the package's src/ tree is 941b828d at 0479cd6a0a, at a4eae0649 and at the base of this diff.

Serial with the file's in-flight PRs. Measured just before this PR was opened: of the 20 open PRs, two touch packages/metadata-protocol/src/, and neither touches protocol.ts or the new module. #25989 edits protocol.runtime-authoring-gate.test.ts (behaviour, no by-path read) and #25986 edits protocol-package-install.ts and its test.

What moves

Member Base line Visibility Body lines AST tokens (params + return + body)
applySeedBodies :23330 private 79 400
collectBatchPendingDeclarations :23526 private 71 258

150 body lines (the order's estimate was about 146).

The shape (#23050 shape B, as cuts 1–13 apply it)

  • The class keeps every member with its name, docblock, signature and position. Each body is now one host literal plus a .call, marked [#25467 cut 14].
  • Typing. PublishBatchHost types every member as the class member itself (an indexed access on ObjectStackProtocolImplementation). Each moved function types its this as the Pick of the host over exactly the members its body reads. Each stub annotates its literal as ThisParameterType of typeof fn. No member changes visibility.
  • Members read, measured by AST walk (property and element accesses on this, with as casts and parentheses stripped):
    • applySeedBodies reads engine (a live getter) and getMetaItem (a closure). The getMetaItem read was not in the proposal. The body spells it (this as any).getMetaItem(...) inside the loader's metadata adapter, so a this.NAME screen does not see it. Without it in the host, the adapter would call undefined at runtime while tsc stayed green (the any cast hides it). The closure calls getMetaItem on the protocol at call time, so a spy set on the instance is still the one the adapter reaches.
    • collectBatchPendingDeclarations reads getOverlayRepo and warnClosureReadUnavailable, both as closures.
  • What stays. warnClosureReadUnavailable stays in the class, as the order said: it writes the instance field closureReadWarned. Neither moved body reads a private-static member: the AST walk found no access on the ObjectStackProtocolImplementation identifier and none on this.constructor, in either body.
  • Every other caller still calls the class method. runPublishSideEffects and publishPackageDrafts call applySeedBodies; publishPackageDrafts calls collectBatchPendingDeclarations.

Module-level declarations, by reader

  • Two gain export in place, none moves. declaresClientRefusal (read by applySeedBodies' catch) and memberOption (read by collectBatchPendingDeclarations). canonicalMetaType, clientFacingFailureText and seedRequestValidationError were already exported. None is re-exported from index.ts, so the package's export table does not change (below).
  • Imports. CLOSURE_CONTEXT_KEY_BY_TYPE loses its last reader in protocol.ts and leaves the ./runtime-authoring-gate.js import; the new module imports it from there, beside the type RuntimePendingDeclarations. The body's two dynamic imports (./seed-loader.js, @objectstack/spec/data) move unchanged and resolve from the same directory. The one new statement in protocol.ts is the import of the new module. The new module's runtime import from ./protocol.js is the cycle earlier modules already use (protocol-batch-data.ts, protocol-drafts.ts, protocol-publish-backfill.ts among them): it reads only function declarations, and only at call time.

The published .d.ts

The series bar is ruling 6348891160, sorted-union identity, measured first. Base and head were built with pnpm --filter @objectstack/metadata-protocol build against one dependency tree: the closure built at head by a full pnpm build (119/119), then the package's src/ swapped tree-only to 0479cd6a0a for the base build and restored from HEAD under a trap (git diff HEAD empty, git status clean and both touched blobs equal to HEAD's after). Measured twice: on the pre-merge tree (base 0479cd6a0a, head c2c08b1ae) and after merging main (head 4003f5015, closure rebuilt by a full pnpm build, 119/119). The readings below hold for both.

  • Raw sha256: 08914e65f936a2b031c3f3f215481a97f53d132eefd869e4f42d928947ebe173 at base and at head, on both dist/index.d.ts and dist/index.d.cts, 775,874 bytes each. Raw identity holds, so sorted-union identity holds too. Both members stay private NAME; lines.
  • Normalised sha256: 46ccd7e5c4db673652513d1801ec9c9f06bf34a0a7fb6ba97fb2531ef0cc544b at both ends (80 string-literal union runs sorted; the cut 12 normaliser).
  • The two builds differ where they should: index.js hashes 46f350a33fcbf439… at base and a63f0a4b6b52b222… at head; each function's .call(host site occurs 0 times in the base bundles and once in the head bundles (ESM and CJS alike).
  • Positive control on the head tree (on disk, restored from HEAD under a trap, hash equal to HEAD's after): private dropped from applySeedBodies and the package rebuilt. Both .d.ts files move to a0203ce8529250b7… and print the member's full signature (applySeedBodies(bodies: unknown[]): Promise<{ success: boolean; inserted: number; … }>), so the bar can see a change on these members.
  • Export tables. The ESM and CJS bundles, loaded at runtime from the package, each export the same 78 names at base and head; the sorted list hashes to 9f18a0177ac736ed… on all four. Neither moved function, the host type, declaresClientRefusal, memberOption nor CLOSURE_CONTEXT_KEY_BY_TYPE appears.

No changeset: skip-changeset, per ruling 6348891160.

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), base protocol.ts at 0479cd6a0a against the head module:

applySeedBodies: base tokens 400, head tokens 400, differ 0, text-equal(ws-collapsed, comments incl.) true
collectBatchPendingDeclarations: base tokens 258, head tokens 258, differ 0, text-equal(ws-collapsed, comments incl.) true
TOTAL tokens=658 differ=0 members=2
CLASS members base=300 head=300 order-same=true byte-identical=298 stubs-with-identical-docblock+signature=2 other=[]
TOP base=242 head=243 byte-identical=239

Negative control, one token changed in an in-memory copy of the head module (seeds.length === 0 to seeds.length !== 0, one occurrence):

MUTATED packages/metadata-protocol/src/protocol-publish-batch.ts "seeds.length === 0" -> "seeds.length !== 0"
applySeedBodies: base tokens 400, head tokens 400, differ 1, text-equal(ws-collapsed, comments incl.) false
collectBatchPendingDeclarations: base tokens 258, head tokens 258, differ 0, text-equal(ws-collapsed, comments incl.) true
TOTAL tokens=658 differ=1 members=2
  • Class side. 300 members at base and at head, in the same order. The 298 that did not move are byte-identical to base, leading comments included. Each of the 2 stubs sits at its base position and keeps its docblock and signature byte-identical (everything before the body's opening brace).
  • Top level of protocol.ts. 239 of the 242 base statements are byte-identical, leading comments included. The other three: the ./runtime-authoring-gate.js import losing CLOSURE_CONTEXT_KEY_BY_TYPE, and memberOption and declaresClientRefusal gaining export. Head adds one statement, the new module's import.
  • Size. +240 / −148 over 2 files, 388 changed lines (the order's estimate was 350–420). protocol-publish-batch.ts has 224 lines.

Readers of the moved lines

The seven test files the cut 13 report named were re-grepped by path for both member names. None reads protocol.ts by path (no readFileSync, no protocol.ts path literal); none names collectBatchPendingDeclarations.

File Hits Verdict
metadata-protocol protocol.batch-verb-driver-text.test.ts 49 (comment); 315, 631, 661, 704 call protocol.applySeedBodies(...) Behaviour through the class method, which the stub keeps. Passes at both ends.
objectql build-probes.test.ts 200, vi.spyOn(protocol, 'applySeedBodies') The spy replaces the class method, which is what callers reach. Passes at head.
objectql protocol-publish-package-drafts.test.ts 418–512 spies; 518–571 direct calls with the real SeedLoaderService Spies as above. The real-loader smoke test runs the moved body end to end, including the getMetaItem adapter read. Passes at head.
objectql publish-package-drafts-response-conformance.test.ts 283 (comment) No edit owed. Passes at head.
runtime packages-publish-drafts-response-conformance.test.ts 215 (comment) No edit owed. Passes at head.
runtime packages-seed-apply-disclosure.test.ts 8 (comment) No edit owed. Passes at head.
spec api/protocol.test.ts 1291 (comment) No edit owed (the comment names the producer, not its file).
  • docs/qa/platform-checklist/areas/platform-core.json:2880 anchors packages/metadata-protocol/src/protocol.ts#applySeedBodies+. The method keeps its name in protocol.ts; check-platform-checklist --changed-from origin/main judges it (see Verification).
  • Every other test or script that reads protocol.ts by path and names a symbol the bodies use (protocol.hydrate-overlay-canonical-type, protocol.read-verb-canonical-fold, protocol.lookup-artifact-item-call-sites, protocol.register-item-call-sites, protocol.object-registry-write-through-spelling, protocol.view-expansion-reconcile-call-sites) ran in the package suite and passes at both ends; check-durability-degradation-log-level ran as a hand gate (below).
  • Bracketed-refusal pin (PRODUCERS): the moved bodies construct no error (0 new Error(, 0 new TypeError(; applySeedBodies throws the seedRequestValidationError(...) it calls, which is built in protocol.ts). No row is owed, as the order said.
  • ADR anchors and decision letters. check-adr-anchors exits 0 at both ends: 86 anchored files, 58,982 citations, files 8,267 → 8,268, and 1,844 → 1,843 decision-letter citations. The one that leaves the anchored file is the [ADR-0131 D6, §12] comment in applySeedBodies' body (protocol.ts ADR-0131 D6 114 → 113). The protocol.ts anchor does not cover ADR-0131, and no entry paragraph describes the seed apply, so the new module gets no entry (disclosure only, as cuts 8, 12 and 13).
  • Durability checker: check-durability-degradation-log-level exits 0 at both ends with identical output, and its --list is identical once [@:][0-9]+ is stripped (159 lines).
  • Engine-double contract (check-engine-double-contract): exits 0 at both ends. The updateData/deleteData seams keep their entries at new line numbers (14085 → 14086, 14110 → 14111). productionFiles is 3,710 at base and 3,712 at head: +1 is the new file, +1 is the gitignored packages/spec/src/migrations/registry.ts, which the spec build writes in the head tree and the clean base tree had not built.
  • Identical output at both ends: check-adr-symbol-anchors, check-system-context-census, check-tenant-audit-census, check-query-options-erasure-ratchet, check-filter-alias-parity. check-nul-bytes differs only in the scanned file count, +1. The base-end readings were taken in a clean detached worktree at 0479cd6a0a.
  • Governed surface. check-governed-merges --branch claude/project-thread-ybjm3h: 0 of 2 paths hit the register, NOT governed; 388 changed lines, under the 3,000-line human-merge bar.

Verification

  • tsc --noEmit in packages/metadata-protocol exits 0 before and after the merges. pnpm exec eslint --no-inline-config packages/metadata-protocol (the whole touched package; it has no lint script, these are the root lint script's flags) exits 0, no output.
  • packages/metadata-protocol suite, base and head, compared per test name and status (vitest --maxWorkers=2, JSON reporter, each run under the shared verify lock, VERDICT command-exit 0 both legs), on the pre-merge tree (the merges changed no file of the package):
    • Base 0479cd6a0a: 21,553 tests (21,534 passed, 0 failed, 19 skipped), 268 files.
    • Head c2c08b1ae: 21,553 tests (21,534 passed, 0 failed, 19 skipped), 268 files.
    • None lost, none added, none changed status. The base leg is the package src/ swapped tree-only from 0479cd6a0a (the new module removed from the tree) and restored from HEAD under a trap.
  • Cross-package readers, at head under the verify lock: objectql build-probes, protocol-publish-package-drafts, publish-package-drafts-response-conformance: 3 files, 43/43 passed, exit 0. runtime packages-publish-drafts-response-conformance, packages-seed-apply-disclosure: 2 files, 16/16 passed, exit 0.
  • check-platform-checklist --changed-from origin/main (merged tree): OK, 1 anchored file in the change set, 29 anchors judged, 0 lost. That includes platform-core.json's protocol.ts#applySeedBodies+.
  • Derived gates. dispatch-gates --commands --repo objectstack-ai/objectstack derives 53 gates on 2 changed paths from the merged tree 721669cb1, with no stale-tree warning; all 53 exit 0 after the full pnpm build (119/119). dispatch-gates --ran: 53 derived, 53 run, 0 NOT-MEASURED, 0 UNRUN, exit 0. (Cut 13 derived 59: .github/workflows/ci.yml changed on main between the two cuts.)

Acceptance notes

  • A docblock that now reads narrower than the code. clientFacingFailureText's docblock (which ships in the .d.ts) says "declaresClientRefusal stays private on purpose: nothing outside this file needs the raw predicate". This cut exports it from protocol.ts because the moved catch reads it. Its purpose still holds at the package boundary: index.ts does not re-export it and the export table is unchanged. The sentence was left as it is, because editing it would change the .d.ts this series holds byte-identical. Rewording it to "outside this package" can ride a later cut that already moves .d.ts text, or a docs-only PR.
  • The proposal said applySeedBodies reads engine only. The AST walk with casts stripped found the (this as any).getMetaItem read too; the cut 15 screen below uses the same walk.

Carry-forward

Unchanged from cut 13: public members whose return type is inferred, the bodies kept by a reader (probeRecord, getMetaItemCached, findReferencesToMeta, packagelessAddress, refuseUnmintableMetaType, metaTypeNamespaceExists, viewContainerNameCollisionRefusal with overlaidShippedContainerViewNames, reportPageRequiresAbsentAtLoad, the 7 applyRegistryWriteThrough callers, readCodeLayerForCarryForward), and the private-static readers (refusePackagedBaseRemoval, packagedBaseRefusal, runPublishSideEffects, promoteDraftForPublish, publishPackageDrafts, discardPackageDrafts) stay in protocol.ts. Added this cut: warnClosureReadUnavailable, which writes an instance field.

🤖 Generated with Claude Code

https://claude.ai/code/session_016SP1spj613tyGCgxoD1An6

…its pending-declaration collection out of protocol.ts (#25467 cut 14)

Move-only. The bodies of applySeedBodies and collectBatchPendingDeclarations
move into the new protocol-publish-batch.ts (host shape B: a live engine
getter, protocol-bound closures for getMetaItem, getOverlayRepo and
warnClosureReadUnavailable). applySeedBodies reads getMetaItem through an any
cast inside the loader's metadata adapter, so the host carries it.
warnClosureReadUnavailable stays in the class: it writes closureReadWarned.
Both members keep their name, docblock and signature in the class as stubs
that delegate. declaresClientRefusal and memberOption gain export in place;
CLOSURE_CONTEXT_KEY_BY_TYPE leaves protocol.ts' import with its last reader.
Nothing re-exports the new module, so the published surface does not move.

Claude-Session: https://claude.ai/code/session_016SP1spj613tyGCgxoD1An6
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/metadata-protocol, touching 17 documentable anchor(s).

6 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/concepts/metadata-lifecycle.mdx (via getMetaItem (symbol, a field of interface PublishBatchHost), getOverlayRepo (symbol, a field of interface PublishBatchHost), getMetaItem (literal, a string literal in applySeedBodies; a string literal in getMetaItem), getOverlayRepo (literal, a string literal in collectBatchPendingDeclarations; a string literal in getOverlayRepo))
  • content/docs/data-modeling/drivers.mdx (via getMetaItem (symbol, a field of interface PublishBatchHost), getMetaItem (literal, a string literal in applySeedBodies; a string literal in getMetaItem))
  • content/docs/deployment/environment-variables.mdx (via getMetaItem (symbol, a field of interface PublishBatchHost), getMetaItem (literal, a string literal in applySeedBodies; a string literal in getMetaItem))
  • content/docs/kernel/contracts/metadata-service.mdx (via getPublished (sdk, the bare tail of client method meta.getPublished, bound to GET /api/v1/meta/:type/:name/published; the bare tail of client method meta.getPublished, bound to GET /meta/:type/:name/published))
  • content/docs/kernel/services-checklist.mdx (via getMetaItem (symbol, a field of interface PublishBatchHost), getMetaItem (literal, a string literal in applySeedBodies; a string literal in getMetaItem))
  • content/docs/protocol/objectql/state-machine.mdx (via /object/:name/state/:field (route, bridged from symbol getMetaItem — its route source's handler names it))

⛔ 3 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17/17-0.mdx (via getLegalNextStates (sdk, the bare tail of client method meta.getLegalNextStates, bound to GET /api/v1/meta/object/:name/state/:field; the bare tail of client method meta.getLegalNextStates, bound to GET /meta/object/:name/state/:field), meta.getLegalNextStates (sdk, the route ledger binds it to GET /api/v1/meta/object/:name/state/:field, selected by route anchor /object/:name/state/:field; the route ledger binds it to GET /meta/object/:name/state/:field, selected by route anchor /object/:name/state/:field))
  • content/docs/releases/v17/17-2.mdx (via getLegalNextStates (sdk, the bare tail of client method meta.getLegalNextStates, bound to GET /api/v1/meta/object/:name/state/:field; the bare tail of client method meta.getLegalNextStates, bound to GET /meta/object/:name/state/:field), meta.getLegalNextStates (sdk, the route ledger binds it to GET /api/v1/meta/object/:name/state/:field, selected by route anchor /object/:name/state/:field; the route ledger binds it to GET /meta/object/:name/state/:field, selected by route anchor /object/:name/state/:field))
  • content/docs/releases/v17/17-3.mdx (via /:type/:name/published (route, bridged from symbol getMetaItem — its route source's handler names it))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 11 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 56a234653bc43aee4df6ad9c80e78bea1957138b → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 1551ea67ea6f787fc008b19f7e74678ce385fa0a — the merge of head 721669cb133b7eb23ff2157f01ab68dad58244cf into base 56a234653bc43aee4df6ad9c80e78bea1957138b, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 1551ea67ea6f787fc008b19f7e74678ce385fa0a && git checkout 1551ea67ea6f787fc008b19f7e74678ce385fa0a
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 56a234653bc43aee4df6ad9c80e78bea1957138b 721669cb133b7eb23ff2157f01ab68dad58244cf && git checkout -B drift-repro 56a234653bc43aee4df6ad9c80e78bea1957138b && git merge --no-ff 721669cb133b7eb23ff2157f01ab68dad58244cf

node scripts/docs-audit/affected-docs.mjs --json 56a234653bc43aee4df6ad9c80e78bea1957138b

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 56a234653bc43aee4df6ad9c80e78bea1957138b → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · domain:server seat 1 · os-project-manager · session_01R1q2xdtYgU6FKrZ7LgjPCC · 2026-10-26T09:10Z

Head 721669cb13, report 6412300878 (on #25467), claim 6410996628. No contract review owed: no packages/spec/** diff and no narrowing.

item reading
Shape draft, base main, Part of #25467 (cut 14), skip-changeset (no published behaviour moves).
Files 2 files, +240/−148: applySeedBodies and collectBatchPendingDeclarations move into protocol-publish-batch.ts; the host carries a getMetaItem closure for the one (this as any) read the screens miss. Not governed.
Fix a pure move: AST compare 658 tokens, 0 differ (negative control 1); .d.ts sha identical at both ends (positive control moves it); export tables equal (78 names); no private-static read, no new Error(, no PRODUCERS row.
Evidence package suite 21,553 tests at both ends, 0 lost, 0 changed; 53/53 derived gates; the seven by-path test files re-grepped.
Carried the clientFacingFailureText docblock wording, left to keep the .d.ts byte-identical. Cut 15 (artifact-lookup family) follows from the same thread once this lands.
Landing 「按照 pm-dispatch 规则复核过的都授权」 (Leehom, 2026-10-25T11:35Z): ready + auto-merge through the relay once every check on this head is green or an expected skip.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 26, 2026 09:12
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 26, 2026 09:12
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · CI note · domain:server seat 1 · os-project-manager · session_01R1q2xdtYgU6FKrZ7LgjPCC · 2026-10-26T09:31Z

Head 721669cb13, run 40297233938 attempt 1: Test Core (7/8) failed on one test, packages/plugins/plugin-security/src/delegated-organization-membership.test.ts:648 (group parity: read, insert and export), with Error: Test timed out in 5000ms. Every other test in the shard passed (plugin-security 4698 passed, 1 failed).

Not this PR's: the diff moves the publish-batch family out of packages/metadata-protocol/src/protocol.ts into protocol-publish-batch.ts and touches nothing plugin-security reads for that case. The failure is a wall-clock timeout on a shard that ran long (objectql alone took 1048s on it), not an assertion. No fix to port.

Re-ran the failed jobs once through the relay (fleet-write run 40300082635, now attempt 2). Auto-merge stays armed. If the same test times out again on attempt 2, it gets a card of its own.


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 40307144724 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

分类:failure —— 按下面的日志分诊。

失败的 job(日志抽取,best effort):

  • Test Core (7/8) — 失败步骤: Run this shard's tests

    @objectstack/plugin-security:test:  FAIL  src/objects/shipped-sets-identity-default-row-scope.test.ts > the shipped sets serve the identity objects as before — 'isolated posture, platform baseline c…'
      ↳ 失败原因: @objectstack/plugin-security:test: Error: Test timed out in 5000ms.
    

↳ 失败原因 是判读的关键:超时(Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言(AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️ 断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError。 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 19 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · queue note · domain:server seat 1 · os-project-manager · session_01R1q2xdtYgU6FKrZ7LgjPCC · 2026-10-26T10:49Z

Queue build 40307144724 failed on plugin-security/src/objects/shipped-sets-identity-default-row-scope.test.ts (timeout). That build carried PR #26026's queue commit 6201f86bf3 beneath this PR, and the same test failed in #26026's own queue build; #26026 is pulled from the queue for root-cause. This PR is move-only in packages/metadata-protocol and touches nothing that test runs. Re-armed through the relay.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants