Skip to content

docs(changeset): correct the objectql release note for the set-rooted predicate delete walk - #27288

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-cgmcnw
Oct 28, 2026
Merged

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

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Bill · project thread
Fixes #27201

Clause-②: yes (widening) — unchanged from the landed changeset; the review 6466053903 found the line right and this card keeps it.
Metadata-protocol: none

维护者速读

  • 改了什么:改正 @objectstack/objectql 待发布说明 .changeset/27004-predicate-delete-walk-as-set.md 里三处与代码不符的说法,并加一条测试钉住其中一项行为。引擎代码一行未动。
  • 为什么改:这份 changeset 会在 Version Packages(chore: version packages #26517)合并时变成 objectql 的 CHANGELOG;合并后复核(6466053903)判定其中三句失实,必须在发版前改正。
  • 风险与回滚:只改发版说明文字与一个测试;发布级别 patch 和两行声明保持不变。回滚即 revert 本 PR,说明退回旧的错误文本。
  • 先看:① .changeset/27004-predicate-delete-walk-as-set.md 新增的三条要点和 Clause-②: 行里改过的例子;② packages/objectql/src/engine-cascade-delete-atomic.test.ts 新增的一条用例。
  • 两分钟验证:pnpm --filter @objectstack/objectql exec vitest run src/engine-cascade-delete-atomic.test.ts,预期 20 条全过。
  • 需要你做的:无,席位落地。Check Changeset 对只改现有 changeset 的 PR 按设计保持红(非必需检查,不打 skip-changeset),改正的书面确认由席位写在本 PR 上。

What changes

The pending changeset of #27073 (card #27004) becomes the @objectstack/objectql release note when Version Packages (#26517) lands. The post-landing contract review of #27073 (comment 6466053903) found the engine right and three statements of the note false. This PR corrects those statements and changes nothing else in the note: the patch level, the title, the Clause-②: yes (widening) and Metadata-protocol: none declarations, and the Before, Now (one qualifier added) and Who-sees-it bullets stay.

  1. By-id probe filters. The note listed "the filter of every probe a by-id delete sends" as unchanged. That is gone from the Unchanged bullet. A new bullet, "A by-id delete's probes, too", states what changed. collectCascadeDeleteSet probes the records found at one depth together, per object and relation, and refuseRestrictsInsideOwnCascade probes together the members asking one relation. With two or more records this sends $in, one probe per 500 records, or on a multiple lookup the OR of each id's $contains (referenceProbeFilterOfSet). The root's own probe and each cascaded child's own walk still send that one record's filter: a batch of one is referenceProbeFilter, and a child is deleted through its own by-id delete(), which joins the set and walks one id.
  2. Two outputs the note left out. Each has its own bullet now:
    • A matched row that another matched row's cascade reaches is now removed by the predicate delete's own id-set deleteMany. The set walk enters every matched id before the first nested delete(), so the cascade skips it. The count the predicate delete returns now includes that row, and its beforeDelete/afterDelete hooks fire once, from the per-row dispatch. Before, the cascade's nested delete() removed the row ahead of deleteMany, so the count left it out, and its hooks fired a second time from that nested delete. The new bullet also notes that a flow's delete_record hands this count on as its result (crud-nodes.ts).
    • Which refusal is reported. The set walk asks each relation about every matched row before it moves on to the next relation. It also runs the own-cascade restrict check for every matched root before the walk starts. So when two relations refuse different matched rows, it reports the first relation's refusal. Before, it reported the first matched row's refusal.
  3. The example. The Clause-②: line said the row-by-row walk refused a report and its manager (a restrict self-lookup, both matched) "or not, depending on which row it met first". It refused in either order, and the line now says so. The Clause-②: yes (widening) declaration at the start of the line is unchanged.

The Now bullet's "cascade deletes (each child through its own delete, hooks and all)" now says "each child the predicate did not match". Without that qualifier it would contradict item 2.

Tests

  • New in packages/objectql/src/engine-cascade-delete-atomic.test.ts: counts a matched row another matched row cascades into, and fires its delete hooks once (objectql: since #26930 a predicate delete walks every matched row one by one; 1,001 rows cost ~1.7 s (18×), and rest's nested-object-door cap test now nears its 5 s budget in the merge queue #27004). An object holds a cascade lookup to itself, and parent and child are both matched. The test asserts the delete returns 2, only the unmatched row is left, and each matched row gets exactly one beforeDelete and one afterDelete. This is the optional pin the card offered.
  • Before writing a sentence, I measured each before/after claim in the new text once, against the old per-row predicate arm. The arm's single cascadeDeleteRelations(object, matchedIds, …) was swapped for the loop it replaced, one root per matched id, through scripts/ablation-replace.mjs. The mutation landed (blob 3dcaaed4af74 → b887e2f85237) and was restored to HEAD (git diff HEAD empty). Temporary probe cases were appended to the test file and removed afterwards; the file's blob equals HEAD.
Reading (in-memory test driver) Set walk (this tree) Old per-row walk (mutation)
Cascaded matched row: count returned 2 1
Cascaded matched row: its hooks before ×1, after ×1 before ×2, after ×2
Report + manager, restrict self-lookup, manager matched first deleted (2) DELETE_RESTRICTED
Same, report matched first deleted (2) DELETE_RESTRICTED
aa_guard restricts p2, zz_guard restricts p1 refuses p2 via aa_guard refuses p1 via zz_guard

The new pin test fails on the mutation (expected 1 to be 2) and passes on this tree.

Acceptance notes

  • Check Changeset stays red, by design. This PR edits a pending changeset and adds none, so the job reds twice: its count of added changesets is 0, and check-empty-changeset.mjs refuses a change to a changeset that exists on the merge base. This is that gate's DELIBERATE CORRECTION class. The route it prescribes: do not restore the note, do not apply skip-changeset, name the note and what changed under it on the PR, and get the correction confirmed there in writing. The note is .changeset/27004-predicate-delete-walk-as-set.md. What changed under it is the engine change fix(objectql): a predicate delete walks the rows it matches as one set, one probe per relation per 500 rows (#27004) #27073 already landed: this PR changes no code, and the note was wrong about that change from the start. The order for this edit is the card's triage ruling (6467217349), verbatim: "⛔ Edit the pending changeset text only. The engine code was judged right." The written confirmation on this PR is still owed. Check Changeset is not one of the ten required contexts.
  • The note must land before chore: version packages #26517 is merged; that is the card's deadline.
  • No engine code changes. packages/objectql/src/engine.ts is byte-identical to origin/main.

Verification

All readings are from the worktree of this branch. Head is cbc268d7 unless a line says otherwise.

Derived gate families: 59 derived, 59 run, 58 exit 0, 1 exit 1 (by design)
  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 59 families at cbc268d7 from 2 changed paths (+38/-3).
  • I ran each one and recorded its exit code. The --ran reconciliation reads: ✓ dispatch-gates --ran: 59 derived famil(ies) accounted for — 59 run, 0 NOT-MEASURED.
  • The one non-zero exit is node scripts/check-empty-changeset.mjs --base origin/main (exit 1). It names .changeset/27004-predicate-delete-walk-as-set.md as existing on the merge base and offers the DELIBERATE CORRECTION route. See Acceptance notes.
  • The first run, at 00ca77b4, had three exit-3 readings: check-plugin-teardown-shape --self-test (a shallow clone; I fetched its pinned control commit), check:lean-entry-closure (needed objectql dist; I built it) and check:dual-build-cjs-loads (needed built packages). All three exit 0 in the cbc268d7 run.
@objectstack/objectql package runs
  • pnpm --filter '@objectstack/objectql^...' build: VERDICT command-exit 0.
  • pnpm --filter @objectstack/objectql typecheck: VERDICT command-exit 0. Output: check:test-typecheck: OK … 39 file(s) / 233 error(s) / 64 pinned signature(s) held.
  • pnpm --filter @objectstack/objectql test at 00ca77b4: Test Files 477 passed (477), Tests 8900 passed (8900), VERDICT command-exit 0. The only commit after it (cbc268d7) changes two lines of the changeset, so packages/objectql is byte-identical.
  • vitest run src/engine-cascade-delete-atomic.test.ts: 20 passed. Under the old-walk mutation, the new case fails with expected 1 to be 2.
Not run locally
  • pnpm lint over the whole repo: CI runs it. I ran pnpm exec eslint --no-inline-config packages/objectql/src/engine-cascade-delete-atomic.test.ts on the one code file this PR touches: exit 0, no findings.
  • test:repo: CI runs it, and the diff touches no file of that project.
  • Full dogfood: not owed. The title carries no !, and the Clause-②: line carries no (narrowing) arm.

Generated by Claude Code

The pending objectql release note for the predicate delete walk listed a
by-id delete's probe filters as unchanged, left out the count and hook
change for a matched row another matched row cascades into and the change
in which refusal is reported, and gave an example the old walk did not
show. Correct those sentences; the level and both declaration lines stay.

Claude-Session: https://claude.ai/code/session_01Utp6CXc5hequf76uJfzrdK
Co-authored-by: Claude <noreply@anthropic.com>
…hed row cascades into

A predicate delete walks its matched rows as one set, so a matched row that
another matched row's cascade reaches is removed by the predicate's own
id-set delete: the count includes it and its delete hooks fire once. The
pending release note states this; the test holds it.

Claude-Session: https://claude.ai/code/session_01Utp6CXc5hequf76uJfzrdK
Co-authored-by: Claude <noreply@anthropic.com>
… and name the nested delete

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

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see

Coarse fallback — 0 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 2302b92b1b4abc09bbf95995909d8656c8edf4cf → packageMentionDocs.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: TIER_DEFAULT
Head-sha: cbc268d7dfb64f33707e2b03743baec47c48ddd7
Local-runs: none

Face: the Clause-②: yes (widening) declaration of the pending changeset .changeset/27004-predicate-delete-walk-as-set.md, which this PR rewrites, and the release-note correction that check-empty-changeset's DELIBERATE CORRECTION class asks to be confirmed on the PR. The order is triage 6467217349 (verbatim: "⛔ Edit the pending changeset text only. The engine code was judged right."); the defects are the post-landing review 6466053903 on PR #27073.

① Derived judgments

Each rewritten sentence, before → after, against the review's three findings:

  1. Clause-② example. Before: "the row-by-row walk refused it, or not, depending on which row it met first". After: "the row-by-row walk refused it, whichever of the two rows it met first". This matches finding 3 (a plain restrict self-lookup refused in either order). The dev's mutation table (the old per-row arm restored once) measured it as refused in both match orders, against 2 deleted on the set walk. Correct.
  2. "Unchanged" list. Before, it ended "… and the filter of every probe a by-id delete sends". After, that clause is removed and a new bullet, "A by-id delete's probes, too.", says that the cascade-set collection (collectCascadeDeleteSet, from the root's children down) and the own-cascade restrict check (refuseRestrictsInsideOwnCascade) now probe same-object records together with $in (an OR of $contains on a multiple lookup), while the root's own probe and each child's own delete keep the one-record filter. This matches finding 1; both symbols exist on origin/main (packages/objectql/src/engine.ts). Correct.
  3. The two visible changes left out. New bullet on a matched row that another matched row's cascade reaches: it is now counted, and its beforeDelete / afterDelete hooks fire once, where before it was left out of the count and fired twice. New bullet on which refusal is reported when several apply: the first refusing relation, where before it was the first refusing matched row. This matches finding 2. The count and hook half is pinned by the new case in engine-cascade-delete-atomic.test.ts, which fails on the mutation with expected 1 to be 2. The refusal-order half is measured in the mutation table (aa_guard/p2 against zz_guard/p1) and not pinned, as the card allowed ("Optional, the lane's call"). Correct.
  4. "Now" bullet qualifier. "each child the predicate did not match" is added so that the bullet does not contradict 3. Correct.

The title, the patch level, Clause-②: yes (widening), Metadata-protocol: none, and the "Before" and "Who sees it" bullets are byte-unchanged, as the card required.

② Semver level

patch stays. The landed change already took it (Metadata-protocol: none, a non-protocol widening), and this PR adds no changeset and changes no behaviour. The added test is code, but it pins already-landed behaviour; it is not a behaviour change.

③ Boundary flags

  • No engine source change: the diff is the changeset (+6/−3) and one test case (+32). No packages/spec, no governed surface.
  • Check Changeset is red by design (check-empty-changeset refuses an edit to a changeset that is on the merge base). This record is the written confirmation that its DELIBERATE CORRECTION route asks for. skip-changeset stays off. Check Changeset is not a required context.

Implemented-by: claude/project-thread-cgmcnw
Reviewed-by: session_01Utp6CXc5hequf76uJfzrdK

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 — ACCEPT · domain:server#3 · session_01Utp6CXc5hequf76uJfzrdK · head cbc268d7 · 2026-10-28T14:56Z

  • Shape: 2 files, +38/−3: .changeset/27004-predicate-delete-walk-as-set.md (text) and one test case in packages/objectql/src/engine-cascade-delete-atomic.test.ts. No engine source, no packages/spec, no governed surface. Matches claim 6469681343's surface; the test is the card's optional pin.
  • Review of record: contract review 6471970627 (TIER_DEFAULT, the declaration face) is a PASS on this head. It judges each rewritten sentence before → after, and it is the written confirmation that check-empty-changeset's DELIBERATE CORRECTION route asks for.
  • Gates: every check-run on cbc268d7 is success or skipped except Check Changeset. The required set is green, and mergeable_state is clean.
  • The one red, carried into the queue on purpose: all three conditions hold:
    • ① The gate's source (scripts/check-empty-changeset.mjs, the DELIBERATE CORRECTION class) says this red is by design: "this gate stays red either way". skip-changeset is not applied.
    • ② The job is in pr-automation.yml, whose only trigger is pull_request, so it does not run on merge_group.
    • ③ This comment and the review record name the gate and its cause. Check Changeset is not one of the ten required contexts.
  • Local runs (dev): all 59 derived gate families were run on the head: 58 exit 0, and 1 exit 1 (the same gate). The objectql suite is 477 files / 8900 tests green. The new case fails on the mutation that restores the old per-row walk.
  • Accepted deviations: eslint was run on the one changed file, not repo-wide (left to CI). One commit was fetched into the shallow clone for a self-test's control.
  • Deadline: this lands before Version Packages chore: version packages #26517 is merged, as triage 6467217349 requires.
  • Landing: ready, then auto-merge through the queue.

Generated by Claude Code

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/s tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

objectql changeset: #27004's pending release note misstates the set-rooted delete walk (probe filters, cascaded-row count, example)

2 participants