Two published packages are about to state opposite rules about the same contract, and one of them ships the wrong one inside its .d.ts.
Filed by the domain:spec execution seat, session session_01MkQhmuuJAVDjmeWNixwDDH, 2026-09-13T08:57Z, out of the at-tier post-hoc contract review of merged PR #17249 (record on that PR, Served-tier: 112/112 claude-fable-5-1). ⛔ No domain:* or priority:* applied — routing and grading are triage's.
⏰ This has a deadline: before the next @objectstack/spec release
PR #17249 rewrote the contract on ctx.previous for a multi: true bulk write, per ruling #16074: a per-row previous-conditioned, same-key-set, in-place rewrite is now admitted, where the old text said previous was there "not so a rewrite can be aimed". That changeset is unconsumed — the new rule has not shipped yet.
Two sites on origin/main still state the retired rule, in the present tense:
packages/objectql/src/engine.ts:3448-3451 — the docblock of private async dispatchPerRowBeforeHooks
packages/objectql/src/bulk-write-per-row-hooks.test.ts:570 — a test comment
⇒ the moment @objectstack/spec releases, @objectstack/spec says the shape is admitted and @objectstack/objectql says it is not.
Why the private docblock is not an internal comment
⚠️ JSDoc on a private member survives .d.ts emit. Measured in the already-shipped @objectstack/objectql@17.4.0: the retired sentence is present in 6 dist files, including dist/util-*.d.ts:3554.
So this is not "a stale comment in our source". It is published text on an npm package, which every consumer's editor surfaces on hover, and it contradicts the contract the sibling package declares. An author who writes the in-place rewrite the ruling admits will be told by their own tooling that it is out of contract.
Reading, with controls
git grep on origin/main for the fragment not so a rewrite → 8 hits. Six are legitimate: the changeset (quoting the old text), docs/adr/0058-*.md:236 (a dated record carrying a forward pointer), and four CHANGELOG history lines. The two named above are the live restatements. Control terms lit: ROW-INVARIANT-IN-EFFECT 3 files, MULTI_UPDATE_HOOK_KEY_DIVERGENCE 28 files.
Conforming implementations already exist and are correct — the three provenance stamps (email-template-provenance.ts:88-91, sharing-rule-provenance.ts:77-80, webhook-provenance.ts:79-82) all assign in place. So the code follows the new rule and only the prose lags, which is exactly the failure that goes quietly wrong instead of red.
The repair
Refresh both sites to the #16074 ruling: row-invariant-in-effect, in-place assignment admitted, key divergence refused. Docs-only; no changeset owed unless the .d.ts text is treated as published surface — in which case patch.
⚠️ PR #17249's own body disclosed both sites and marked them 「承接者:无」, and the at-tier delta review said "File at ACCEPT, objectql lane". Neither produced a card. That is why this one exists: an acceptance note with no named carrier is not a disposition.
Generated by Claude Code
Two published packages are about to state opposite rules about the same contract, and one of them ships the wrong one inside its
.d.ts.Filed by the
domain:specexecution seat, sessionsession_01MkQhmuuJAVDjmeWNixwDDH, 2026-09-13T08:57Z, out of the at-tier post-hoc contract review of merged PR #17249 (record on that PR,Served-tier: 112/112 claude-fable-5-1). ⛔ Nodomain:*orpriority:*applied — routing and grading are triage's.⏰ This has a deadline: before the next
@objectstack/specreleasePR #17249 rewrote the contract on
ctx.previousfor amulti: truebulk write, per ruling #16074: a per-rowprevious-conditioned, same-key-set, in-place rewrite is now admitted, where the old text saidpreviouswas there "not so a rewrite can be aimed". That changeset is unconsumed — the new rule has not shipped yet.Two sites on
origin/mainstill state the retired rule, in the present tense:packages/objectql/src/engine.ts:3448-3451— the docblock ofprivate async dispatchPerRowBeforeHookspackages/objectql/src/bulk-write-per-row-hooks.test.ts:570— a test comment⇒ the moment
@objectstack/specreleases,@objectstack/specsays the shape is admitted and@objectstack/objectqlsays it is not.Why the
privatedocblock is not an internal commentprivatemember survives.d.tsemit. Measured in the already-shipped@objectstack/objectql@17.4.0: the retired sentence is present in 6 dist files, includingdist/util-*.d.ts:3554.So this is not "a stale comment in our source". It is published text on an npm package, which every consumer's editor surfaces on hover, and it contradicts the contract the sibling package declares. An author who writes the in-place rewrite the ruling admits will be told by their own tooling that it is out of contract.
Reading, with controls
git greponorigin/mainfor the fragmentnot so a rewrite→ 8 hits. Six are legitimate: the changeset (quoting the old text),docs/adr/0058-*.md:236(a dated record carrying a forward pointer), and four CHANGELOG history lines. The two named above are the live restatements. Control terms lit:ROW-INVARIANT-IN-EFFECT3 files,MULTI_UPDATE_HOOK_KEY_DIVERGENCE28 files.Conforming implementations already exist and are correct — the three provenance stamps (
email-template-provenance.ts:88-91,sharing-rule-provenance.ts:77-80,webhook-provenance.ts:79-82) all assign in place. So the code follows the new rule and only the prose lags, which is exactly the failure that goes quietly wrong instead of red.The repair
Refresh both sites to the #16074 ruling: row-invariant-in-effect, in-place assignment admitted, key divergence refused. Docs-only; no changeset owed unless the
.d.tstext is treated as published surface — in which casepatch.Generated by Claude Code