Skip to content

refactor(metadata-protocol): move the lock-state group out of protocol.ts into protocol-lock-state.ts (#25467 cut 10) - #25740

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-5tpuo2
Oct 25, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-5tpuo2

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 10 of the protocol.ts split (#24586 ruling A, recorded in 6270097840). Cuts 1–9 landed as #25494, #25518, #25538, #25589, #25601, #25623, #25642, #25719 and #25731. The claim is 6392807595; the scope is the lock-state group the cut 9 report (6392271625) proposed from Release 6385903828, as the server seat approved it.

Six ObjectStackProtocolImplementation members move unedited into a new module, packages/metadata-protocol/src/protocol-lock-state.ts (#23050 shape B; no getter). protocol.ts loses 4 net lines (29,795 to 29,791). The cut is move-only, and the card stays open for later cuts.

Branch claude/project-thread-5tpuo2 is the harness-pinned branch for this thread. It names no issue, so the claim comment is the identity record. It holds one commit, 3ebaea97, on main ec4ef94ead.

Why this group alone. The order allowed adding registry materialize/strip if this group was too small to stand alone. It is the size of cut 9 (451 body tokens against cut 9's 496; 242 changed lines against 299), it is one closed group (the six call only one another and four class members that stay), and the claim's file surface names one module for it. So this cut carries the group alone, and registry materialize/strip is proposed as cut 11 in the report.

Serial with the file's in-flight PRs. Measured just before this PR was opened: of the 17 open PRs, only #25737 touches packages/metadata-protocol/src/, in protocol.runtime-authoring-gate.test.ts, a test that reads no source by path and names no moved member, outside this cut's file surface. The release PR #25640 touches the package's CHANGELOG.md and package.json only. #25718's 9,211-file list was read by git diff --name-only against its merge base: 0 paths under packages/metadata-protocol/src/. origin/main moved to e2b40a264b after the base; git log ec4ef94ead..origin/main -- packages/metadata-protocol is empty, and git merge-tree of this head with origin/main is clean.

What moves

Member Base line Visibility Tokens (params + return + body)
servedLockState :15893 private 214
isShippedFlowName :15972 public 58
isStoredFlowEntryOfShippedName :15987 private 43
declinesStoredRow :16048 public 59
isUnservedSealedOverlay :16070 private 34
isStoredEntryOfDeclinedName :16084 private 43

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

  • The class keeps every member with its name, docblock, signature and position. Each body is now one host literal plus fn.call(host, …), marked [#25467 cut 10]. servedLockState keeps its storedRowServed = false default in the class signature and in the module function's parameter list, and the stub passes the parameter through.
  • Typing. LockStateHost types every member as the class member itself (ObjectStackProtocolImplementation['m']). 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 cast, and no member changes visibility.
  • Members read, all as late-bound closures: packagedBaseRefusal, packagedArtifactOwner, isDeclaredCodeDatasource and isSealedManagedItem (which stay in the class), and isShippedFlowName, isUnservedSealedOverlay and declinesStoredRow (moved bodies calling one another through the protocol). No field is read, so the host has no getter.
  • Every other caller still calls the class method: the reads in protocol.ts (getMetaItem, readFlattenedMetaItems, getMetaItemLayered, restoreArtifactRegistryView, loadMetaFromDb and others), cut 2's MetaDiagnosticsHost (its servedLockState closure), and the published doors in packages/rest/src/meta-item-history-routes.ts and packages/runtime/src/domains/meta.ts, which ask declinesStoredRow on the protocol instance.

Module-level declarations, by reader

  • None moves, and none gains export. The one module-level declaration of protocol.ts the bodies read is canonicalMetaType (in isUnservedSealedOverlay), already exported; the new module imports it from protocol.js as cut 5's protocol-lock-version-gates.ts does. The cycle is evaluation-safe: it is read only inside a function body.
  • Everything else the bodies read is imported from the module protocol.ts imports it from: @objectstack/spec/kernel (extractProtection, evaluateLockForWrite, evaluateLockForDelete, MetadataLockSchema, and resolveLockState as a type), ./packaged-base-regime.js (isOriginGatedType), @objectstack/metadata-core (isCodeArtifactBody), @objectstack/spec/shared (PLURAL_TO_SINGULAR), ./item-lock.js (ItemLock, type).
  • Imports in protocol.ts. extractProtection, evaluateLockForWrite, evaluateLockForDelete and MetadataLockSchema lose their last reader and leave the @objectstack/spec/kernel import list; resolveLockState keeps a reader (the class signature of servedLockState) and stays. isOriginGatedType, isCodeArtifactBody, PLURAL_TO_SINGULAR and canonicalMetaType keep other readers. The one new statement is the import of the new module.

The published .d.ts

The series bar is ruling 6348891160, sorted-union identity, measured first. Base ec4ef94ead and head 3ebaea97 were built with pnpm --filter @objectstack/metadata-protocol build against one dependency tree (the closure built once at head, then the package's src/ swapped tree-only to base for the base build and restored from HEAD under a trap; git diff HEAD empty and git status clean after, protocol.ts blob equal to HEAD's).

  • Raw sha256: 325970c1661efaa6a3762ce7de9e6bb205ea16099784f2b6c5950d62d175b331 at base and at head, on both dist/index.d.ts and dist/index.d.cts, 772,838 bytes each (the hash cuts 8 and 9 reported). cmp exits 0 on both pairs. Raw identity holds, so sorted-union identity holds too. The two public members keep their declared boolean return type, so their declarations do not move (declinesStoredRow(type: string, name: unknown): boolean;), and the four private ones stay private NAME; lines.
  • Normalised sha256: 9562749edf7b8bba67ed2f1779799a68b3d6934a8605074934572d9507c55ba6 at both ends, both files (109 string-literal union runs sorted). This run's normaliser is its own script; its figure happens to equal cut 9's.
  • The two builds differ where they should: index.js hashes a41e81cf2349514e… at base (cut 9's head hash) and b99c5148b08d3fc5… at head; servedLockState.call and declinesStoredRow.call each occur 0 times in the base bundle and once in the head bundle (ESM and CJS alike).
  • Positive control on a copy of the head index.d.ts, declinesStoredRow(type: string, name: unknown) changed to name: string: raw c74ae751a3cb7a09…, normalised fd072bd9d1ab499a…. Both move.
  • 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. None of the moved functions or the host type appears.

No changeset: skip-changeset, per ruling 6348891160.

Proof of move-only

  • Bodies, by AST token (the TypeScript compiler's AST leaf tokens over parameters less the added this, return type and body): 451 tokens compared, 0 differ. Leaf tokens rather than the raw scanner because the scanner, run without template rescanning, swallows the rest of servedLockState's body into one template-tail token after its error message's first ${…}, which would compare whitespace. The parameter, return type and body text, comments included, is also equal with whitespace collapsed, for all six. Negative control: one changed token in isUnservedSealedOverlay's body (=== 'permission' to !==, on a copy) makes the check report differ=1.
  • Class side. 299 members at base and at head, in the same order. The 293 that did not move are byte-identical to base, leading comments included. Each of the 6 stubs sits at its base position and keeps its docblock and signature byte-identical.
  • Top level of protocol.ts. 236 of the 237 base statements other than the class are byte-identical, leading comments included. The other one is the @objectstack/spec/kernel import that loses four names. Head adds one statement, the new module's import.
  • Size. +198 / −44 over 3 files, 242 changed lines. protocol-lock-state.ts has 155 lines.

Readers of the moved lines

Every test and script that reads protocol.ts by path was grepped for each moved member's name and for the calls their bodies make (packagedBaseRefusal, packagedArtifactOwner, isDeclaredCodeDatasource, isSealedManagedItem, isCodeArtifactBody, isOriginGatedType, evaluateLockFor*, extractProtection, MetadataLockSchema, the error message).

  • The six test files the order named call the method on a protocol instance or name it in prose; none reads its body in protocol.ts by path. The hypothesis holds, and every member moves:
    • protocol.code-defined-datasource-door.test.ts: declinesStoredRow named in a comment only.
    • protocol.declines-stored-row-published.test.ts: calls protocol.isShippedFlowName(…) and protocol.declinesStoredRow(…).
    • protocol.reads-environment-only.test.ts: calls protocol.declinesStoredRow(…).
    • packages/plugins/plugin-security/src/security-catalog-echo.test.ts: servedLockState named in a comment only. 19/19 pass at head.
    • packages/rest/src/meta-published-overlay.test.ts: calls protocol.isShippedFlowName(…) and hides declinesStoredRow behind a Proxy to pin that the door asks that one predicate. The stub's closures call through this, the method stays on the class, and 16/16 pass at head.
    • packages/runtime/src/domains/meta-published-runtime-publish.test.ts: the same two shapes as the REST test. 12/12 pass at head.
  • Other name hits, all prose or calls: protocol.flow-by-name-shipped-name.test.ts and protocol.flow-layered-shipped-name.test.ts (docblocks), packages/qa/dogfood/test/flow-shipped-name-published-door.dogfood.test.ts (a comment), protocol-meta-diagnostics.ts (the cut 2 host type and its call), item-lock.ts (a comment).
  • Bracketed-refusal pin (PRODUCERS): servedLockState's unreachable-state throw new Error( moves with its body, so the new module gets a row, 'protocol-lock-state.ts': 1, the module's own new Error( count, beside the rows cuts 1, 3, 5, 6 and 7 added. protocol.ts goes from 75 to 74 such sites, still above its floor of 5. Control: with the module's throw changed to new TypeError( (on disk, restored from HEAD under a trap, hash equal to HEAD's after), the new row fails with "protocol-lock-state.ts no longer constructs errors here … expected 0 to be greater than or equal to 1" (1 failed, 12 passed); restored, 13/13 pass.
  • protocol.read-verb-canonical-fold.test.ts lists the members whose body folds through canonicalizeMetaRequestType(request). None of the six does; the list is unchanged and green.
  • docs/qa/platform-checklist/areas/studio-authoring.json cites "protocol.ts servedLockState". The method keeps its name in protocol.ts; check-platform-checklist --changed-from origin/main: OK, 29 anchors judged, 0 lost.
  • ADR anchors and decision letters. check:adr-anchors exits 0 at both ends: 85 anchored files, 52,570 citations, 1,846 → 1,846 decision-letter citations; the only difference in its output is the file count, 6,623 → 6,624. The protocol.ts anchor entry's ADR-0126 §2 / §3 paragraph names isShippedFlowName / isStoredFlowEntryOfShippedName, but the mechanism it describes, hydrateOverlayIntoRegistry registering a stored flow row without the envelope and readFlattenedMetaItems serving the loader's entries alone, stays in protocol.ts, and both names stay there as class members. So the new module gets no entry (see Acceptance notes).
  • Durability checker (check-durability-degradation-log-level --list): identical at both ends once [@:][0-9]+ is stripped (159 lines). The gate exits 0 at both ends with identical output.
  • Engine-double contract (check-engine-double-contract): exits 0 at both ends. The updateData/deleteData seams keep their entries at new line numbers (14039 → 14044, 14064 → 14069). productionFiles is 3,686 at base and 3,688 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 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 ec4ef94ead.
  • Governed surface. check-governed-merges --branch claude/project-thread-5tpuo2: 0 of 3 paths hit the register, NOT governed; 242 changed lines, under the 3,000-line human-merge bar.

Verification

All at head 3ebaea97.

  • pnpm --filter @objectstack/metadata-protocol typecheck exits 0. pnpm exec eslint --no-inline-config packages/metadata-protocol 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):
    • Base ec4ef94ead: 21,504 tests (21,485 passed, 0 failed, 19 skipped), 266 files.
    • Head 3ebaea97: 21,505 tests (21,486 passed, 0 failed, 19 skipped), 266 files.
    • None lost, none changed status, one added: the new PRODUCERS row's case. The base leg is the package src/ swapped tree-only from ec4ef94ead (the new module removed from the tree) and restored from HEAD under a trap; git diff HEAD empty and git status clean after.
  • Derived gates. dispatch-gates --commands --repo objectstack-ai/objectstack derives 59 gates on 3 changed paths; all 59 exit 0. check-plugin-teardown-shape --self-test, check:dual-build-cjs-loads and check:lean-entry-closure first answered PREREQUISITE NOT MET (exit 3: a pinned fixture commit outside the shallow clone; unbuilt dist/) and exit 0 once the commit was fetched and pnpm build ran. dispatch-gates --ran: 59 derived, 59 run, 0 NOT-MEASURED, 0 UNRUN, exit 0.

Acceptance notes

  • No new ADR anchor entry: the moved bodies are the predicates the ADR-0126 paragraph names, and the read and hydration paths that paragraph describes stay in protocol.ts. If the seat reads the predicates themselves as the mechanism, the fix is a packages__metadata-protocol__src__protocol-lock-state.ts.json entry citing ADR-0126; the letter count does not move either way.
  • origin/main moved to e2b40a264b after the base (8 commits, none in packages/metadata-protocol, merge-tree clean); not merged before this PR, so the suite and gates measured ec4ef94ead-based trees. The merge queue rebuilds on current main.

Carry-forward

Unchanged from cut 9: 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 stay in protocol.ts.


Generated by Claude Code

…l.ts into protocol-lock-state.ts (#25467 cut 10)

Move-only. The bodies of servedLockState, isShippedFlowName,
isStoredFlowEntryOfShippedName, declinesStoredRow, isUnservedSealedOverlay
and isStoredEntryOfDeclinedName move to the new protocol-lock-state.ts
(shape B: every method keeps its name, docblock and declared signature in
the class; each body is a LockStateHost literal plus a .call). The
bracketed-refusal pin follows the moved throw site with a PRODUCERS row.

Claude-Session: https://claude.ai/code/session_01RobcokwdH6frowqrsE1fRc
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 25, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

18 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

What this run could not see
  • 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 5cf1b9d2e880c061f7f4ddc6899b1db61d60a5b0 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 6debba99709999cf4df4a17a51385dbb6f5d52a1 — the merge of head 3ebaea9729a4fcc79b1a1eb7e40b782020aefc0d into base 5cf1b9d2e880c061f7f4ddc6899b1db61d60a5b0, 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 6debba99709999cf4df4a17a51385dbb6f5d52a1 && git checkout 6debba99709999cf4df4a17a51385dbb6f5d52a1
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 5cf1b9d2e880c061f7f4ddc6899b1db61d60a5b0 3ebaea9729a4fcc79b1a1eb7e40b782020aefc0d && git checkout -B drift-repro 5cf1b9d2e880c061f7f4ddc6899b1db61d60a5b0 && git merge --no-ff 3ebaea9729a4fcc79b1a1eb7e40b782020aefc0d

node scripts/docs-audit/affected-docs.mjs --json 5cf1b9d2e880c061f7f4ddc6899b1db61d60a5b0

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · domain:server seat 1 · os-project-manager · session_01R1q2xdtYgU6FKrZ7LgjPCC · 2026-10-25T04:15Z

Head 3ebaea9729, report 6393303455, claim 6392807595.

item reading
Shape draft, base main, Part of #25467 (no closing keyword), skip-changeset label, Clause-②: no and Metadata-protocol: none line-initial.
Files (3, +198/−44) protocol.ts, the new protocol-lock-state.ts, and the bracketed-refusal pin protocol.bracketed-refusal-opener-absence.test.ts (one PRODUCERS row for the moved throw, as cuts 6–7 did). Exactly the claim's surface; no packages/spec/**, no docs/adr/**. Not governed (check-governed-merges --pr 25740), 242 changed lines.
Move-only report's AST proof: 451 tokens, 0 differ; one-token negative control reads differ=1. Seat's read of the six stubs: every host literal carries only callbacks to class members (packagedBaseRefusal, packagedArtifactOwner, isShippedFlowName, isDeclaredCodeDatasource, isUnservedSealedOverlay, isSealedManagedItem, declinesStoredRow), no field by value, so nothing can go stale. Each method keeps its name and declared signature in the class.
Readers the six test files that name these members call or spy on them; none reads a body in protocol.ts by path (report, verdict per file).
ADR anchor no new entry: the ADR-0126 paragraph names the two flow predicates, but the paths it describes stay in protocol.ts. Accepted.
.d.ts raw identity at both ends (325970c1…), normalised equal; export tables 78/78. Ruling 6348891160 holds.
Gates dev 59 derived, 59/59 exit 0, --ran reconciled; package suite 21,504 → 21,505 (+1, the PRODUCERS case), 0 lost; letters 1,846 → 1,846; hand gates and durability --list identical. CI at this act: in progress. Lint & Repo Gates and Type Check owed success before ready.
Landing ready + auto-merge through the relay once every check on this head is green or an expected skip. Cut 11 (registry materialize/strip, per the report) is the next claim on this card after this merges.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 25, 2026 04:46
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 25, 2026 04:46
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 25, 2026
@github-actions

Copy link
Copy Markdown
Contributor

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

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

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

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

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

    @objectstack/plugin-pinyin-search:test:  FAIL  src/boot-reconcile.integration.test.ts > #25634 the boot reconcile reads what changed since its last completed walk > control: the explicit rebuild (forc
      ↳ 失败原因: @objectstack/plugin-pinyin-search: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 队列共有 1 个失败构建(不含本次)。

分诊清单:

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

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

@github-actions

Copy link
Copy Markdown
Contributor

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

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

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

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

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

    @objectstack/plugin-pinyin-search:test:  FAIL  src/boot-reconcile.integration.test.ts > #25634 the boot reconcile reads what changed since its last completed walk > a first boot walks every row; an un
      ↳ 失败原因: @objectstack/plugin-pinyin-search: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 已在队列失败 1 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 2 个失败构建(不含本次)。

分诊清单:

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

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

@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 25, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Queue ejection triage · domain:server seat 1 · session_01R1q2xdtYgU6FKrZ7LgjPCC · 2026-10-25T05:37Z

Removed from the merge queue at 2026-10-25T05:36:19Z after its second red queue build (40163192259, then 40164208512). Both reds are the same signature and neither is in this PR's files:

Disposition: the same signature has ejected this PR twice, so it is ⛔ not re-enqueued now. It waits on #25750. The seat re-arms auto-merge once #25750 has a fix on main or the signature stops ejecting other PRs. Nothing on this head changes.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Re-armed · domain:server seat 1 · session_01R1q2xdtYgU6FKrZ7LgjPCC · 2026-10-25T11:27Z

Auto-merge is enabled again on head 3ebaea9729 (unchanged since triage 6393991359). The fix for the signature that ejected this PR twice, #25775 (Fixes #25749, Fixes #25750), is in the merge queue ahead of it, so this PR's queue build stacks on a tree that already carries the fix. If #25775 leaves the queue without merging, this PR waits again rather than being re-armed.


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 tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants