Skip to content

feat(spec,runtime): a package export carries its namespace and dependencies, so a reinstalled .custom pair keeps both (#29617) - #29651

Merged
objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/project-thread-f8jiyr
Nov 3, 2026
Merged

objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/project-thread-f8jiyr

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K

Requested by Warren · project thread
Fixes #29617

Clause-②: yes (widening)
Metadata-protocol: added
Skills-docs: none — both keys are written by the export door from the registry row and read by the install door; no author writes them, and skills/objectstack-platform/SKILL.md already sends the reader to the pair's export, which now reinstalls whole

维护者速读

  • 改了什么:GET /packages/:id/export 的导出清单新增两个可选固定键 namespace 与 dependencies,从包的注册表行读取并写出;spec 的 PackageExportManifestSchema 同步声明这两个键(形状直接复用安装清单 ManifestSchema 的同名键)。
  • 为什么改:导出再经 install-local 重装时,.custom 配对包丢了命名空间(落到 custom)和对基础包的依赖;实测重装直接被 ADR-0146 D2 覆盖闸门拒绝 403 NOT_OVERRIDABLE。
  • 风险与回滚:只多写两个键,行里没有就不写;旧导出照旧可装。回滚即撤回本 PR。
  • 先看:packages/runtime/src/domains/packages.ts 中 assemblePackageManifest 末尾新增的两行;packages/spec/src/api/package-lifecycle.zod.ts 的两个新键;dogfood 新测试 package-export-reinstall-custom-pair.dogfood.test.ts。
  • 两分钟验证:pnpm --filter @objectstack/dogfood exec vitest run test/package-export-reinstall-custom-pair.dogfood.test.ts,预期 3 条全绿(源端导出带两键、目标端重装 200、重装后注册表行命名空间为 showcase_custom 且依赖指向 showcase)。
  • 需要你做的:无。两个键名已获批准(见下方 Name approval),triage 的契约复审 PASS 在案,席位落地。

What changes

  • Spec. PackageExportManifestSchema (packages/spec/src/api/package-lifecycle.zod.ts) declares two more optional fixed keys, namespace and dependencies. Each is the install manifest's own declaration, ManifestSchema.shape.namespace / ManifestSchema.shape.dependencies, re-described for the export, so an export refuses exactly what an install would. The schema stays open for the per-type collections.
  • Runtime. assemblePackageManifest (packages/runtime/src/domains/packages.ts) writes both from the package's registry row (registry.getPackage(id).manifest), as the row declares them, and writes neither when the row declares none. dependencies is a copy of the row's map.
  • Generated: packages/spec/authorable-surface/api.json (two rows) and content/docs/references/api/package-lifecycle.mdx.
  • One changeset, @objectstack/spec minor and @objectstack/runtime minor (the kind is added, on both packages the diff moves).

Why

Measured by the filer through the real door on 81cd9eb0, and again here by ablation on this branch: the export answered the pair's items with id, name, version, and none of the two keys its registry row holds. Reinstalled through POST /api/v1/marketplace/install-local, the pair had no namespace (the door derives custom from the id) and no edge to its base. The edge is what orders the pair after its base (ADR-0141 D5) and what admits its overrides declarations at the managed install (ADR-0146 D2). With the emission ablated, the round-trip pin's install answers:

403 NOT_OVERRIDABLE — 'dashboard/showcase_revenue_pulse' declares an override of 'com.example.showcase', which is outside the dependency closure of 'com.example.showcase.custom'. Declare a dependency on 'com.example.showcase' in the `dependencies` of 'com.example.showcase.custom''s manifest, or remove `overrides` (ADR-0146 D2). Nothing was installed or written.

This also unblocks the server slice of #28914 (the whole-ontology export), which composes this per-package export.

Out of scope

  • type. The export still does not write type. Adding it alone would not make the export a POST /packages body.
  • The flat-collection refusal of POST /api/v1/packages. That door still refuses an export (400: no type, flat collection keys). It is not a reinstall door; install-local is the door that reinstalls an export.

Name approval

The maintainer approved the two key names namespace and dependencies on PackageExportManifestSchema as named: Warren chose 「全部批准」 on the decision card 「六个命名/措辞全部按推荐批准吗?」 (item 6), project thread "Decide pending spec name approvals", 2026-11-03T00:08Z. Seat pointer on #29617: comment 6591852561.

Tests

  • Round trip, two real showcase boots (new packages/qa/dogfood/test/package-export-reinstall-custom-pair.dogfood.test.ts). Source boot: the ensure door creates com.example.showcase.custom, a dashboard override is saved into it and published, and the pair's export carries the namespace (showcase_custom) and dependencies (com.example.showcase) its registry row declares (the row is the control). Target boot, a fresh showcase with install-local mounted: the export installs 200, and the reinstalled pair's registry row declares the same namespace and the same edge to the showcase.
  • Door unit pin (new packages/runtime/src/domains/packages-export-registry-row-keys.test.ts): the real dispatcher over a registry double. The pair's export carries both keys (a copy of the row's map) and parses as PackageExportManifestSchema; a row declaring neither writes neither; type stays out.
  • Spec (packages/spec/src/api/package-lifecycle.test.ts, a new block at the end of the file): a pair's export parses and keeps both keys; both are optional; each refuses what an install would (1crm, Crm, a non-string range, an array); each describe names its source and the ADRs and no tracker id.
  • Ablation (fix committed first; scripts/ablation-replace.mjs, runtime rebuilt, ablation-dist-preflight.mjs runtime 'manifest.namespace = namespace' --absent passed): the dogfood file went 3 of 3 red (source export lacks namespace; install 403 NOT_OVERRIDABLE as quoted above; target row 404), the door pin 2 of 3 red. Restore proved blob == HEAD, git diff HEAD empty, git status --porcelain empty; runtime rebuilt, preflight found the marker in 2 built files, and the dogfood file was 3 of 3 green again.

Acceptance notes

Verification

Head 3d93d712 (main merged at d280265b, which carries #29521; the merge's two generated artifacts were regenerated from the merged tree and the docblock now counts seven fixed keys).

  • Round trip after the merge, dogfood closure rebuilt: test/package-export-reinstall-custom-pair.dogfood.test.ts Tests 3 passed (3); runtime export pins (4 files) Tests 12 passed (12); lock VERDICT command-exit 0.
  • pnpm --filter @objectstack/spec test at 3d93d712: Test Files 801 passed (801), Tests 23734 passed | 1 todo.
  • pnpm typecheck for spec, runtime and dogfood at 3d93d712: exit 0.
  • pnpm --filter @objectstack/runtime test: before the merge (6d249e4f) Test Files 530 passed (530), Tests 8515 passed | 19 skipped; the re-run at 3d93d712 was still running at hand-back (the merge brought one runtime test file and a vitest config line, no source).
  • pnpm --filter @objectstack/spec check:generated at 3d93d712: all 14 artifacts current.
  • eslint (--no-inline-config) on the five changed .ts files: exit 0. Repo-wide pnpm lint is CI's.
dispatch-gates --commands and the ran list

Before the merge (6d249e4f, 105 derived): 100 ran, 97 exit 0. NOT MEASURED, exit 3 (prerequisite not met): node scripts/check-plugin-teardown-shape.mjs --self-test (its fixture commit is outside the shallow clone), pnpm check:dual-build-cjs-loads (needs every package's dist/), and check:skill-examples (client-react unbuilt; after building it, exit 0, 281 examples type-check). The last five (check:type-check-debt onward) were stopped by the merge.

After the merge (3d93d712, 104 derived): re-running at hand-back; 45 had run, 44 exit 0, the same check-plugin-teardown-shape --self-test exit 3. CI runs every family.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic.
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; eslint passes on the changed files (repo-wide pnpm lint in CI).
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec touched: its generated artifacts are regenerated and committed.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English.
  • The Before Submitting list in CONTRIBUTING.md is done (not walked item by item in this run).

Generated by Claude Code

…encies (#29617)

PackageExportManifestSchema declares namespace and dependencies as optional
fixed keys, each the install manifest's own shape; assemblePackageManifest
writes both from the package's registry row, so a reinstalled .custom pair
keeps its namespace and the edge to its base.

Claude-Session: https://claude.ai/code/session_01JQwxbWPCwHb9Zxnf7ou79C
Co-authored-by: Claude <noreply@anthropic.com>
…nd trip through install-local; changeset

Claude-Session: https://claude.ai/code/session_01JQwxbWPCwHb9Zxnf7ou79C
Co-authored-by: Claude <noreply@anthropic.com>
…'s namespace and dependencies

Claude-Session: https://claude.ai/code/session_01JQwxbWPCwHb9Zxnf7ou79C
Co-authored-by: Claude <noreply@anthropic.com>
…hheldMaps; regenerate

Claude-Session: https://claude.ai/code/session_01JQwxbWPCwHb9Zxnf7ou79C
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Nov 2, 2026
@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 2 changed package(s); no hand-written page names any of them. ⚠️ 2 changed file(s) yielded no anchor (packages/runtime/src/domains/packages.ts, packages/spec/authorable-surface/api.json), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

What this run could not see
  • 2 changed file(s) yielded no anchor (packages/runtime/src/domains/packages.ts, packages/spec/authorable-surface/api.json) — pages documenting those are invisible to this run
  • the SDK route bridge reached 60 of 216 client-bound route-ledger rows — the other 156 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 156: 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; 101 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 — 173 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 3482f3d8d2c48ec8d2e3306f1ce26c0364e800c2 → packageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json 3482f3d8d2c48ec8d2e3306f1ce26c0364e800c2

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

Contract-review-claim: session_01AavokzJ5DndAwitDXvKy4U · head 3d93d71 · 2026-11-03T00:25Z

Taken under (c): the spec lane's seats have stood down. There is no record yet.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 3d93d712732f2ee47fc2566e072cc6fd2c1d3ce6
Local-runs: none
Scope: packages/spec/src/api/package-lifecycle.zod.ts, packages/runtime/src/domains/packages.ts (assemblePackageManifest), .changeset/29617-package-export-namespace-dependencies.md + packages/spec/src/kernel/manifest.zod.ts (ManifestSchema.namespace / .dependencies), packages/spec/src/kernel/unmanaged-install.ts (UNMANAGED_INSTALL_KEY_DISPOSITIONS), packages/metadata-protocol/src/protocol-customization-package.ts (customizationNamespace), packages/spec/authorable-surface/api.json and content/docs/references/api/package-lifecycle.mdx (generated, read)

Inputs read:

Taken by triage under (c): the domain:spec lane has stood down.

① Derived judgments

  • Shape: right. Each new key reuses the install manifest's own declaration:

    • namespace is ManifestSchema.shape.namespace: z.string().regex(/^[a-z][a-z0-9_]{1,19}$/).optional().
    • dependencies is ManifestSchema.shape.dependencies: z.record(z.string(), z.string()).optional().

    Each is re-described only. An export therefore refuses exactly what an install would, both keys stay optional, and there is no second copy to drift. The .custom pair namespace minted by customizationNamespace (<base>_custom) is already refused by the ensure door when it exceeds 20 characters, so every row the export reads passes this regex.

  • Producer: right. assemblePackageManifest writes namespace and a shallow copy of dependencies from registry.getPackage(id).manifest, only when the row declares them, after label. A row declaring neither writes neither (pinned in packages-export-registry-row-keys.test.ts). type is still not written, as the PR body says.

  • Round trip through the unmanaged import: right. UNMANAGED_INSTALL_KEY_DISPOSITIONS already classifies namespace as base (read to mint the base) and dependencies as identity (carried onto the new base's manifest). The import accepts both, and no new row is owed, as the body says. feat(spec): the package export manifest declares withheldMaps, classified door for the unmanaged import #29521's withheldMaps (door) and label (door) rows are untouched.

  • The measured defect is closed: a reinstalled pair landed under custom and was refused 403 NOT_OVERRIDABLE by the ADR-0146 D2 gate. The new dogfood file drives export → install-local across two real boots and checks the target row's namespace (showcase_custom) and its dependency on the base. The body records the ablation: the file was 3/3 red without the two writes.

  • Generated artifacts: authorable-surface/api.json (two rows) and package-lifecycle.mdx were regenerated on the merged tree. No strictness-ledger or dropped-refinements baseline is touched, so this PR is not on the Queue-flake anchor: scripts/strictness-ledger-doc.test.ts #29524 / Queue-flake anchor: scripts/dropped-refinements.test.ts #29530 collision surface. authorable-surface/api.json is generated, though, and packages: a package export marks each withheld connector header/env map, so an import knows it is incomplete (#28640 ruling 3A) #29342 is also about to edit it.

  • Gates on this head: 57 success, 8 skipped, 0 failures, 0 pending (total_count: 65). The PR reads mergeable_state: clean, draft.

② Semver level

@objectstack/spec: minor and @objectstack/runtime: minor, with Clause-②: yes (widening) and Metadata-protocol: added on both the PR body and the changeset. Two new optional keys on a published contract are an added kind, which takes at least minor on each package the diff moves (AGENTS.md step 3). This is consistent with the merged withheldMaps changeset on the same schema (added, spec minor). Right.

③ Boundary flags

④ Design judgment

Reading both keys off the registry row, with the install manifest's own schema as the contract, is the minimal true fix. The export now carries what an install decides by, under the same names the install door reads, with no translation layer. Writing nothing when the row declares nothing keeps old exports and non-pair packages byte-identical.

Tried to break it:

  • A row with no namespace: no key is written, and the install derives one as before. Right.
  • A dependencies map aliasing the row: it is copied, so a later mutation of the manifest cannot reach the row. Right.
  • An export carrying withheldMaps beside the new keys: the import's dispositions accept all three. Right.
  • A namespace longer than 20 characters: it cannot exist on a pair row, because the ensure door refuses it. A non-pair row came from an install that already passed the same regex.
  • Security: none. Both keys describe package identity and edges, never credentials.

Must-fix: none.

Should-fix: none.

Implemented-by: claude/project-thread (os-warren)
Reviewed-by: session_01AavokzJ5DndAwitDXvKy4U

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K

维护者速读

  • 改了什么:包导出清单(GET /packages/:id/export)多写两个可选键 namespace、dependencies,取自注册表行;spec 的 PackageExportManifestSchema 直接复用安装清单的同名声明。
  • 为什么改:.custom 配对包导出后再重装,会落到错误命名空间 custom、丢失对基础包的依赖,被覆盖闸门拒为 403 NOT_OVERRIDABLE。
  • 风险与回滚:行里没有就不写,旧导出照旧可装;回滚即撤回本 PR。
  • 先看:packages/runtime/src/domains/packages.ts 末尾 11 行与 package-lifecycle.zod.ts 的两个新键。
  • 两分钟验证:pnpm --filter @objectstack/dogfood exec vitest run test/package-export-reinstall-custom-pair.dogfood.test.ts 3 条全绿。
  • 需要你做的:无。键名已获批准(Warren 2026-11-03T00:08Z),契约复审 PASS 6592043758 在案,席位落地。

席位意见:最小真修复,形状零新增(两键均为 ManifestSchema.shape 原声明),同意落地。

席位核对 — ACCEPT · domain:spec#3 · head 3d93d712732f2ee47fc2566e072cc6fd2c1d3ce6

  • Shape: two optional fixed keys, each ManifestSchema.shape.<key> re-described; the export refuses exactly what an install refuses. Producer writes both from registry.getPackage(id).manifest only when declared, dependencies as a shallow copy. Read in the diff origin/main...3d93d712.
  • Premise measured: the filer's real-door measurement on 81cd9eb0, re-proved by the dev's ablation (dogfood round trip 3/3 red without the two writes, 403 NOT_OVERRIDABLE; restored 3/3 green), dev report 6591604566.
  • Gates (check-runs at this head): 65 total, 57 success, 8 skipped, 0 red. 8 files, +275 −3 (under the 3000-line threshold); no governed path, so Tier S/H does not apply.
  • Contract review: triage record 6592043758, Served-tier: CONTRACT_REVIEW_TIER, Head-sha: 3d93d712…, VERDICT PASS, no must-fix and no should-fix.
  • Declarations: Clause-②: yes (widening), Metadata-protocol: added, @objectstack/spec minor + @objectstack/runtime minor, on the body and the changeset. Skills-docs: none — REASON (export door writes, install door reads; no author writes them): accepted.
  • Accepted deviations: none. Out of scope, named in the body: type and the flat-collection refusal of POST /api/v1/packages (Acceptance notes; not a defect of this card).
  • Landing: the name approval is quoted in the body (## Name approval). The branch is 6 behind main with none of those commits touching the eight files or authorable-surface/api.json; the merge queue rebuilds on main. Land next: ready + auto-merge.

@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

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

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

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

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

  • Type Check UI — 失败步骤: Verify spec-named symbols are derived, not hand-written(日志不可读,点进 job 看)

↳ 失败原因 是判读的关键:超时(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,按失败测试文件聚合):

  • ⚠️ 本次没有可用的聚合签名(日志里没有能解析出测试文件名的 FAIL 行)—— 这不是「没有同签名的其他 PR」,是这一轮没测到。跨 PR 聚合本次不可用,请手工比对其他 PR 的同类评论。
  • ⚠️ 24h 评论账本没读完(超过 5 页仍未读到窗口尽头),所以上面的「不同 PR 数」是下界,不是全量。

历史信号:

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

分诊清单:

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

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

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

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

1 participant