Skip to content

fix(objectql): a by-id delete the driver answers as removing nothing leaves no recycle-bin entry - #31702

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-0gxwgx
Nov 9, 2026
Merged

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

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steve · project thread

Fixes #31630

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:按 id 删除一条业务记录时,如果驱动回答"没删掉"(false 或 0),引擎现在会在同一个事务/工作单元里把刚写进回收站的那条快照撤回,回收站不再留下指向一条仍然存在的记录的无效条目。
  • 为什么改:快照是在物理删除之前写的;驱动答"没删掉"时条目一直留着,管理员会在回收站列表里看到它,点恢复会被 RESTORE_UNIQUE_VALUE_TAKEN 拒绝,直到 TTL 清理才消失。
  • 风险与回滚:只改引擎按 id 删除这一条分支;快照内容、恢复/清除、recycleBinEntryId 的回答都不变。撤回失败只记一条 error 日志,不改删除的返回值。回滚即整体 revert。
  • 先看:① packages/objectql/src/engine.ts 的 runByIdDelete(新增的 else await entry?.withdraw())② packages/objectql/src/recycle-bin/capture.ts 的 RecycleBinCapture.write 返回的 withdraw/compensate(共用一个 remove)③ capture.test.ts 新 describe。
  • 两分钟验证:pnpm --filter @objectstack/objectql exec vitest run src/recycle-bin/capture.test.ts,预期 27 条全过,其中新增 6 条。
  • 需要你做的:无,席位落地。

What changes

  • @objectstack/objectql, the by-id delete arm (runByIdDelete in engine.ts). After the root row's driver.delete, an answer of false (not found) or an off-contract 0 now calls entry.withdraw(): the recycle-bin entry this delete wrote is removed through the bin's driver with the driver options it was written with, so inside the delete's transaction when one covers the bin, and immediately otherwise. A confirmed removal is unchanged: exactly one entry, and onRecycleBinEntry is told its id. The recycleBinEntryId behaviour from PR feat(rest): the recycle bin has REST doors, and a delete answer names the entry to undo (#30237 PR 5) #31595 is untouched (still withheld for false / 0).
  • recycle-bin/capture.ts. RecycleBinWrittenEntry gains withdraw(). compensate() (a removal that threw, no transaction) and withdraw() share one remove(outcome) that keeps the existing failure handling: no entry id ⇒ one error line; the bin's delete throws ⇒ one error line with the driver fault redacted (redactBoundStatement), never replacing the delete's own answer or error. The module docblock gains step 5 and says why the entry is still written first.
  • Changeset: .changeset/31630-recycle-bin-no-ghost-entry.md (@objectstack/objectql patch): admins no longer see entries in the recycle-bin list for records that are still live.

Why withdraw (a) and not write-after-confirm (b)

  • Business need. Both stop the ghost entry an admin sees. Measured on main (table below): every in-tree driver answers false for a row it did not remove, so the case is reachable on memory, SQL and Mongo alike, not only off-contract.
  • Long-term soundness. The delete plans no transaction when nothing references the object (planCascadeAtomicity → 'none', the common case) and none across datasources ('split'). There, (b) would write the entry after the row is gone, and a bin write that then fails would leave a removed row with no entry to undo it and the caller an error for a delete that happened; nothing can compensate that. Written first, as today, a bin that cannot take the entry refuses the delete before the row goes. (a) keeps that guarantee and adds its converse: no entry without its removal.
  • Hard for an AI to get wrong. One seam: the written entry carries both of its undo paths (compensate for a throw, withdraw for a removal that removed nothing), chosen at the one place the driver's answer is read. The ordering stays the one the module docblock already explains; (b) would move three documented steps.
  • No scope spread. One method on an internal return type and one else branch; no spec, wire, error code or option moves.

Step 0 (measured on main @ 8c9260f79)

Question Reading
Order in the by-id arm (runByIdDelete) openRecycleBinCapture → cascadeDeleteRelations (collect, judge restricts, onCollected reads the snapshot, walk) → bin.write() (raw driver.create on sys_recycle_bin) → driver.delete(root)
Same unit? Yes. planCascadeAtomicity 'atomic' ⇒ all of it runs inside this.transaction(...) and the entry is written with that handle; 'none' / 'split' ⇒ no transaction, the entry is written bare.
Driver answers false / 0 on main 1 entry stays in sys_recycle_bin; recycleBinEntryId is withheld (removed !== false && removed !== 0 gate); the delete answers false / 0. compensate() runs only on a throw.
driver-memory (MemoryDriver.delete) false when no row has the id (throws instead under strictMode); true after the splice.
driver-sql (SqlDriver#delete body in sql-driver-sequence-semantics.ts) count > 0: false when the id + tenant scope matched no row (each rotation shard probed newest-first). Covers sqlite-wasm and turso local too (they inherit SqlDriver).
driver-mongodb (MongoDBDriver.delete) deletedCount > 0: false when nothing matched.
0 No in-tree driver answers it; only an off-contract driver would. Kept because PR #31595 already treats it as "kept".
Cascade-set path (engine-delete-cascade.ts / the walk in engine-cascade-planning.ts) Not the same shape. Each cascade child is deleted through this.delete() inside the inherited set, where openRecycleBinCapture returns undefined (a member of the set is a row of its root's entry), and the predicate arm (multi) opens no capture. No entry is written ahead of an unconfirmed removal there, so nothing to fix; see Acceptance notes for a neighbouring question.

Tests

New top-level describe in the existing harness packages/objectql/src/recycle-bin/capture.test.ts (its fake driver gains a removesNothingOf switch: answers the given value and keeps the row). No existing test edited.

  • positive: a removal the driver confirms keeps exactly one entry, one bin write (create).
  • false and 0, no transaction (loner): the delete answers the same value, the row is still there, no entry, the bin saw create then delete (written then withdrawn, not merely never written), no callback, no error log.
  • false and 0, inside the cascade's transaction (account tree): no entry, and the withdrawal carried the same transaction handle as the write.
  • the bin refuses the withdrawal: the delete still answers false, the entry stays, one error line.
Ablation (red, then restored)

node scripts/ablation-replace.mjs --file packages/objectql/src/engine.ts replacing else await entry?.withdraw(); with else void 0; (anchor 1 → 0, blob a501f350c801 → 6d46e7296474); the suite imports ../engine.js from source, so no dist leg.

× withdraws the entry when no transaction covered it, the driver answering false (not found)
× withdraws the entry when no transaction covered it, the driver answering 0 (an off-contract count of none)
× withdraws the entry inside the transaction that wraps the cascade, the driver answering false
× withdraws the entry inside the transaction that wraps the cascade, the driver answering 0
× logs at error, and keeps the answer, when the entry cannot be withdrawn
AssertionError: expected [ { object_name: 'loner', …(7) } ] to have a length of +0 but got 1
Test Files  1 failed (1) · Tests  5 failed | 22 passed (27)

Restored: blob after restore a501f350c801 == HEAD, git diff HEAD empty. The positive pin (exactly one entry) stays green under the ablation, as it should.

Acceptance notes

  • Cascade, root answers false after the walk. In the 'atomic' plan the walk has already removed the root's cascade children (and cleared set_null references) when the root's own removal answers false; the transaction commits because nothing threw. The withdrawn entry was the only snapshot of those children. Before this PR that entry stayed, but while the root is live its restore is refused RESTORE_UNIQUE_VALUE_TAKEN for the whole entry (the entry is the unit), so that undo was not reachable. Where the root was already gone (another actor removed it between the snapshot read and this removal), the old entry could have brought the root and those children back, and now there is none. Whether that case should roll the unit back instead is a question about the delete's answer, not about the entry; it is left to the seat (report open_questions).
  • A cascade child the driver answers false for keeps its row in the root's snapshot. Not this card's shape (the entry is the root's, and its root was removed), and changing the snapshot is outside this card. Noted, not filed (read-only inference, no reproduction).
  • The recycleBinEntryId jsdoc in protocol.zod.ts (contract review N4) lists when the key is absent; with this PR the false / 0 case also writes no entry, so "absent when the delete wrote none" now covers it. No spec edit.

Verification

All readings on head c7032b19 (branch base 8c9260f79), through scripts/pm/os-verify-lock.sh, each VERDICT command-exit 0:

Run Result
turbo run build closure (dogfood, objectql, driver-memory, driver-sql) exit 0
pnpm --filter @objectstack/objectql test 572 files / 11622 tests passed
pnpm --filter @objectstack/objectql typecheck exit 0
eslint packages/objectql --no-inline-config (root config) exit 0
pnpm --filter @objectstack/driver-memory test 75 files / 1772 tests passed
pnpm --filter @objectstack/driver-sql test 245 passed, 13 skipped files / 4132 passed, 222 skipped tests
pnpm --filter @objectstack/dogfood test (full, no shard) 440 passed, 3 skipped files / 3652 passed, 41 skipped tests
Derived gates: 69 / 69, reconciled

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 69 commands; all 69 run, each exit 0. --ran reconciliation: "69 derived famil(ies) accounted for — 69 run, 0 NOT-MEASURED". Three first-pass exit 3s were container prerequisites, not findings, and were re-run green: check-plugin-teardown-shape.mjs --self-test (fixture commit fetched), check:dual-build-cjs-loads (after a full pnpm build), check:type-check-debt (lock queue-timeout behind this card's own dogfood run).

test:repo was not run locally (no diff in that project); CI's Test Core carries it.


Generated by Claude Code

…leaves no recycle-bin entry

The capture writes the entry before the root row is removed; when the
driver answers false (or an off-contract 0) the entry is now withdrawn
inside the same unit, so the bin keeps no entry for a row that is still
live.

Claude-Session: https://claude.ai/code/session_01ADHkmR6Bca8k3KKZsr3Ej4
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 8, 2026
@github-actions

github-actions Bot commented Nov 8, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

2 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
  • 1 anchor(s) matched too much of the corpus to be a work list: ObjectQL (symbol, 80 pages)
  • 2 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 65 of 231 client-bound route-ledger rows — the other 166 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 166: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 59 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 107 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 — 21 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 82a3ed2e861d260fdd65517e6b17f03c42eb90c2 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 0dd5db2fc95b1aa7db36f2dc68351d17e48c0632 — the merge of head c769a547bca53b06f472cfe4fcfd75fe5b2c4876 into base 82a3ed2e861d260fdd65517e6b17f03c42eb90c2, 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 0dd5db2fc95b1aa7db36f2dc68351d17e48c0632 && git checkout 0dd5db2fc95b1aa7db36f2dc68351d17e48c0632
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 82a3ed2e861d260fdd65517e6b17f03c42eb90c2 c769a547bca53b06f472cfe4fcfd75fe5b2c4876 && git checkout -B drift-repro 82a3ed2e861d260fdd65517e6b17f03c42eb90c2 && git merge --no-ff c769a547bca53b06f472cfe4fcfd75fe5b2c4876

node scripts/docs-audit/affected-docs.mjs --json 82a3ed2e861d260fdd65517e6b17f03c42eb90c2

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#4 · os-steve · session_01ADHkmP8JJo4zF2sjTBRwnX

Flake reading · PR #31702 · head a2ffc1687defa450f8c38c88224a23ff982773a3 · 2026-11-08T22:53Z

Test Core (7/8) (job 128881878254, CI run 42529745507 attempt 1) failed on one test only: @objectstack/plugin-security src/delegated-admin-own-name-set.posture.test.ts › posture 'isolated' › "the 4 states get, at every door, the answer the id naming no user gets". This is the known per-posture loop timeout filed as #31663 (main push 529e5351 timed out the same it, the next push was green). This PR touches only packages/objectql (engine delete path and recycle-bin capture) and no plugin-security file. Every other shard and check on this head is green. Judged a flake, so the failed jobs are re-run once, after the run completes, through run_rerun_failed. A second failure is treated as real.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#4 · os-steve · session_01ADHkmP8JJo4zF2sjTBRwnX

ACCEPT · PR #31702 · #31630 · 2026-11-08T23:18Z

Head-sha: a2ffc1687defa450f8c38c88224a23ff982773a3

  • Seat review (TIER_DEFAULT; Clause-②: no, Metadata-protocol: none; no contract face is hit, so no contract-review record is owed): RecycleBinWrittenEntry.withdraw() removes the entry when the driver answers that the by-id delete removed nothing (false, or an off-contract 0). It uses the unit's own driver options, so it runs inside the covering transaction when there is one. It shares compensate's removal path, never throws, and logs a failed removal at error without changing the delete's answer. recycleBinEntryId is still told only for a removed row. Snapshot, restore and purge are untouched. Writing the entry before the removal is kept on purpose: a write after a non-transactional removal could leave a removed row with no undo. Right.
  • Open question (os-dev-report on recycle bin: a by-id delete whose driver removes nothing leaves a bin entry snapshotting a still-live row #31630): the root answers false after an atomic cascade has already removed its children. Ruled A, keep: the entry is withdrawn as the card's acceptance asks. Accepted deviation: in that race the cascaded children have no bin entry to restore from. The race is rare and needs a concurrent delete of the same root. No follow-up card.
  • Cascade-set path: not the same shape (children open no capture), left unchanged, as the delta allows.
  • Surface: claim on recycle bin: a by-id delete whose driver removes nothing leaves a bin entry snapshotting a still-live row #31630 (packages/objectql/src/**) covers every file in the diff.
  • Tests and ablation: 6 new pins in capture.test.ts; removing the withdraw call turns 5 red, restored. After the merge round on this head: objectql 11622, driver-memory 1772, dogfood recycle-bin files 148, objectql typecheck 0.
  • Prose face (TIER_DEFAULT): the changeset's sentences match the diff: entry written before removal; withdrawn in the same unit on false/0; one entry and an unchanged recycleBinEntryId on a confirmed delete; snapshot, restore and purge untouched; a failed withdrawal logged at error.
  • Gates on this head: CI attempt 1 had one red, Test Core (7/8), on plugin-security delegated-admin-own-name-set.posture.test.ts. That is the known timeout carded as plugin-security: delegated-admin-own-name-set.posture.test.ts L878 loops every state through every door inside one it() on the default 5 s timeout, and timed out on main push 529e5351 #31663, in a package this diff does not touch (flake reading 6728293512). The failed jobs were re-run once through run_rerun_failed and passed. Every check-run on this head is now success (35) or skipped (15). check-governed-merges --pr 31702: not governed, +128/-23 (Tier S). K = 4 (< 15), mergeable_state clean.
  • Landing: pr_ready + automerge_enable in this stroke. Card closes on merge (Fixes #31630).

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 8, 2026 23:18
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 8, 2026 23:18
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 8, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Nov 9, 2026
@github-actions

github-actions Bot commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

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

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

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

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

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

    @objectstack/plugin-security:test:  FAIL  src/delegated-admin-own-name-set.posture.test.ts > [ADR-0090 D12] a delegated answer about a user the caller cannot read does not depend on that user's existe
      ↳ 失败原因: @objectstack/plugin-security:test: Error: Test timed out in 5000ms.
    

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

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

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

历史信号:

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

分诊清单:

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

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#4 · os-steve · session_01ADHkmP8JJo4zF2sjTBRwnX

Queue failure reading · PR #31702 · head a2ffc1687defa450f8c38c88224a23ff982773a3 · 2026-11-09T00:18Z

The merge queue dequeued this PR. In merge-group CI run 42536035987, job Test Core (7/8) (128900969406) failed, and the job ran on main 82a3ed2e with this head on top.

The failure is in the same it as the PR-run failure that flake reading 6728293512 judged a flake. It is @objectstack/plugin-security src/delegated-admin-own-name-set.posture.test.ts:878, posture 'isolated', "the 4 states get, at every door, the answer the id naming no user gets", and it hit Test timed out in 5000ms.

That reading said a second failure is treated as real, and this is the second one. The CI runs on the last 10 main pushes (21:40Z–00:02Z, 1a20cbc8 … 82a3ed2e) are all green. Twice on this head and zero times on main over the same window is no longer the base rate of #31663.

The loop calls h.engine.delete(...), at :274, and at :906 inside the per-state answers. This PR changes the engine's delete path, adding recycle-bin capture. A slower delete is a plausible way for this PR to push that it past 5 s. So this reading does not re-queue the PR, and nothing is re-run. The dev will measure the it's time on main against this head. If the capture adds cost to deletes on objects the recycle bin does not cover, that cost is the defect to fix here. The test's timeout is not the fix.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#4 · os-steve · session_01ADHkmP8JJo4zF2sjTBRwnX

ACCEPT (re-queue) · PR #31702 · #31630 · 2026-11-09T01:04Z

Head-sha: c769a547bca53b06f472cfe4fcfd75fe5b2c4876

  • Review carried over: the seat review in ACCEPT 6728563820 on a2ffc168 still applies. The head moved only by a merge of origin/main, with no rebase and no force. The seat re-derived the net diff against each merge base, with index/@@ lines dropped. It is byte-identical on both heads: sha256 d16725f0…59b1 from git diff e81636ce a2ffc168 and from git diff 82a3ed2e c769a547. That is 4 files, +128/-23. No reviewed face moved.
  • Queue failure (reading 6729235459) cleared by measurement, not by a re-run. The dev timed delegated-admin-own-name-set.posture.test.ts:878 (posture 'isolated') three times on each side, using main's objectql sources against this head's:
    • head: 819, 711 and 796 ms;
    • main: 818, 765 and 792 ms.
      The two are the same within noise and far under the 5000 ms limit.
  • Uncovered-object deletes: a by-id delete of sys_user_permission_set or sys_user_position makes the same driver calls with the bin registered as without it. It never touches sys_recycle_bin. openRecycleBinCapture returns before any read for sys_/cloud_/ai_ objects, and the existing pin "a platform object (%s) writes no entry" covers that.
  • Cross-PR evidence: the same signature also dequeued an unrelated PR, refactor(objectql): ScopedContext's two bodies that hand the instance on move into its facet (#30020 S1) #31665, and is aggregated as Queue-flake anchor: src/delegated-admin-own-name-set.posture.test.ts #31726 (plugin-security: delegated-admin-own-name-set.posture.test.ts L878 loops every state through every door inside one it() on the default 5 s timeout, and timed out on main push 529e5351 #31663 family). The reading's "treated as real" is withdrawn on this evidence.
  • Gates on this head: every check-run is success (35) or skipped (15), with none in progress. check-governed-merges --pr 31702: not governed. K = 9 (< 15), and mergeable_state is clean.
  • Landing: pr_ready and automerge_enable happen in this stroke, to re-queue once. The card closes on merge (Fixes #31630).

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 Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

recycle bin: a by-id delete whose driver removes nothing leaves a bin entry snapshotting a still-live row

1 participant