Skip to content

refactor(metadata-protocol): move the artifact registry's lookups out of protocol.ts into protocol-artifact-lookup.ts (#25467 cut 15) - #26104

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 15 of the protocol.ts split (#24586 ruling A, recorded in 6270097840). Cuts 1–14 landed as #25494, #25518, #25538, #25589, #25601, #25623, #25642, #25719, #25731, #25740, #25797, #25939, #25972 and #26014. The claim is 6414543503; 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 ObjectStackProtocolImplementation members 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.ts loses 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-ybjm3h is the harness-pinned branch for this thread, restarted from main 3117289002 after cut 14 merged. It names no issue, so the claim comment is the identity record. It holds the commit dd85dd1b4 (the move and the pin edit together: the move alone turns the pin red, as the control below shows) and two merges of main: be7897799 (59e0b7e0f) and aa2f8e31f (9840a879d). Neither touches packages/metadata-protocol/: the package's src/ tree is af425909 at 3117289002 and at both merged main heads.

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 edits protocol.meta-types-degenerate-derivation.test.ts, which does not overlap this cut. No open PR edits protocol.ts, the new module or the #22024 pin.

What moves

Member Base line Visibility Body lines AST tokens (params + return + body)
lookupArtifactItem :16493 private 24 162
artifactLockLayerAt :16533 private 3 43
shippedArtifactsOf :16558 private 14 184
shippingPackagesOf :16594 private 39 406

80 body lines (the proposal said about 80).

The shape (#23050 shape B, as cuts 1–14 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 15].
  • Typing. ArtifactLookupHost 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, parentheses and ! stripped; bare this checked too):
    • lookupArtifactItem and shippingPackagesOf read engine only, through a live getter.
    • shippedArtifactsOf reads lookupArtifactItem and shippingPackagesOf, and artifactLockLayerAt reads shippedArtifactsOf; each goes as a closure.
    • So the family reaches its own members through the class methods, not by calling the module's functions directly. A spy or double set on the instance (for example on lookupArtifactItem) is still the one shippedArtifactsOf reaches, as before the move.
  • No private-static read. The AST walk found no access on the ObjectStackProtocolImplementation identifier and none on this.constructor, in any of the four bodies. 0 new Error( (and no other error constructor), so no PRODUCERS row.
  • Every other caller still calls the class method: the 18 lookupArtifactItem( sites in protocol.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 for artifactLockLayerAt, and readFlattenedMetaItems and the others for shippingPackagesOf.

Module-level declarations, by reader

  • None gains export, none moves. The bodies read PLURAL_TO_SINGULAR (from @objectstack/spec/shared), resolveArtifactLockLayer and the type ItemAddress (from ./item-lock.js), which the new module imports from where protocol.ts imports them, and ShippingPackages, protocol.ts's own exported type, imported as a type. The module's only import from ./protocol.js is a type import, so it adds no runtime cycle.
  • Imports in protocol.ts. resolveArtifactLockLayer loses its last reader and leaves the ./item-lock.js import (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.ts records a disposition for every lookupArtifactItem( in the source. The move takes three of them into the new module (the function the class member delegates to, and shippedArtifactsOf's two calls) and adds one in protocol.ts (the stub's host closure, lookupArtifactItem: (...a) => this.lookupArtifactItem(...a)). The edit:

  • The pin reads protocol-artifact-lookup.ts beside protocol.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.
  • Two new rows. The seam function's own definition. And the stub's closure (...a): disposition no, recorded as passing no package because it forwards the package-less call too, and its disposition is the two forwarded calls'.
  • The non-vacuity case now expects one definition in each file. One new case asserts that the seam file's found sites are exactly its recorded rows, three of them, so a scan that read the wrong file or none cannot pass by accident.
  • The describe and existing test titles are unchanged, so the suite comparison below keys on the same names. The header prose says the population is two files now.

Controls (each on disk, restored from HEAD under a trap, hash equal to HEAD's after):

  • The widened pin catches a re-scoped call in the seam. this.lookupArtifactItem(type, name, packageId) was changed to this.lookupArtifactItem(type, name) in the module. On-disk counts read first: 0 scoped calls, 2 package-less. Result: 2 failed, 3 passed:
    • "the sites in the source are exactly the recorded ones" fails: a recorded site that is no longer in the source.
    • The new seam-scan case fails.
  • The pin had to be widened. The pin file at base, run against the cut: 2 failed, 2 passed:
    • "a site with no recorded disposition", which is the new closure site.
    • expected 18 to be 19.

With the widened pin, 5/5 pass.

The published .d.ts

The series bar is ruling 6348891160, sorted-union identity, measured first. Base 3117289002 and head dd85dd1b4 were built with pnpm --filter @objectstack/metadata-protocol build against one dependency tree. The closure was built at head by a full pnpm build (119/119). For the base build, the package's src/ was swapped tree-only to base and then restored from HEAD under a trap; git diff HEAD was empty, git status clean, and all three touched blobs equal to HEAD's after. Measured twice: on the pre-merge tree (base 3117289002, head dd85dd1b4) and after merging main (head 59e0b7e0f, closure rebuilt by a full pnpm build, 119/119; the later merge 9840a879d changed only CI workflow files and scripts under ui/ and scripts/, no package source). 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. All four 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 13ff6c73088affd6… at base and 5671669a82bc3837… at head; each of the four functions' .call(host sites occurs 0 times in the base bundles and once in the head bundles (ESM and CJS alike).
  • Positive control on the head tree: private was dropped from lookupArtifactItem (on disk, restored from HEAD under a trap) and the package rebuilt. Both .d.ts files move to 4290e8428fa3c8f5… and print lookupArtifactItem(type: string, name: string, currentPackageId?: string, member?: string): unknown;, 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. No moved function, the host type or resolveArtifactLockLayer 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 3117289002 against the head module:

lookupArtifactItem: base tokens 162, head tokens 162, differ 0, text-equal(ws-collapsed, comments incl.) true
artifactLockLayerAt: base tokens 43, head tokens 43, differ 0, text-equal(ws-collapsed, comments incl.) true
shippedArtifactsOf: base tokens 184, head tokens 184, differ 0, text-equal(ws-collapsed, comments incl.) true
shippingPackagesOf: base tokens 406, head tokens 406, differ 0, text-equal(ws-collapsed, comments incl.) true
TOTAL tokens=795 differ=0 members=4
CLASS members base=300 head=300 order-same=true byte-identical=296 stubs-with-identical-docblock+signature=4 other=[]
TOP base=243 head=244 byte-identical=242

Negative control, one token changed in an in-memory copy of the head module (id !== 'sys_metadata' to id === 'sys_metadata', one occurrence):

MUTATED packages/metadata-protocol/src/protocol-artifact-lookup.ts "id !== 'sys_metadata'" -> "id === 'sys_metadata'"
lookupArtifactItem: base tokens 162, head tokens 162, differ 0, text-equal(ws-collapsed, comments incl.) true
artifactLockLayerAt: base tokens 43, head tokens 43, differ 0, text-equal(ws-collapsed, comments incl.) true
shippedArtifactsOf: base tokens 184, head tokens 184, differ 0, text-equal(ws-collapsed, comments incl.) true
shippingPackagesOf: base tokens 406, head tokens 406, differ 1, text-equal(ws-collapsed, comments incl.) false
TOTAL tokens=795 differ=1 members=4
  • Class side. 300 members at base and at head, in the same order. The 296 that did not move are byte-identical to base, leading comments included. Each of the 4 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. 242 of the 243 base statements are byte-identical, leading comments included. The other one is the ./item-lock.js import, which loses resolveArtifactLockLayer. Head adds one statement, the new module's import.
  • Size. +231 / −87 over 3 files, 318 changed lines (the proposal's estimate was 330–400). protocol-artifact-lookup.ts has 158 lines; the pin edit is +50/−16.

Readers of the moved lines

Every test and script that reads protocol.ts by path, every test file in packages/ and ui/, and docs/, scripts/ and content/ were grepped for the four member names.

  • By path: only 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 pin reads a moved body's text (above). protocol.register-item-call-sites.test.ts names shippedArtifactsOf only inside a docblock; its by-path regex is on anotherPackageShips, which stays. The other by-path readers of protocol.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.
  • In prose or behaviour, no edit owed.
    • metadata-protocol protocol.lock-gate-canonical-type.test.ts drives the lookup through the gate (behaviour). protocol.declines-stored-row-published and protocol.i18n-bundle-list-merge name it in comments. All pass at both ends.
    • Run at head under the verify lock:
      • objectql protocol-registry-shadow, protocol-boot-hydration-scoped and environment-overlay-email-locale-restart (comments; behaviour through the class): 3 files, 61/61.
      • rest meta-published-overlay: 16/16.
      • runtime meta-published-runtime-publish: 12/12.
    • ui PermissionMatrixEditor.artifactTier.test.tsx and ResourceEditPage.resetVerdict.test.tsx name it in comments only.
  • No hit in docs/, scripts/ or content/; docs/qa/ cites none of the four.
  • ADR anchors. check-adr-anchors exits 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 cite ADR-0010 §3.3, ADR-0048 and ADR-0029 D3. The D3 one sits in shippedArtifactsOf's docblock, which stays in protocol.ts, so no decision-letter citation leaves the anchored file.
  • 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). shippingPackagesOf's try { … } catch { return []; } around a registry listing is not on the list (it reads no store).
  • Engine-double contract (check-engine-double-contract): exits 0 at both ends. The updateData/deleteData seams keep their entries at new line numbers (14086 → 14087, 14111 → 14112). productionFiles is 3,718 at base and 3,720 at head: +1 is the new file, +1 is the gitignored packages/spec/src/migrations/registry.ts the spec build writes in the head tree.
  • 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 3117289002.
  • Governed surface. 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 --noEmit in packages/metadata-protocol exits 0 before and after the merge (after the full build; the first run, on a stale dependency tree, failed only in protocol-package-install*.ts on @objectstack/metadata-core exports that the build then supplied). 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 merge changed no file of the package):
    • Base 3117289002: 21,557 tests (21,538 passed, 0 failed, 19 skipped), 269 files.
    • Head dd85dd1b4: 21,558 tests (21,539 passed, 0 failed, 19 skipped), 269 files.
    • None lost, none changed status, one added: the pin's new seam-scan case. The base leg is the package src/ swapped tree-only from 3117289002 (the new module removed, the pin at its base blob) and restored from HEAD under a trap.
  • check-platform-checklist --changed-from origin/main (merged tree): OK, 1 anchored file in the change set, 29 anchors judged, 0 lost.
  • Derived gates. dispatch-gates --commands --repo objectstack-ai/objectstack derives 59 gates on 3 changed paths from the merged tree 9840a879d, with no stale-tree warning (the first derivation, at 59e0b7e0f, flagged ci.yml and check-closing-keyword-parity.mjs as changed on main, so main was merged once more before running). All 59 exit 0 after the full pnpm build. dispatch-gates --ran: 59 derived, 59 run, 0 NOT-MEASURED, 0 UNRUN, exit 0.

Acceptance notes

  • Per-call cost. lookupArtifactItem is 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.
  • The family reaches itself through the class. shippedArtifactsOf and artifactLockLayerAt call 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:

  • 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, and readCodeLayerForCarryForward.
  • The private-static readers: refusePackagedBaseRemoval, packagedBaseRefusal, runPublishSideEffects, promoteDraftForPublish, publishPackageDrafts and discardPackageDrafts.
  • The field writers: warnClosureReadUnavailable.

🤖 Generated with Claude Code

https://claude.ai/code/session_016SP1spj613tyGCgxoD1An6

… 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>
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

17 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json 0a6948210cc96a95cc568b30193895e157968c01.

⛔ 9 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

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 0a6948210cc96a95cc568b30193895e157968c01 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 97c4e61fd69154cf52f4774bab4aac89304ca25c — the merge of head 9840a879dab62b2e1edc5d4070e199a79c4e7b22 into base 0a6948210cc96a95cc568b30193895e157968c01, 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 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

⚠️ 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 0a6948210cc96a95cc568b30193895e157968c01 → 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-26T12:53Z

Head 9840a879da, report 6415780867 (on #25467), claim 6414543503. No contract review owed: no packages/spec/** diff and no narrowing.

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

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 26, 2026 12:54
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 26, 2026 12:54
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 26, 2026
Merged via the queue into main with commit d23f19e Oct 26, 2026
53 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-ybjm3h branch October 26, 2026 13:28
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 tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants