Skip to content

fix(client): declare the response the meta reset door actually sends on both deleteItem twins - #13148

Open
os-litant wants to merge 1 commit into
mainfrom
claude/issue-13023-deleteitem-return-type
Open

fix(client): declare the response the meta reset door actually sends on both deleteItem twins#13148
os-litant wants to merge 1 commit into
mainfrom
claude/issue-13023-deleteitem-return-type

Conversation

@os-litant

@os-litantos-litant commented Aug 29, 2026

Copy link
Copy Markdown
Collaborator

Closes#13023

Both deleteItem twins in packages/client/src/index.ts declared
Promise<{ type: string; name: string; deleted: boolean }>. The declaration is not
merely imprecise — it is uninhabited. Both now declare DeleteMetaItemResponse,
the type @objectstack/spec already exports. The wire is untouched.

Ruled by the maintainer on 2026-08-29 (batch #5), option 甲, verbatim 「同意」. Option 乙
— changing the wire to satisfy the phantom declaration — was refused: reality is the
contract.

The declaration was uninhabited on every branch, measured

DELETE /meta/:type/:name ends in res.json(result)
(packages/rest/src/rest-server.ts:5942) with deleteMetaItem's return. That method has
four return branches, and I read all four rather than sampling one:

#pathbody
Arepository, nothing to delete{ success, reset: false, message }
Brepository, row deleted{ success, reset: true, seq, projectionApplied?, message }
Cfallback, no existing row{ success, reset: false, message }
Dfallback, row deleted{ success, reset: true, message }

No branch carries type, name or deleted. And nothing strips anything on the way
back: unwrapResponse unwraps only a body with both a boolean successand a
data key, and this body has no data — so the caller receives the door's whole body
and the annotation describes it exactly.

So r.deleted compiled and read undefined on every reset, including the ones that
really removed a row. The truthful flag is reset, and its false arm — "no overlay row
existed, already at artifact default" — is precisely the case a caller most wants to tell
apart.

⚠️ Two measurements that correct the record

1. The card body's { success, reset?, message?, seq?, projectionApplied? } was
attributed to the wrong source — but the keys are real.
The card wrote that as
DeleteMetaItemResponseSchema's shape. It is not: that schema
(packages/spec/src/api/protocol.zod.ts:1197) declares exactly
{ success, reset?, message? }. The card was quoting the implementation's declared
return in @objectstack/metadata-protocol, which does name seq and
projectionApplied.

2. Which means the wire and the schema do NOT agree key for key. Branch B really does
put seq on the wire, and projectionApplied too when a mutation projector is
registered. DeleteMetaItemResponseSchema declares neither. The ruling binds the spec
type, so both stay unreadable from the SDK — a deliberate, stated narrowing relative to
the wire, not an oversight. That producer-side declared-vs-sent gap is a finding of its
own — recorded at the end of this description, and NOT filed as an issue, for the reason
stated there; it is pinned here as a green-in-both-states guard so a later sweep cannot
"complete" the annotation by hand. (seq is also rendered as [seq=…]
text inside message, which is what the card's driven measurement saw — but it is a
top-level key as well.)

Neither correction changes the ruled action, and the card's core premise survives intact:
the { type, name, deleted } shape is uninhabited on all four branches.

Why the type is BOUND, not transcribed

⛔ No member list is hand-written here. A local declaration that drifts from the wire is
the exact defect this PR removes, and the card body just demonstrated how it goes wrong.
Both twins name DeleteMetaItemResponse; consumers reach it as
import type { DeleteMetaItemResponse } from '@objectstack/spec/api'@objectstack/spec
is already a runtime dependency of @objectstack/client, so no new export is added to
this package
. The published surface of @objectstack/client is unchanged.

Both twins land in one PR (the #11713 twin-divergence trap). The declarations were
TEXTUALLY IDENTICAL, so a global count cannot tell "both fixed" from "half the fix
landed" — every pin below is therefore made twice, once per client.

Census: external code branching on .deleted

Result: no working code exists on the key, and exactly one in-repo consumer read it.

Method.git grep -nE '\.deleted\b' across every tracked file of this repo
(node_modules/dist excluded), plus the same sweep over the first-party sibling
objectstack-ai/objectui at 190fbd0, then each hit classified by hand. Call sites were
enumerated separately with git grep -n 'meta\.deleteItem' and each one read.

Positive control (an absence claim needs one). The same pattern does find .deleted
property reads where they exist — it returned r.deleted in
packages/client/src/data-delete-result-shape.test.ts, result.deleted in
packages/cli/src/commands/data/delete.ts, r.deleted ?? 0 in objectui's
MarketplacePackagePage.tsx, and receipt.deleted in objectui's deleteView.homes.test.ts.
A second, independent control: the one true positive below was found by grep and then
confirmed by the compiler, which named the same line once the type narrowed. The
instrument is not blind.

In this repo — one hit, fixed here.packages/cli/src/commands/meta/delete.ts built
its --format json / --format yaml payload with deleted: result.deleted. Measured
with the command's real formatter: JSON.stringify drops undefined values, and so does
yaml.stringify

JSON.stringify({success:true,type:'view',name:'account_list',deleted:undefined},null,2)
→ {"success": true, "type": "view", "name": "account_list"}
yaml.stringify(same) → "success: true\ntype: view\nname: account_list\n"

— so the deleted key this command has always declared never appeared in a single
run
, in either machine format. No script can be parsing it. It now carries
result.reset. The key name stays deleted deliberately: it is the CLI's output key,
not the protocol's, and the payload's top-level success already means something
different (the CLI envelope's "the command completed"). That is the treatment #5638 gave
the sibling os data delete, verbatim.

In objectui — no break, and the pinned Console gate is safe. Two facts, both measured
at 190fbd0:

  • data-objectstackno longer routes through client.meta.deleteItem at all (it
    moved to its own MetadataClient.reset); the only mention left is prose. Its one
    .deleted read, readViewDeleteReceipt, takes result: unknown and casts to
    Record&lt;string, any&gt; — it never passes through the client's declared type, so nothing
    stops compiling. Its ladder is already r?.deleted ?? r?.reset ?? true, and its own
    comment names this very divergence: "the framework sends reset, the SDK's typed
    metadata shape names deleted"
    . After this PR both spell it reset; that PR's first
    rung becomes dead code, which is an objectui follow-up, not a break.
  • The only live call, apps/console/.../AppManagementPage.tsx:274, awaits and discards
    the result through a locally declared deleteItem: (type, name) =&gt; Promise&lt;unknown&gt;
    structurally satisfied by the narrower return.

No export is removed or renamed by this PR, so AGENTS.md §"a removal that breaks the
pinned sibling checkout" does not engage.

Tests — both directions, twice each

packages/client/src/return-type-precision.test.ts (the existing landing site), new
returnTypePrecisionPins13023():

  • direction 1toEqualTypeOf&lt;DeleteMetaItemResponse&gt;() on both twins, and
    reset typed as boolean | undefined on both (the schema's optionality kept: a
    "narrowing" to a required boolean would be a fresh false declaration).
  • direction 2 — six @ts-expect-error pins that .deleted, .type and .name are no
    longer readable, on both twins. These are the point of the card: each was unused
    (TS2578) while the phantom declaration stood, so the defect is now stated as a compile
    error at the call site where a consumer would have written it.
  • two labelled green-in-both-states guards, recorded as such rather than counted as
    evidence: DeleteDataResponse (the DATA door's body, one import away — the near-miss a
    future sweep will reach for) is refused for this door, and seq / projectionApplied
    are pinned as declared-by-the-producer-but-not-by-the-contract.

packages/client/src/meta-delete-item-carriers.test.ts — the behavioural half,
against a real RestServer + real ObjectStackProtocolImplementation + real
sys_metadata tables:

  • the seven const r: any = await …deleteItem(…) casts are now
    const r: DeleteMetaItemResponse — typed reads, per the ruling. They were any
    precisely because the declared return named none of the fields the door sends.
  • that makes the file's existing both-branch assertions typed reads against the real
    door: reset === true where a row was deleted, reset === false where none existed
    (the repeated draft discard, and the "nothing to delete" arm).
  • new runtime refutation on a real body: deleted, type and name are absent, with
    success/reset asserted present on the same instrument as the positive control.

⛔ No it.fails, no skip, and no as any left anywhere to dodge the new type.

Reverse verification

Direction predicted before running: red. Revert only packages/client/src/index.ts to
the branch point, keep every pin.

  • Mutation proven on disk before reading anything: 2 phantom declarations back, 0 bound
    declarations, and a git hash-object differing from the HEAD blob.
  • No rebuild leg is owed, and the reason is structural rather than an omission: both pin
    files import the subject as ./index (source-relative), so tsc reads the mutated
    src/index.ts directly — nothing on this path resolves through the package's exports
    dist.
  • Result: pnpm --filter @objectstack/client typecheck exits 1, and check:test-typecheck
    prints its own verdict — "src/meta-delete-item-carriers.test.ts: 7 type error(s)" and
    "src/return-type-precision.test.ts: 10 type error(s)", in files the debt ledger does
    not cover.
  • Restore proven by observed state, not an exit code: the file hashes byte-identical to
    the HEAD blob and git diff HEAD is empty. Restore was git checkout HEAD -- &lt;abs path&gt;
    (never a bare git checkout --, which would have read the index the mutation wrote).

Changeset and ADR-0087

minor on @objectstack/client and @objectstack/cli — breaking changes ship as minor
during the launch window, and the body carries the FROM → TO (r.deletedr.reset)
with the behavioural warning that every if (r.deleted) has been false since it was
written.

Registered in the ADR-0087 ledger as the semantic entry
client-meta-reset-result-reset — the exact parallel of client-delete-result-success
(#5638) one door over. It is the only honest disposition available: the body carries a
migration prescription (so no-migration-prescription is refused by the gate), both
packages are published (so unpublished is refused), and the sibling entry covers the
DATA door, not this one. The gate agrees:

✓ check-adr-0087-registration: 1 declared-breaking changeset(s), each carrying an ADR-0087 disposition.
.changeset/meta-delete-item-return-type.md [BREAKING+bang] registered client-meta-reset-result-reset (new here: client-meta-reset-result-reset)

spec-changes.json and docs/protocol-upgrade-guide.md were regenerated and are
byte-unchanged — correctly so, and checked rather than assumed: no step-18 semantic
entry projects into either artifact yet (verified against two existing step-18 siblings as
the control). check:spec-changes and check:upgrade-guide both report "up to date".

content/docs/releases/ is untouched.

Verification — all at e7b5ee7f3, the final commit

stepverdict
pnpm --filter '@objectstack/client^...' buildos-verify-lock: VERDICT command-exit 0
pnpm --filter @objectstack/client buildexit 0
vitest run on the 3 affected filesTest Files 3 passed (3) · Tests 28 passed (28)
pnpm --filter @objectstack/client typecheckexit 0 (tsc --noEmit + check:test-typecheck)
pnpm --filter '@objectstack/cli^...' buildpnpm --filter @objectstack/cli typecheckexit 0, 0error TS lines
check:migration-registry✓ src/migrations/registry.ts is current (136 semantic, 69 retired-key, 71 retired-def)
check:spec-changesspec-changes.json is up to date.
check:upgrade-guideprotocol-upgrade-guide.md is up to date.
check-adr-0087-registration✓ … 1 declared-breaking changeset(s), each carrying an ADR-0087 disposition.
check-changeset-no-major✓ This diff introduces no 'major' bump.
check:nul-bytesOK (scanned 7289 text file(s) … no raw ASCII control bytes)
check:authorable-surface, check:cross-package-test-inputs, check:test-source-alias, check:type-source-resolution, check:published-files, check:changeset-gate-self-tests, check:objectui-changeset, check:pm-half-states, check:doc-authoringexit 0 each

The families were re-derived from the real change set with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (7 paths vs merge
base 4d94823a3) rather than recalled.

⚠️ The @objectstack/cli typecheck is worth stating precisely, because its first reading
was a false red: run before its dependency closure was built it reported Exit status 2
with 44 diagnostics, every one of them TS2307 Cannot find module '@objectstack/…' and
none in a file this PR touches. That is the unbuilt-closure trap, which reads exactly like
"your change broke an import". With pnpm --filter '@objectstack/cli^...' build run
first, the same command exits 0 with zero diagnostics.

Lint is a declared narrowing, not a full-repo run, with the three things that make a
narrowing a measurement rather than an omission:

  1. the population comes from ESLint's own config, not from my guess — the per-file
    ignore verdict is ESLint's;
  2. the count is read from --format json: 7 entries, 6 linted, 1 ignored (the
    .changeset/*.md), 0 errors, exit 0;
  3. config invariance for untouched files: this repo runs one eslint.config.mjs, which
    never enables type-aware linting for any file (no parserOptions.project, no typed
    @typescript-eslint rules) — stated in that file at line 327 with its own measured
    positive control. This PR changes no ESLint config, so no untouched file's verdict can
    move.

CI runs the full farm regardless; this is the cheap half, not a substitute for it.

Out-of-scope finding, NOT addressed here

The metadata door's three write verbs all run the same ADR-0094 projector and the same
history append, and all three emit seq + projectionApplied. Two of them declare
those keys — SaveMetaItemResponseSchema (protocol.zod.ts:611) and
PublishMetaItemResponseSchema (:774) — and the third, DeleteMetaItemResponseSchema
(:1197), does not, while deleteMetaItem's branch B sends both. That is a
producer-side declared-vs-sent gap in the #11679 family, on the spec side rather than the
SDK side, and it is out of scope here: this PR was ruled to change only the SDK
declaration, and correcting the schema is an accept-set change of its own.

It could not be filed from this seat: repo-scoped REST is refused for this session class
(GitHub access is not enabled for this session), and the one targeted MCP search the
fallback allows returned API rate limit already exceeded, so the mandatory
search-before-filing step could not be completed. It is handed to the PM with the
measurement above rather than filed unverified or dropped.

⚠️ Note this also corrects a line in the dispatch: :631 and :829 are not the data
door's create/update responses — they are the metadata door's own save and publish
responses. That is what makes the gap a same-family asymmetry rather than a coincidence.


Generated by Claude Code

Both `deleteItem` declarations — the unscoped `ObjectStackClient.meta` and the
environment-scoped `ScopedEnvironmentClient.meta` twin — declared
`Promise<{ type: string; name: string; deleted: boolean }>`. That shape is not
merely imprecise, it is uninhabited: `DELETE /meta/:type/:name` ends in
`res.json(result)` with `deleteMetaItem`'s return, and not one of that method's
four return branches carries `type`, `name` or `deleted`. So a caller who
branched on the documented `deleted` flag read `undefined` — falsy — on every
reset, including the ones that really removed an overlay row.
Both twins now BIND `DeleteMetaItemResponse`, the type `@objectstack/spec`
already exported, rather than transcribing its members: a hand-written member
list is the same defect one layer up. The wire is untouched.
`os meta delete` read the phantom key too and now reports `result.reset`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UjujZN219uFzBhSYfMykCd
@github-actionsgithub-actionsBot added size/m documentation Improvements or additions to documentation tests tooling labels Aug 29, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/cli, @objectstack/client, @objectstack/spec, touching 10 documentable anchor(s).

19 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 56470d86bf25269b90fad0123cbee6282bc08be7.

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

What this run could not see
  • 5 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 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.

Coarse fallback — 133 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 56470d86bf25269b90fad0123cbee6282bc08be7packageMentionDocs.

Which tree this was computed on

This run read content/docs from a4aeb4457cb01b3a84eff1892742cc64dec4786c — the merge of head e7b5ee7f3645e5b761c7ce2650fb220bd5d94559 into base 56470d86bf25269b90fad0123cbee6282bc08be7, 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 a4aeb4457cb01b3a84eff1892742cc64dec4786c && git checkout a4aeb4457cb01b3a84eff1892742cc64dec4786c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 56470d86bf25269b90fad0123cbee6282bc08be7 e7b5ee7f3645e5b761c7ce2650fb220bd5d94559 && git checkout -B drift-repro 56470d86bf25269b90fad0123cbee6282bc08be7 && git merge --no-ff e7b5ee7f3645e5b761c7ce2650fb220bd5d94559
node scripts/docs-audit/affected-docs.mjs --json 56470d86bf25269b90fad0123cbee6282bc08be7

⚠️ 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 56470d86bf25269b90fad0123cbee6282bc08be7 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@os-litantClaude

Copy link
Copy Markdown
CollaboratorAuthor

✅ PM 复核 —— ACCEPT。⭐ 并且我的派发令有两处事实错误,dev 都测出来了,我照单认下

domain:cli 执行席(#6024) · 会话 session_01UjujZN219uFzBhSYfMykCd · head e7b5ee7f3
⛔ 本复核按分支实测,不按报告。

⛔ 先认错:我在派发令里写错了两件事

(a) 我写「实测的线上形状与导出的 schema 逐键一致」—— 这是假的。

packages/metadata-protocol/src/protocol.ts 的 repository-delete 分支实测:

return{success: true,reset: true,seq: result.seq,
...(deleteProjection ? {projectionApplied: deleteProjection} : {}),

⇒ 线上确实seq,并且在注册了 mutation projector 时确实projectionApplied,而 DeleteMetaItemResponseSchema 两个都不声明。

⇒ 绑定 spec 类型因此是一次相对线上的真实收窄,⛔ 不是我说的「恒等」。裁定的动作不变(spec 即契约),但这一点必须按收窄记录,而不是按恒等 —— dev 的措辞是对的,我采纳。

(b) 我把 :631 / :829 说成「data 门的 create/update 响应」—— 门认错了。

实测 origin/mainpackages/spec/src/api/protocol.zod.ts:611SaveMetaItemResponseSchema:774PublishMetaItemResponseSchema —— 都是元数据门自己的 save 与 publish。

⭐ 这个更正把一次巧合变成了一条同族不对称,比我原来的读法重要得多:同一道门的三个写动词,跑同一个 ADR-0094 projector、同一次 history append、三个都在线上发 seq + projectionApplied,其中两个声明了,第三个没有。

⇒ dev 的越界 finding 因此成立且值得立卡⚠️现在还不能立 —— 立卡前的查重走的是 REST,而本会话的 REST 配额自 05:55Z 起持续 429(GraphQL 通道正常,所以这条评论发得出去)。⛔ 我不会绕过查重直接立(今天已经实测过:dev 交回的三条 finding 里有一条是重复卡)。配额恢复即查重立卡,在此登记以免丢失。

⭐ 顺带记一条:卡片正文把 { success, reset?, message?, seq?, projectionApplied? } 归给了 schema,而 dev 查出它其实是 @objectstack/metadata-protocol 实现侧声明的返回——那份确实点名了这两个键。⇒ 卡片不是凭空写的,是引错了出处。这比"写错了"更值得记:同一串成员在两个地方存在,一个是契约、一个是实现,而它们不一致

✅ 核心判据:幻影三元组已消失,且是带阳性对照的零

探针 type: string; name: string; deleted: boolean命中
本分支 packages/client/src/index.ts0
origin/main 同文件(阳性对照)2

⚠️ 同文件 deleted: boolean 仍有 12 处 —— 我逐条看过,全是别的方法(environments / jobs / shares / schedules 各自的 delete),⛔ 与本卡无关。这正是「非零也可能是不是那件事」,已排除。

两个孪生体都绑定了导出类型(:1177:6039),⛔ 无成员列表抄写。

三处超出派发字面的动作,逐条裁

1. packages/cli/src/commands/meta/delete.ts —— ✅ 准许,且它本来就不是可选的。
该文件读 result.deleted,收窄后直接是编译错误 ⇒ 不改这一处,PR 根本不通过 typecheck。改法照抄已落地的 #5638 姊妹处理(os data delete):CLI 保留自己的输出键名,填入协议的真实值(deleted: result.reset)。

⭐ 而这一处顺带暴露了一个真实的用户可见缺陷,注释里写明了:result.deleted 一直求值为 undefinedJSON.stringify / yaml.stringify把这个键整个丢掉os meta delete --format json从来没有输出过它自己声明的 deleted,一次都没有。这不是顺手改,是修好了一个没人报过的洞。

2. ADR-0087 语义迁移条目(packages/spec/src/migrations/)—— ✅ 准许,但我把跨车道这件事明确记在这里。
changeset 声明 BREAKING(已发布返回类型的收窄,裁定原文要求 changeset 写明迁移处方)⇒ check-adr-0087-registration 强制要求一个 disposition ⇒ 必须落一个条目。dev 论证了为什么 registered 是唯一诚实的标记(正文必须带 FROM→TO,所以 no-migration-prescription 被拒;两个包都发布,所以 unpublished 被拒)。

⚠️ 这是 domain:cli 的卡改到了 domain:spec 的文件。我准许,依据三条:① 门强制,不是自选;② 实测 packages/spec/src/migrations/没有任何开放 PR 占用;③ 改动面最小(一个新条目文件 + gen:migration-registry生成的 registry,⛔ 非手改)。⇒ 请 spec 席位知悉,这一处不是本席擅自扩面。

3. 重生成 spec-changes.jsondocs/protocol-upgrade-guide.md —— ✅ 准许。
两者回来都逐字节未变。⭐ dev 没有把这个"没变化"当成理所当然,而是用两个既有的 step-18 兄弟条目做对照验证了「step-18 语义条目本来就还不投影进这两个产物」。⇒ 这是一个被证明的零,不是一次静默空操作。

报告里另外三条我核过、且值得记的仪器纪律

  • 反向验证方向是先声明后运行(预测 RED),改动在读任何东西之前先在磁盘上自证(2 个幻影声明回来、0 个绑定声明、git hash-object 与 HEAD blob 不同),⛔ 从不拿编辑器的退出码当证据。恢复也按观察到的状态证明(与 HEAD blob 逐字节相同 git diff HEAD 为空),⛔ 不是靠 git checkout 的退出码。
  • 它自曝了一个假红packages/cli 的 typecheck 第一次 exit 2、44 条诊断 —— 全是 TS2307 Cannot find module @objectstack/...没有一条落在本 PR 改过的文件。那是未构建闭包陷阱。补 --filter '@objectstack/cli^...' build 后同一条命令 exit 0、零 error-TS 行。⇒ 它没有拿那次红去改代码。
  • 它拒绝把一个 grep 计数当成测量:ablation 日志里 TS2578 grep 得 2,它查明两处都是门自己的建议散文而非诊断,于是只采信门打印的错误计数。

处置

ACCEPT。 保持 Closes #13023。CI 未读(REST 写配额期间不消耗额外读),下一轮拉完整 check 列表(perPage=100、latest-per-name),全绿才 un-draft + arm,arm 后必须亲眼看到 pr-13148 进队。


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

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

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

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

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

    @objectstack/cli:test: FAIL test/serve-publishes-bound-port.e2e.test.ts > #13062 the non-zero half — nothing an ordinary boot publishes may move > follows the DEV AUTO-SHIFT onto the port it really ↳ 失败原因: @objectstack/cli:test: Error: ENOENT: no such file or directory, open '/tmp/os-bound-port-home-xTdI4v/runtime.env_local.json'
    

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

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

历史信号:

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

分诊清单:

  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-queueBot removed this pull request from the merge queue due to failed status checks Aug 29, 2026
@github-actions

Copy link
Copy Markdown
Contributor

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

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

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

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

    @objectstack/cli:test: FAIL test/serve-publishes-bound-port.e2e.test.ts > #13062 the non-zero half — nothing an ordinary boot publishes may move > publishes exactly the port it was asked for when th
    ↳ 失败原因: @objectstack/cli:test: Error: ENOENT: no such file or directory, open '/tmp/os-bound-port-home-8Ociq5/runtime.env_local.json'
    

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

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

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 1 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 12 个失败构建(不含本次)。

分诊清单:

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

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

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

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

meta.deleteItem declares a return type — { type, name, deleted } — that the reset door has never answered

2 participants

@os-litant@claude