Skip to content

fix(lint): relationship/delete-behavior suggestion drops set_null on master_detail - #13668

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-11668-delete-behavior-suggestion-text
Aug 31, 2026
Merged

fix(lint): relationship/delete-behavior suggestion drops set_null on master_detail#13668
os-project-manager merged 1 commit into
mainfrom
claude/issue-11668-delete-behavior-suggestion-text

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#11668

What

lintDataModel's relationship/delete-behavior suggestion (packages/lint/src/data-model-rules.ts) told an author an undeclared master_detail.deleteBehavior could be cascade, restrict, or set_null. Since #9689 (PR #11406, maintainer ruling 2026-08-19), an authored deleteBehavior: 'set_null' on a master_detail field is a named parse-time rejection (packages/spec/src/data/field.zod.ts) — a detail row cannot outlive its master, so the engine resolves every value except restrict to cascade on this type. Following this suggestion's own enumeration literally walked an author into that rejection at publish time.

Before:

master_detail "${obj.name}.${fieldName}" → ${parent} should declare deleteBehavior (cascade/restrict/set_null)

After:

master_detail "${obj.name}.${fieldName}" → ${parent} should declare deleteBehavior (cascade/restrict — set_null is not honored on master_detail; use a lookup field if children must survive the parent)

The corrected enumeration (cascade/restrict) is not my own choice — it is read off two existing authorities that already agree:

The message keeps the issue's suggested "outcome-naming courtesy": it still mentionsset_null, but only to say it is not honored on this type and to point at lookup as the alternative — it no longer offers it as a declarable choice.

⛔ Not touched, per the card's Zone 1: the rule's logic (still consumes RAW authored objects — def.deleteBehavior === undefined still means "not authored"; #9689's idempotent-materialization change never reaches this rule), and the fix payload (deleteBehavior: 'cascade'), which was already correct.

Tests

Generated by Claude Code


Generated by Claude Code

…master_detail
The `relationship/delete-behavior` suggestion in `lintDataModel` told an
author an undeclared `master_detail.deleteBehavior` could be `cascade`,
`restrict`, or `set_null`. Since #9689 (PR #11406, maintainer ruling
2026-08-19), an authored `deleteBehavior: 'set_null'` on a `master_detail`
field is a named parse-time rejection, so following the suggestion literally
walked an author into a publish-time error.
The message now enumerates only the two values FieldSchema actually accepts
on a master_detail (cascade/restrict — matching object.form.ts's
master_detail deleteBehavior options), and keeps the outcome-naming courtesy
of the parse-time rejection message: it still names set_null to say plainly
it is not honored on this type, and points to `lookup` for the case where
children must survive the parent. The `fix` payload was already correct and
is unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to listnot 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
  • 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 — 5 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 967402a12d6c7f43361df68357e29bba93465e47packageMentionDocs.

Which tree this was computed on

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

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

@claude

claudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

⏸ Evicted in the same batch as #13659deliberately holding the re-queue for a few minutes, and saying why

domain:devx PM seat (#6023), session session_01Pk26oZ12t5N1hwGW1m1MgC. Stating the hold rather than leaving it silent: a wait that is not written down does not exist.

What happened

Queue build 33363096644 went red and evicted both PRs in its batch — this one (enqueued 06:15:02Z) and #13659 (06:09:43Z). Failure: Test Core (1/6)@objectstack/clitest/run-dev-unbuilt-workspace.e2e.test.ts, two assertions. Full diagnosis and mechanism: #13683.

⚠️ Why I am not simply re-queueing this one immediately

I cannot honestly claim "not this PR's" here, the way I can for #13659.

That does not make it the cause. Its change is two added assertions about a lint message string, with no relationship to run-dev.js, oclif resolution, or packages/cli/src/utils/format.ts. But the failing fixture works by mutating a shared path inside packages/cli/node_modules (renaming @objectstack/spec/dist aside so an import fails on purpose), and a fixture like that is perturbable by changes to what else runs in the same package's test process. ⚠️Available mechanism ≠ demonstrated one — I am not asserting it.

The hold, and what it buys

#13659 is re-queued alone. If I re-queue this one now, the two likely batch together again and a green or red tells me nothing about which PR it belonged to.

Sequencing them costs nothing extra — the same two queue attempts, run in an order that discriminates:

#13659 alonereadingthis PR's next step
passesthe failure is not #13659's, and the batch is the remaining variablere-queue this one alone; a red then points at this PR and is mine to root-cause
fails again, same signaturereproducible and independent of this PR entirelyre-queue this one normally; the defect is packages/cli's, per #13683

⛔ Not a flake dismissal, ⛔ not an empty commit, ⛔ no test skipped or quarantined, ⛔ no re-run spent yet on this PR — its single allowance is intact.

The PR itself is unchanged and green: 33 checks, 0 failed, mergeable_state: clean, ACCEPTed on #11668. Nothing here is a defect in the change.

Re-queueing as soon as #13659 resolves.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

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

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

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

  • Temporal Conformance (live PG + MySQL) — 失败步骤: Run driver-sql suite against both live servers

    × §3 keeps sub-second resolution, so same-second updates are ordered 1026ms
    FAIL src/sql-driver-11224-update-stamp-precision.test.ts > #11224 — the UPDATE door stamps at the audit column's precision (live mysql) > §3 keeps sub-second resolution, so same-second updates are or
    ↳ 失败原因: AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000
    

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

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

  • src/sql-driver-11224-update-stamp-precision.test.ts — 24h 窗口内只有本 PR 撞到过,暂不汇总(再有一个不同 PR 撞到就会自动开汇总 issue)。
  • ⚠️ 24h 评论账本没读完(超过 5 页仍未读到窗口尽头),所以上面的「不同 PR 数」是下界,不是全量。

历史信号:

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

分诊清单:

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

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

@claude

claudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

✅ The sequenced experiment resolved — and this PR's own red is a validity guard, not a behaviour change. Re-queueing once.

domain:devx PM seat (#6023), session session_01Pk26oZ12t5N1hwGW1m1MgC. Reporting the result of the hold I declared above.

1. The experiment: #13659 alone PASSED ⇒ the run-dev failure was not attributable to this PR

Re-queued #13659 by itself at 06:30:08Z. Its build gh-readonly-queue/main/pr-13659-94decf8a… (06:30:26Z) completed success. The run-dev-unbuilt-workspace.e2e.test.ts failure did not reproduce.

⚠️And my batching premise was wrong — correcting it rather than quietly dropping it. I assumed the two PRs shared one batch. The queue built them on separate branches:

runbranchoutcome
33363096644…/pr-13659-713f83f5…run-dev-unbuilt-workspace
33364345029…/pr-13668-713f83f5…a different test entirely
…/pr-13659-94decf8a…success

⇒ The "grouped-queue neighbour" hypothesis I raised on #13683 is weakened, not confirmed: the two PRs' failures came from separate builds and were different tests. #13683 updated accordingly.

2. This PR's own red: Temporal Conformance (live PG + MySQL)

AssertionError: this run took over a second, so second-precision stamps could have differed too:
expected 1006 to be less than 1000

That assertion is not about the product. It is the test's own validity precondition. Read at packages/drivers/driver-sql/src/sql-driver-11224-update-stamp-precision.test.ts:384-391:

// Monotone regardless (the invariant), and — the point — the run spans// less than the full second a truncated stamp would need to distinguish// any two of these at all.constspan=stamps[stamps.length-1]-stamps[0];expect(span,'this run took over a second, so …').toBeLessThan(1_000);

The test needs the whole run to fit inside one second, so that a truncated second-precision stamp could not have distinguished the updates — which is what makes the following distinctness assertion mean anything. At span = 1006 the test detected that its own premise failed by 6 ms. Monotonicity — the actual invariant, asserted on the line above — held.

The experiment was invalidated; the product was not falsified.

⚠️This is a clean counterexample to the triage comment's own heuristic (「断言(AssertionError: …)才指向真实的行为改变」). That heuristic is sound in general, and it is what I used to refuse a flake reading on #13659. Here it misfires, because the assertion is a timing measurement. The discriminator is not AssertionError vs timeout — it is what the assertion measures.

3. Why it is not this PR's

This PR's diff: packages/lint/src/data-model-rules.ts (one message string), packages/cli/test/data-model-rules.test.ts (two assertions), one changeset. The failing test drives live PostgreSQL and MySQL through packages/drivers/driver-sql. No mechanism connects them.

⭐ And it is known ground: #11572 (closed, domain:engine) is this exact guard — "a probabilistic guard that reds with CORRECT code on a fast runner". Today's is its mirror image (too slow, not too fast), which that card did not cover. Filed as a fresh finding rather than reopening another lane's closed card.

4. Re-queueing — the one allowance, now spent

ROUNDS = 6, MIN_GAP_MS = 5 ⇒ ~25 ms of deliberate spacing plus 6 UPDATE round-trips against two live servers, inside a hard 1000 ms wall. Ordinarily ample; under runner contention, 1006 ms happens. This is the case the rules reserve a re-run for.

⛔ Not skipping, disabling or quarantining the test. ⛔ No empty commit. ⛔ Not touching driver-sql from this PR — that would widen a lint-message change into another lane's package. If it reds again with this signature, I stop re-queueing and it belongs to the new finding, not to this PR.

The PR itself remains green and unchanged: 33 checks, 0 failed, ACCEPTed on #11668.


Generated by Claude Code

@os-project-manager
os-project-manager added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit 9057811Aug 31, 2026
34 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-11668-delete-behavior-suggestion-text branch August 31, 2026 07:12
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/steststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lint relationship/delete-behavior suggestion still names set_null as declarable on a master_detail — a spelling #9689 makes a parse-time rejection

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
fix(lint): relationship/delete-behavior suggestion drops set_null on master_detail by os-project-manager · Pull Request #13668 · objectstack-ai/objectstack · GitHub
Skip to content

fix(lint): relationship/delete-behavior suggestion drops set_null on master_detail - #13668

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-11668-delete-behavior-suggestion-text
Aug 31, 2026
Merged

fix(lint): relationship/delete-behavior suggestion drops set_null on master_detail#13668
os-project-manager merged 1 commit into
mainfrom
claude/issue-11668-delete-behavior-suggestion-text

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#11668

What

lintDataModel's relationship/delete-behavior suggestion (packages/lint/src/data-model-rules.ts) told an author an undeclared master_detail.deleteBehavior could be cascade, restrict, or set_null. Since #9689 (PR #11406, maintainer ruling 2026-08-19), an authored deleteBehavior: 'set_null' on a master_detail field is a named parse-time rejection (packages/spec/src/data/field.zod.ts) — a detail row cannot outlive its master, so the engine resolves every value except restrict to cascade on this type. Following this suggestion's own enumeration literally walked an author into that rejection at publish time.

Before:

master_detail "${obj.name}.${fieldName}" → ${parent} should declare deleteBehavior (cascade/restrict/set_null)

After:

master_detail "${obj.name}.${fieldName}" → ${parent} should declare deleteBehavior (cascade/restrict — set_null is not honored on master_detail; use a lookup field if children must survive the parent)

The corrected enumeration (cascade/restrict) is not my own choice — it is read off two existing authorities that already agree:

The message keeps the issue's suggested "outcome-naming courtesy": it still mentionsset_null, but only to say it is not honored on this type and to point at lookup as the alternative — it no longer offers it as a declarable choice.

⛔ Not touched, per the card's Zone 1: the rule's logic (still consumes RAW authored objects — def.deleteBehavior === undefined still means "not authored"; #9689's idempotent-materialization change never reaches this rule), and the fix payload (deleteBehavior: 'cascade'), which was already correct.

Tests

Generated by Claude Code


Generated by Claude Code

…master_detail
The `relationship/delete-behavior` suggestion in `lintDataModel` told an
author an undeclared `master_detail.deleteBehavior` could be `cascade`,
`restrict`, or `set_null`. Since #9689 (PR #11406, maintainer ruling
2026-08-19), an authored `deleteBehavior: 'set_null'` on a `master_detail`
field is a named parse-time rejection, so following the suggestion literally
walked an author into a publish-time error.
The message now enumerates only the two values FieldSchema actually accepts
on a master_detail (cascade/restrict — matching object.form.ts's
master_detail deleteBehavior options), and keeps the outcome-naming courtesy
of the parse-time rejection message: it still names set_null to say plainly
it is not honored on this type, and points to `lookup` for the case where
children must survive the parent. The `fix` payload was already correct and
is unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to listnot 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
  • 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 — 5 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 967402a12d6c7f43361df68357e29bba93465e47packageMentionDocs.

Which tree this was computed on

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

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

@claude

claudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

⏸ Evicted in the same batch as #13659deliberately holding the re-queue for a few minutes, and saying why

domain:devx PM seat (#6023), session session_01Pk26oZ12t5N1hwGW1m1MgC. Stating the hold rather than leaving it silent: a wait that is not written down does not exist.

What happened

Queue build 33363096644 went red and evicted both PRs in its batch — this one (enqueued 06:15:02Z) and #13659 (06:09:43Z). Failure: Test Core (1/6)@objectstack/clitest/run-dev-unbuilt-workspace.e2e.test.ts, two assertions. Full diagnosis and mechanism: #13683.

⚠️ Why I am not simply re-queueing this one immediately

I cannot honestly claim "not this PR's" here, the way I can for #13659.

That does not make it the cause. Its change is two added assertions about a lint message string, with no relationship to run-dev.js, oclif resolution, or packages/cli/src/utils/format.ts. But the failing fixture works by mutating a shared path inside packages/cli/node_modules (renaming @objectstack/spec/dist aside so an import fails on purpose), and a fixture like that is perturbable by changes to what else runs in the same package's test process. ⚠️Available mechanism ≠ demonstrated one — I am not asserting it.

The hold, and what it buys

#13659 is re-queued alone. If I re-queue this one now, the two likely batch together again and a green or red tells me nothing about which PR it belonged to.

Sequencing them costs nothing extra — the same two queue attempts, run in an order that discriminates:

#13659 alonereadingthis PR's next step
passesthe failure is not #13659's, and the batch is the remaining variablere-queue this one alone; a red then points at this PR and is mine to root-cause
fails again, same signaturereproducible and independent of this PR entirelyre-queue this one normally; the defect is packages/cli's, per #13683

⛔ Not a flake dismissal, ⛔ not an empty commit, ⛔ no test skipped or quarantined, ⛔ no re-run spent yet on this PR — its single allowance is intact.

The PR itself is unchanged and green: 33 checks, 0 failed, mergeable_state: clean, ACCEPTed on #11668. Nothing here is a defect in the change.

Re-queueing as soon as #13659 resolves.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

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

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

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

  • Temporal Conformance (live PG + MySQL) — 失败步骤: Run driver-sql suite against both live servers

    × §3 keeps sub-second resolution, so same-second updates are ordered 1026ms
    FAIL src/sql-driver-11224-update-stamp-precision.test.ts > #11224 — the UPDATE door stamps at the audit column's precision (live mysql) > §3 keeps sub-second resolution, so same-second updates are or
    ↳ 失败原因: AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000
    

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

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

  • src/sql-driver-11224-update-stamp-precision.test.ts — 24h 窗口内只有本 PR 撞到过,暂不汇总(再有一个不同 PR 撞到就会自动开汇总 issue)。
  • ⚠️ 24h 评论账本没读完(超过 5 页仍未读到窗口尽头),所以上面的「不同 PR 数」是下界,不是全量。

历史信号:

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

分诊清单:

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

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

@claude

claudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

✅ The sequenced experiment resolved — and this PR's own red is a validity guard, not a behaviour change. Re-queueing once.

domain:devx PM seat (#6023), session session_01Pk26oZ12t5N1hwGW1m1MgC. Reporting the result of the hold I declared above.

1. The experiment: #13659 alone PASSED ⇒ the run-dev failure was not attributable to this PR

Re-queued #13659 by itself at 06:30:08Z. Its build gh-readonly-queue/main/pr-13659-94decf8a… (06:30:26Z) completed success. The run-dev-unbuilt-workspace.e2e.test.ts failure did not reproduce.

⚠️And my batching premise was wrong — correcting it rather than quietly dropping it. I assumed the two PRs shared one batch. The queue built them on separate branches:

runbranchoutcome
33363096644…/pr-13659-713f83f5…run-dev-unbuilt-workspace
33364345029…/pr-13668-713f83f5…a different test entirely
…/pr-13659-94decf8a…success

⇒ The "grouped-queue neighbour" hypothesis I raised on #13683 is weakened, not confirmed: the two PRs' failures came from separate builds and were different tests. #13683 updated accordingly.

2. This PR's own red: Temporal Conformance (live PG + MySQL)

AssertionError: this run took over a second, so second-precision stamps could have differed too:
expected 1006 to be less than 1000

That assertion is not about the product. It is the test's own validity precondition. Read at packages/drivers/driver-sql/src/sql-driver-11224-update-stamp-precision.test.ts:384-391:

// Monotone regardless (the invariant), and — the point — the run spans// less than the full second a truncated stamp would need to distinguish// any two of these at all.constspan=stamps[stamps.length-1]-stamps[0];expect(span,'this run took over a second, so …').toBeLessThan(1_000);

The test needs the whole run to fit inside one second, so that a truncated second-precision stamp could not have distinguished the updates — which is what makes the following distinctness assertion mean anything. At span = 1006 the test detected that its own premise failed by 6 ms. Monotonicity — the actual invariant, asserted on the line above — held.

The experiment was invalidated; the product was not falsified.

⚠️This is a clean counterexample to the triage comment's own heuristic (「断言(AssertionError: …)才指向真实的行为改变」). That heuristic is sound in general, and it is what I used to refuse a flake reading on #13659. Here it misfires, because the assertion is a timing measurement. The discriminator is not AssertionError vs timeout — it is what the assertion measures.

3. Why it is not this PR's

This PR's diff: packages/lint/src/data-model-rules.ts (one message string), packages/cli/test/data-model-rules.test.ts (two assertions), one changeset. The failing test drives live PostgreSQL and MySQL through packages/drivers/driver-sql. No mechanism connects them.

⭐ And it is known ground: #11572 (closed, domain:engine) is this exact guard — "a probabilistic guard that reds with CORRECT code on a fast runner". Today's is its mirror image (too slow, not too fast), which that card did not cover. Filed as a fresh finding rather than reopening another lane's closed card.

4. Re-queueing — the one allowance, now spent

ROUNDS = 6, MIN_GAP_MS = 5 ⇒ ~25 ms of deliberate spacing plus 6 UPDATE round-trips against two live servers, inside a hard 1000 ms wall. Ordinarily ample; under runner contention, 1006 ms happens. This is the case the rules reserve a re-run for.

⛔ Not skipping, disabling or quarantining the test. ⛔ No empty commit. ⛔ Not touching driver-sql from this PR — that would widen a lint-message change into another lane's package. If it reds again with this signature, I stop re-queueing and it belongs to the new finding, not to this PR.

The PR itself remains green and unchanged: 33 checks, 0 failed, ACCEPTed on #11668.


Generated by Claude Code

@os-project-manager
os-project-manager added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit 9057811Aug 31, 2026
34 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-11668-delete-behavior-suggestion-text branch August 31, 2026 07:12
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/steststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lint relationship/delete-behavior suggestion still names set_null as declarable on a master_detail — a spelling #9689 makes a parse-time rejection

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(lint): relationship/delete-behavior suggestion drops set_null on master_detail by os-project-manager · Pull Request #13668 · objectstack-ai/objectstack · GitHub
Skip to content

fix(lint): relationship/delete-behavior suggestion drops set_null on master_detail - #13668

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-11668-delete-behavior-suggestion-text
Aug 31, 2026
Merged

fix(lint): relationship/delete-behavior suggestion drops set_null on master_detail#13668
os-project-manager merged 1 commit into
mainfrom
claude/issue-11668-delete-behavior-suggestion-text

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#11668

What

lintDataModel's relationship/delete-behavior suggestion (packages/lint/src/data-model-rules.ts) told an author an undeclared master_detail.deleteBehavior could be cascade, restrict, or set_null. Since #9689 (PR #11406, maintainer ruling 2026-08-19), an authored deleteBehavior: 'set_null' on a master_detail field is a named parse-time rejection (packages/spec/src/data/field.zod.ts) — a detail row cannot outlive its master, so the engine resolves every value except restrict to cascade on this type. Following this suggestion's own enumeration literally walked an author into that rejection at publish time.

Before:

master_detail "${obj.name}.${fieldName}" → ${parent} should declare deleteBehavior (cascade/restrict/set_null)

After:

master_detail "${obj.name}.${fieldName}" → ${parent} should declare deleteBehavior (cascade/restrict — set_null is not honored on master_detail; use a lookup field if children must survive the parent)

The corrected enumeration (cascade/restrict) is not my own choice — it is read off two existing authorities that already agree:

The message keeps the issue's suggested "outcome-naming courtesy": it still mentionsset_null, but only to say it is not honored on this type and to point at lookup as the alternative — it no longer offers it as a declarable choice.

⛔ Not touched, per the card's Zone 1: the rule's logic (still consumes RAW authored objects — def.deleteBehavior === undefined still means "not authored"; #9689's idempotent-materialization change never reaches this rule), and the fix payload (deleteBehavior: 'cascade'), which was already correct.

Tests

Generated by Claude Code


Generated by Claude Code

…master_detail
The `relationship/delete-behavior` suggestion in `lintDataModel` told an
author an undeclared `master_detail.deleteBehavior` could be `cascade`,
`restrict`, or `set_null`. Since #9689 (PR #11406, maintainer ruling
2026-08-19), an authored `deleteBehavior: 'set_null'` on a `master_detail`
field is a named parse-time rejection, so following the suggestion literally
walked an author into a publish-time error.
The message now enumerates only the two values FieldSchema actually accepts
on a master_detail (cascade/restrict — matching object.form.ts's
master_detail deleteBehavior options), and keeps the outcome-naming courtesy
of the parse-time rejection message: it still names set_null to say plainly
it is not honored on this type, and points to `lookup` for the case where
children must survive the parent. The `fix` payload was already correct and
is unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to listnot 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
  • 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 — 5 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 967402a12d6c7f43361df68357e29bba93465e47packageMentionDocs.

Which tree this was computed on

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

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

@claude

claudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

⏸ Evicted in the same batch as #13659deliberately holding the re-queue for a few minutes, and saying why

domain:devx PM seat (#6023), session session_01Pk26oZ12t5N1hwGW1m1MgC. Stating the hold rather than leaving it silent: a wait that is not written down does not exist.

What happened

Queue build 33363096644 went red and evicted both PRs in its batch — this one (enqueued 06:15:02Z) and #13659 (06:09:43Z). Failure: Test Core (1/6)@objectstack/clitest/run-dev-unbuilt-workspace.e2e.test.ts, two assertions. Full diagnosis and mechanism: #13683.

⚠️ Why I am not simply re-queueing this one immediately

I cannot honestly claim "not this PR's" here, the way I can for #13659.

That does not make it the cause. Its change is two added assertions about a lint message string, with no relationship to run-dev.js, oclif resolution, or packages/cli/src/utils/format.ts. But the failing fixture works by mutating a shared path inside packages/cli/node_modules (renaming @objectstack/spec/dist aside so an import fails on purpose), and a fixture like that is perturbable by changes to what else runs in the same package's test process. ⚠️Available mechanism ≠ demonstrated one — I am not asserting it.

The hold, and what it buys

#13659 is re-queued alone. If I re-queue this one now, the two likely batch together again and a green or red tells me nothing about which PR it belonged to.

Sequencing them costs nothing extra — the same two queue attempts, run in an order that discriminates:

#13659 alonereadingthis PR's next step
passesthe failure is not #13659's, and the batch is the remaining variablere-queue this one alone; a red then points at this PR and is mine to root-cause
fails again, same signaturereproducible and independent of this PR entirelyre-queue this one normally; the defect is packages/cli's, per #13683

⛔ Not a flake dismissal, ⛔ not an empty commit, ⛔ no test skipped or quarantined, ⛔ no re-run spent yet on this PR — its single allowance is intact.

The PR itself is unchanged and green: 33 checks, 0 failed, mergeable_state: clean, ACCEPTed on #11668. Nothing here is a defect in the change.

Re-queueing as soon as #13659 resolves.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

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

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

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

  • Temporal Conformance (live PG + MySQL) — 失败步骤: Run driver-sql suite against both live servers

    × §3 keeps sub-second resolution, so same-second updates are ordered 1026ms
    FAIL src/sql-driver-11224-update-stamp-precision.test.ts > #11224 — the UPDATE door stamps at the audit column's precision (live mysql) > §3 keeps sub-second resolution, so same-second updates are or
    ↳ 失败原因: AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000
    

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

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

  • src/sql-driver-11224-update-stamp-precision.test.ts — 24h 窗口内只有本 PR 撞到过,暂不汇总(再有一个不同 PR 撞到就会自动开汇总 issue)。
  • ⚠️ 24h 评论账本没读完(超过 5 页仍未读到窗口尽头),所以上面的「不同 PR 数」是下界,不是全量。

历史信号:

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

分诊清单:

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

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

@claude

claudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

✅ The sequenced experiment resolved — and this PR's own red is a validity guard, not a behaviour change. Re-queueing once.

domain:devx PM seat (#6023), session session_01Pk26oZ12t5N1hwGW1m1MgC. Reporting the result of the hold I declared above.

1. The experiment: #13659 alone PASSED ⇒ the run-dev failure was not attributable to this PR

Re-queued #13659 by itself at 06:30:08Z. Its build gh-readonly-queue/main/pr-13659-94decf8a… (06:30:26Z) completed success. The run-dev-unbuilt-workspace.e2e.test.ts failure did not reproduce.

⚠️And my batching premise was wrong — correcting it rather than quietly dropping it. I assumed the two PRs shared one batch. The queue built them on separate branches:

runbranchoutcome
33363096644…/pr-13659-713f83f5…run-dev-unbuilt-workspace
33364345029…/pr-13668-713f83f5…a different test entirely
…/pr-13659-94decf8a…success

⇒ The "grouped-queue neighbour" hypothesis I raised on #13683 is weakened, not confirmed: the two PRs' failures came from separate builds and were different tests. #13683 updated accordingly.

2. This PR's own red: Temporal Conformance (live PG + MySQL)

AssertionError: this run took over a second, so second-precision stamps could have differed too:
expected 1006 to be less than 1000

That assertion is not about the product. It is the test's own validity precondition. Read at packages/drivers/driver-sql/src/sql-driver-11224-update-stamp-precision.test.ts:384-391:

// Monotone regardless (the invariant), and — the point — the run spans// less than the full second a truncated stamp would need to distinguish// any two of these at all.constspan=stamps[stamps.length-1]-stamps[0];expect(span,'this run took over a second, so …').toBeLessThan(1_000);

The test needs the whole run to fit inside one second, so that a truncated second-precision stamp could not have distinguished the updates — which is what makes the following distinctness assertion mean anything. At span = 1006 the test detected that its own premise failed by 6 ms. Monotonicity — the actual invariant, asserted on the line above — held.

The experiment was invalidated; the product was not falsified.

⚠️This is a clean counterexample to the triage comment's own heuristic (「断言(AssertionError: …)才指向真实的行为改变」). That heuristic is sound in general, and it is what I used to refuse a flake reading on #13659. Here it misfires, because the assertion is a timing measurement. The discriminator is not AssertionError vs timeout — it is what the assertion measures.

3. Why it is not this PR's

This PR's diff: packages/lint/src/data-model-rules.ts (one message string), packages/cli/test/data-model-rules.test.ts (two assertions), one changeset. The failing test drives live PostgreSQL and MySQL through packages/drivers/driver-sql. No mechanism connects them.

⭐ And it is known ground: #11572 (closed, domain:engine) is this exact guard — "a probabilistic guard that reds with CORRECT code on a fast runner". Today's is its mirror image (too slow, not too fast), which that card did not cover. Filed as a fresh finding rather than reopening another lane's closed card.

4. Re-queueing — the one allowance, now spent

ROUNDS = 6, MIN_GAP_MS = 5 ⇒ ~25 ms of deliberate spacing plus 6 UPDATE round-trips against two live servers, inside a hard 1000 ms wall. Ordinarily ample; under runner contention, 1006 ms happens. This is the case the rules reserve a re-run for.

⛔ Not skipping, disabling or quarantining the test. ⛔ No empty commit. ⛔ Not touching driver-sql from this PR — that would widen a lint-message change into another lane's package. If it reds again with this signature, I stop re-queueing and it belongs to the new finding, not to this PR.

The PR itself remains green and unchanged: 33 checks, 0 failed, ACCEPTed on #11668.


Generated by Claude Code

@os-project-manager
os-project-manager added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit 9057811Aug 31, 2026
34 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-11668-delete-behavior-suggestion-text branch August 31, 2026 07:12
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/steststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lint relationship/delete-behavior suggestion still names set_null as declarable on a master_detail — a spelling #9689 makes a parse-time rejection

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(lint): relationship/delete-behavior suggestion drops set_null on master_detail by os-project-manager · Pull Request #13668 · objectstack-ai/objectstack · GitHub
Skip to content

fix(lint): relationship/delete-behavior suggestion drops set_null on master_detail - #13668

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-11668-delete-behavior-suggestion-text
Aug 31, 2026
Merged

fix(lint): relationship/delete-behavior suggestion drops set_null on master_detail#13668
os-project-manager merged 1 commit into
mainfrom
claude/issue-11668-delete-behavior-suggestion-text

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#11668

What

lintDataModel's relationship/delete-behavior suggestion (packages/lint/src/data-model-rules.ts) told an author an undeclared master_detail.deleteBehavior could be cascade, restrict, or set_null. Since #9689 (PR #11406, maintainer ruling 2026-08-19), an authored deleteBehavior: 'set_null' on a master_detail field is a named parse-time rejection (packages/spec/src/data/field.zod.ts) — a detail row cannot outlive its master, so the engine resolves every value except restrict to cascade on this type. Following this suggestion's own enumeration literally walked an author into that rejection at publish time.

Before:

master_detail "${obj.name}.${fieldName}" → ${parent} should declare deleteBehavior (cascade/restrict/set_null)

After:

master_detail "${obj.name}.${fieldName}" → ${parent} should declare deleteBehavior (cascade/restrict — set_null is not honored on master_detail; use a lookup field if children must survive the parent)

The corrected enumeration (cascade/restrict) is not my own choice — it is read off two existing authorities that already agree:

The message keeps the issue's suggested "outcome-naming courtesy": it still mentionsset_null, but only to say it is not honored on this type and to point at lookup as the alternative — it no longer offers it as a declarable choice.

⛔ Not touched, per the card's Zone 1: the rule's logic (still consumes RAW authored objects — def.deleteBehavior === undefined still means "not authored"; #9689's idempotent-materialization change never reaches this rule), and the fix payload (deleteBehavior: 'cascade'), which was already correct.

Tests

Generated by Claude Code


Generated by Claude Code

…master_detail
The `relationship/delete-behavior` suggestion in `lintDataModel` told an
author an undeclared `master_detail.deleteBehavior` could be `cascade`,
`restrict`, or `set_null`. Since #9689 (PR #11406, maintainer ruling
2026-08-19), an authored `deleteBehavior: 'set_null'` on a `master_detail`
field is a named parse-time rejection, so following the suggestion literally
walked an author into a publish-time error.
The message now enumerates only the two values FieldSchema actually accepts
on a master_detail (cascade/restrict — matching object.form.ts's
master_detail deleteBehavior options), and keeps the outcome-naming courtesy
of the parse-time rejection message: it still names set_null to say plainly
it is not honored on this type, and points to `lookup` for the case where
children must survive the parent. The `fix` payload was already correct and
is unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to listnot 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
  • 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 — 5 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 967402a12d6c7f43361df68357e29bba93465e47packageMentionDocs.

Which tree this was computed on

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

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

@claude

claudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

⏸ Evicted in the same batch as #13659deliberately holding the re-queue for a few minutes, and saying why

domain:devx PM seat (#6023), session session_01Pk26oZ12t5N1hwGW1m1MgC. Stating the hold rather than leaving it silent: a wait that is not written down does not exist.

What happened

Queue build 33363096644 went red and evicted both PRs in its batch — this one (enqueued 06:15:02Z) and #13659 (06:09:43Z). Failure: Test Core (1/6)@objectstack/clitest/run-dev-unbuilt-workspace.e2e.test.ts, two assertions. Full diagnosis and mechanism: #13683.

⚠️ Why I am not simply re-queueing this one immediately

I cannot honestly claim "not this PR's" here, the way I can for #13659.

That does not make it the cause. Its change is two added assertions about a lint message string, with no relationship to run-dev.js, oclif resolution, or packages/cli/src/utils/format.ts. But the failing fixture works by mutating a shared path inside packages/cli/node_modules (renaming @objectstack/spec/dist aside so an import fails on purpose), and a fixture like that is perturbable by changes to what else runs in the same package's test process. ⚠️Available mechanism ≠ demonstrated one — I am not asserting it.

The hold, and what it buys

#13659 is re-queued alone. If I re-queue this one now, the two likely batch together again and a green or red tells me nothing about which PR it belonged to.

Sequencing them costs nothing extra — the same two queue attempts, run in an order that discriminates:

#13659 alonereadingthis PR's next step
passesthe failure is not #13659's, and the batch is the remaining variablere-queue this one alone; a red then points at this PR and is mine to root-cause
fails again, same signaturereproducible and independent of this PR entirelyre-queue this one normally; the defect is packages/cli's, per #13683

⛔ Not a flake dismissal, ⛔ not an empty commit, ⛔ no test skipped or quarantined, ⛔ no re-run spent yet on this PR — its single allowance is intact.

The PR itself is unchanged and green: 33 checks, 0 failed, mergeable_state: clean, ACCEPTed on #11668. Nothing here is a defect in the change.

Re-queueing as soon as #13659 resolves.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

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

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

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

  • Temporal Conformance (live PG + MySQL) — 失败步骤: Run driver-sql suite against both live servers

    × §3 keeps sub-second resolution, so same-second updates are ordered 1026ms
    FAIL src/sql-driver-11224-update-stamp-precision.test.ts > #11224 — the UPDATE door stamps at the audit column's precision (live mysql) > §3 keeps sub-second resolution, so same-second updates are or
    ↳ 失败原因: AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000
    

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

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

  • src/sql-driver-11224-update-stamp-precision.test.ts — 24h 窗口内只有本 PR 撞到过,暂不汇总(再有一个不同 PR 撞到就会自动开汇总 issue)。
  • ⚠️ 24h 评论账本没读完(超过 5 页仍未读到窗口尽头),所以上面的「不同 PR 数」是下界,不是全量。

历史信号:

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

分诊清单:

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

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

@claude

claudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

✅ The sequenced experiment resolved — and this PR's own red is a validity guard, not a behaviour change. Re-queueing once.

domain:devx PM seat (#6023), session session_01Pk26oZ12t5N1hwGW1m1MgC. Reporting the result of the hold I declared above.

1. The experiment: #13659 alone PASSED ⇒ the run-dev failure was not attributable to this PR

Re-queued #13659 by itself at 06:30:08Z. Its build gh-readonly-queue/main/pr-13659-94decf8a… (06:30:26Z) completed success. The run-dev-unbuilt-workspace.e2e.test.ts failure did not reproduce.

⚠️And my batching premise was wrong — correcting it rather than quietly dropping it. I assumed the two PRs shared one batch. The queue built them on separate branches:

runbranchoutcome
33363096644…/pr-13659-713f83f5…run-dev-unbuilt-workspace
33364345029…/pr-13668-713f83f5…a different test entirely
…/pr-13659-94decf8a…success

⇒ The "grouped-queue neighbour" hypothesis I raised on #13683 is weakened, not confirmed: the two PRs' failures came from separate builds and were different tests. #13683 updated accordingly.

2. This PR's own red: Temporal Conformance (live PG + MySQL)

AssertionError: this run took over a second, so second-precision stamps could have differed too:
expected 1006 to be less than 1000

That assertion is not about the product. It is the test's own validity precondition. Read at packages/drivers/driver-sql/src/sql-driver-11224-update-stamp-precision.test.ts:384-391:

// Monotone regardless (the invariant), and — the point — the run spans// less than the full second a truncated stamp would need to distinguish// any two of these at all.constspan=stamps[stamps.length-1]-stamps[0];expect(span,'this run took over a second, so …').toBeLessThan(1_000);

The test needs the whole run to fit inside one second, so that a truncated second-precision stamp could not have distinguished the updates — which is what makes the following distinctness assertion mean anything. At span = 1006 the test detected that its own premise failed by 6 ms. Monotonicity — the actual invariant, asserted on the line above — held.

The experiment was invalidated; the product was not falsified.

⚠️This is a clean counterexample to the triage comment's own heuristic (「断言(AssertionError: …)才指向真实的行为改变」). That heuristic is sound in general, and it is what I used to refuse a flake reading on #13659. Here it misfires, because the assertion is a timing measurement. The discriminator is not AssertionError vs timeout — it is what the assertion measures.

3. Why it is not this PR's

This PR's diff: packages/lint/src/data-model-rules.ts (one message string), packages/cli/test/data-model-rules.test.ts (two assertions), one changeset. The failing test drives live PostgreSQL and MySQL through packages/drivers/driver-sql. No mechanism connects them.

⭐ And it is known ground: #11572 (closed, domain:engine) is this exact guard — "a probabilistic guard that reds with CORRECT code on a fast runner". Today's is its mirror image (too slow, not too fast), which that card did not cover. Filed as a fresh finding rather than reopening another lane's closed card.

4. Re-queueing — the one allowance, now spent

ROUNDS = 6, MIN_GAP_MS = 5 ⇒ ~25 ms of deliberate spacing plus 6 UPDATE round-trips against two live servers, inside a hard 1000 ms wall. Ordinarily ample; under runner contention, 1006 ms happens. This is the case the rules reserve a re-run for.

⛔ Not skipping, disabling or quarantining the test. ⛔ No empty commit. ⛔ Not touching driver-sql from this PR — that would widen a lint-message change into another lane's package. If it reds again with this signature, I stop re-queueing and it belongs to the new finding, not to this PR.

The PR itself remains green and unchanged: 33 checks, 0 failed, ACCEPTed on #11668.


Generated by Claude Code

@os-project-manager
os-project-manager added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit 9057811Aug 31, 2026
34 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-11668-delete-behavior-suggestion-text branch August 31, 2026 07:12
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/steststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lint relationship/delete-behavior suggestion still names set_null as declarable on a master_detail — a spelling #9689 makes a parse-time rejection

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' fix(lint): relationship/delete-behavior suggestion drops set_null on master_detail by os-project-manager · Pull Request #13668 · objectstack-ai/objectstack · GitHub
Skip to content

fix(lint): relationship/delete-behavior suggestion drops set_null on master_detail - #13668

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-11668-delete-behavior-suggestion-text
Aug 31, 2026
Merged

fix(lint): relationship/delete-behavior suggestion drops set_null on master_detail#13668
os-project-manager merged 1 commit into
mainfrom
claude/issue-11668-delete-behavior-suggestion-text

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#11668

What

lintDataModel's relationship/delete-behavior suggestion (packages/lint/src/data-model-rules.ts) told an author an undeclared master_detail.deleteBehavior could be cascade, restrict, or set_null. Since #9689 (PR #11406, maintainer ruling 2026-08-19), an authored deleteBehavior: 'set_null' on a master_detail field is a named parse-time rejection (packages/spec/src/data/field.zod.ts) — a detail row cannot outlive its master, so the engine resolves every value except restrict to cascade on this type. Following this suggestion's own enumeration literally walked an author into that rejection at publish time.

Before:

master_detail "${obj.name}.${fieldName}" → ${parent} should declare deleteBehavior (cascade/restrict/set_null)

After:

master_detail "${obj.name}.${fieldName}" → ${parent} should declare deleteBehavior (cascade/restrict — set_null is not honored on master_detail; use a lookup field if children must survive the parent)

The corrected enumeration (cascade/restrict) is not my own choice — it is read off two existing authorities that already agree:

The message keeps the issue's suggested "outcome-naming courtesy": it still mentionsset_null, but only to say it is not honored on this type and to point at lookup as the alternative — it no longer offers it as a declarable choice.

⛔ Not touched, per the card's Zone 1: the rule's logic (still consumes RAW authored objects — def.deleteBehavior === undefined still means "not authored"; #9689's idempotent-materialization change never reaches this rule), and the fix payload (deleteBehavior: 'cascade'), which was already correct.

Tests

Generated by Claude Code


Generated by Claude Code

…master_detail
The `relationship/delete-behavior` suggestion in `lintDataModel` told an
author an undeclared `master_detail.deleteBehavior` could be `cascade`,
`restrict`, or `set_null`. Since #9689 (PR #11406, maintainer ruling
2026-08-19), an authored `deleteBehavior: 'set_null'` on a `master_detail`
field is a named parse-time rejection, so following the suggestion literally
walked an author into a publish-time error.
The message now enumerates only the two values FieldSchema actually accepts
on a master_detail (cascade/restrict — matching object.form.ts's
master_detail deleteBehavior options), and keeps the outcome-naming courtesy
of the parse-time rejection message: it still names set_null to say plainly
it is not honored on this type, and points to `lookup` for the case where
children must survive the parent. The `fix` payload was already correct and
is unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to listnot 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
  • 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 — 5 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 967402a12d6c7f43361df68357e29bba93465e47packageMentionDocs.

Which tree this was computed on

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

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

@claude

claudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

⏸ Evicted in the same batch as #13659deliberately holding the re-queue for a few minutes, and saying why

domain:devx PM seat (#6023), session session_01Pk26oZ12t5N1hwGW1m1MgC. Stating the hold rather than leaving it silent: a wait that is not written down does not exist.

What happened

Queue build 33363096644 went red and evicted both PRs in its batch — this one (enqueued 06:15:02Z) and #13659 (06:09:43Z). Failure: Test Core (1/6)@objectstack/clitest/run-dev-unbuilt-workspace.e2e.test.ts, two assertions. Full diagnosis and mechanism: #13683.

⚠️ Why I am not simply re-queueing this one immediately

I cannot honestly claim "not this PR's" here, the way I can for #13659.

That does not make it the cause. Its change is two added assertions about a lint message string, with no relationship to run-dev.js, oclif resolution, or packages/cli/src/utils/format.ts. But the failing fixture works by mutating a shared path inside packages/cli/node_modules (renaming @objectstack/spec/dist aside so an import fails on purpose), and a fixture like that is perturbable by changes to what else runs in the same package's test process. ⚠️Available mechanism ≠ demonstrated one — I am not asserting it.

The hold, and what it buys

#13659 is re-queued alone. If I re-queue this one now, the two likely batch together again and a green or red tells me nothing about which PR it belonged to.

Sequencing them costs nothing extra — the same two queue attempts, run in an order that discriminates:

#13659 alonereadingthis PR's next step
passesthe failure is not #13659's, and the batch is the remaining variablere-queue this one alone; a red then points at this PR and is mine to root-cause
fails again, same signaturereproducible and independent of this PR entirelyre-queue this one normally; the defect is packages/cli's, per #13683

⛔ Not a flake dismissal, ⛔ not an empty commit, ⛔ no test skipped or quarantined, ⛔ no re-run spent yet on this PR — its single allowance is intact.

The PR itself is unchanged and green: 33 checks, 0 failed, mergeable_state: clean, ACCEPTed on #11668. Nothing here is a defect in the change.

Re-queueing as soon as #13659 resolves.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

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

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

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

  • Temporal Conformance (live PG + MySQL) — 失败步骤: Run driver-sql suite against both live servers

    × §3 keeps sub-second resolution, so same-second updates are ordered 1026ms
    FAIL src/sql-driver-11224-update-stamp-precision.test.ts > #11224 — the UPDATE door stamps at the audit column's precision (live mysql) > §3 keeps sub-second resolution, so same-second updates are or
    ↳ 失败原因: AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000
    

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

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

  • src/sql-driver-11224-update-stamp-precision.test.ts — 24h 窗口内只有本 PR 撞到过,暂不汇总(再有一个不同 PR 撞到就会自动开汇总 issue)。
  • ⚠️ 24h 评论账本没读完(超过 5 页仍未读到窗口尽头),所以上面的「不同 PR 数」是下界,不是全量。

历史信号:

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

分诊清单:

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

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

@claude

claudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

✅ The sequenced experiment resolved — and this PR's own red is a validity guard, not a behaviour change. Re-queueing once.

domain:devx PM seat (#6023), session session_01Pk26oZ12t5N1hwGW1m1MgC. Reporting the result of the hold I declared above.

1. The experiment: #13659 alone PASSED ⇒ the run-dev failure was not attributable to this PR

Re-queued #13659 by itself at 06:30:08Z. Its build gh-readonly-queue/main/pr-13659-94decf8a… (06:30:26Z) completed success. The run-dev-unbuilt-workspace.e2e.test.ts failure did not reproduce.

⚠️And my batching premise was wrong — correcting it rather than quietly dropping it. I assumed the two PRs shared one batch. The queue built them on separate branches:

runbranchoutcome
33363096644…/pr-13659-713f83f5…run-dev-unbuilt-workspace
33364345029…/pr-13668-713f83f5…a different test entirely
…/pr-13659-94decf8a…success

⇒ The "grouped-queue neighbour" hypothesis I raised on #13683 is weakened, not confirmed: the two PRs' failures came from separate builds and were different tests. #13683 updated accordingly.

2. This PR's own red: Temporal Conformance (live PG + MySQL)

AssertionError: this run took over a second, so second-precision stamps could have differed too:
expected 1006 to be less than 1000

That assertion is not about the product. It is the test's own validity precondition. Read at packages/drivers/driver-sql/src/sql-driver-11224-update-stamp-precision.test.ts:384-391:

// Monotone regardless (the invariant), and — the point — the run spans// less than the full second a truncated stamp would need to distinguish// any two of these at all.constspan=stamps[stamps.length-1]-stamps[0];expect(span,'this run took over a second, so …').toBeLessThan(1_000);

The test needs the whole run to fit inside one second, so that a truncated second-precision stamp could not have distinguished the updates — which is what makes the following distinctness assertion mean anything. At span = 1006 the test detected that its own premise failed by 6 ms. Monotonicity — the actual invariant, asserted on the line above — held.

The experiment was invalidated; the product was not falsified.

⚠️This is a clean counterexample to the triage comment's own heuristic (「断言(AssertionError: …)才指向真实的行为改变」). That heuristic is sound in general, and it is what I used to refuse a flake reading on #13659. Here it misfires, because the assertion is a timing measurement. The discriminator is not AssertionError vs timeout — it is what the assertion measures.

3. Why it is not this PR's

This PR's diff: packages/lint/src/data-model-rules.ts (one message string), packages/cli/test/data-model-rules.test.ts (two assertions), one changeset. The failing test drives live PostgreSQL and MySQL through packages/drivers/driver-sql. No mechanism connects them.

⭐ And it is known ground: #11572 (closed, domain:engine) is this exact guard — "a probabilistic guard that reds with CORRECT code on a fast runner". Today's is its mirror image (too slow, not too fast), which that card did not cover. Filed as a fresh finding rather than reopening another lane's closed card.

4. Re-queueing — the one allowance, now spent

ROUNDS = 6, MIN_GAP_MS = 5 ⇒ ~25 ms of deliberate spacing plus 6 UPDATE round-trips against two live servers, inside a hard 1000 ms wall. Ordinarily ample; under runner contention, 1006 ms happens. This is the case the rules reserve a re-run for.

⛔ Not skipping, disabling or quarantining the test. ⛔ No empty commit. ⛔ Not touching driver-sql from this PR — that would widen a lint-message change into another lane's package. If it reds again with this signature, I stop re-queueing and it belongs to the new finding, not to this PR.

The PR itself remains green and unchanged: 33 checks, 0 failed, ACCEPTed on #11668.


Generated by Claude Code

@os-project-manager
os-project-manager added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit 9057811Aug 31, 2026
34 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-11668-delete-behavior-suggestion-text branch August 31, 2026 07:12
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/steststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lint relationship/delete-behavior suggestion still names set_null as declarable on a master_detail — a spelling #9689 makes a parse-time rejection

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(lint): relationship/delete-behavior suggestion drops set_null on master_detail by os-project-manager · Pull Request #13668 · objectstack-ai/objectstack · GitHub
Skip to content

fix(lint): relationship/delete-behavior suggestion drops set_null on master_detail - #13668

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-11668-delete-behavior-suggestion-text
Aug 31, 2026
Merged

fix(lint): relationship/delete-behavior suggestion drops set_null on master_detail#13668
os-project-manager merged 1 commit into
mainfrom
claude/issue-11668-delete-behavior-suggestion-text

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#11668

What

lintDataModel's relationship/delete-behavior suggestion (packages/lint/src/data-model-rules.ts) told an author an undeclared master_detail.deleteBehavior could be cascade, restrict, or set_null. Since #9689 (PR #11406, maintainer ruling 2026-08-19), an authored deleteBehavior: 'set_null' on a master_detail field is a named parse-time rejection (packages/spec/src/data/field.zod.ts) — a detail row cannot outlive its master, so the engine resolves every value except restrict to cascade on this type. Following this suggestion's own enumeration literally walked an author into that rejection at publish time.

Before:

master_detail "${obj.name}.${fieldName}" → ${parent} should declare deleteBehavior (cascade/restrict/set_null)

After:

master_detail "${obj.name}.${fieldName}" → ${parent} should declare deleteBehavior (cascade/restrict — set_null is not honored on master_detail; use a lookup field if children must survive the parent)

The corrected enumeration (cascade/restrict) is not my own choice — it is read off two existing authorities that already agree:

The message keeps the issue's suggested "outcome-naming courtesy": it still mentionsset_null, but only to say it is not honored on this type and to point at lookup as the alternative — it no longer offers it as a declarable choice.

⛔ Not touched, per the card's Zone 1: the rule's logic (still consumes RAW authored objects — def.deleteBehavior === undefined still means "not authored"; #9689's idempotent-materialization change never reaches this rule), and the fix payload (deleteBehavior: 'cascade'), which was already correct.

Tests

Generated by Claude Code


Generated by Claude Code

…master_detail
The `relationship/delete-behavior` suggestion in `lintDataModel` told an
author an undeclared `master_detail.deleteBehavior` could be `cascade`,
`restrict`, or `set_null`. Since #9689 (PR #11406, maintainer ruling
2026-08-19), an authored `deleteBehavior: 'set_null'` on a `master_detail`
field is a named parse-time rejection, so following the suggestion literally
walked an author into a publish-time error.
The message now enumerates only the two values FieldSchema actually accepts
on a master_detail (cascade/restrict — matching object.form.ts's
master_detail deleteBehavior options), and keeps the outcome-naming courtesy
of the parse-time rejection message: it still names set_null to say plainly
it is not honored on this type, and points to `lookup` for the case where
children must survive the parent. The `fix` payload was already correct and
is unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to listnot 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
  • 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 — 5 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 967402a12d6c7f43361df68357e29bba93465e47packageMentionDocs.

Which tree this was computed on

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

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

@claude

claudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

⏸ Evicted in the same batch as #13659deliberately holding the re-queue for a few minutes, and saying why

domain:devx PM seat (#6023), session session_01Pk26oZ12t5N1hwGW1m1MgC. Stating the hold rather than leaving it silent: a wait that is not written down does not exist.

What happened

Queue build 33363096644 went red and evicted both PRs in its batch — this one (enqueued 06:15:02Z) and #13659 (06:09:43Z). Failure: Test Core (1/6)@objectstack/clitest/run-dev-unbuilt-workspace.e2e.test.ts, two assertions. Full diagnosis and mechanism: #13683.

⚠️ Why I am not simply re-queueing this one immediately

I cannot honestly claim "not this PR's" here, the way I can for #13659.

That does not make it the cause. Its change is two added assertions about a lint message string, with no relationship to run-dev.js, oclif resolution, or packages/cli/src/utils/format.ts. But the failing fixture works by mutating a shared path inside packages/cli/node_modules (renaming @objectstack/spec/dist aside so an import fails on purpose), and a fixture like that is perturbable by changes to what else runs in the same package's test process. ⚠️Available mechanism ≠ demonstrated one — I am not asserting it.

The hold, and what it buys

#13659 is re-queued alone. If I re-queue this one now, the two likely batch together again and a green or red tells me nothing about which PR it belonged to.

Sequencing them costs nothing extra — the same two queue attempts, run in an order that discriminates:

#13659 alonereadingthis PR's next step
passesthe failure is not #13659's, and the batch is the remaining variablere-queue this one alone; a red then points at this PR and is mine to root-cause
fails again, same signaturereproducible and independent of this PR entirelyre-queue this one normally; the defect is packages/cli's, per #13683

⛔ Not a flake dismissal, ⛔ not an empty commit, ⛔ no test skipped or quarantined, ⛔ no re-run spent yet on this PR — its single allowance is intact.

The PR itself is unchanged and green: 33 checks, 0 failed, mergeable_state: clean, ACCEPTed on #11668. Nothing here is a defect in the change.

Re-queueing as soon as #13659 resolves.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

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

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

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

  • Temporal Conformance (live PG + MySQL) — 失败步骤: Run driver-sql suite against both live servers

    × §3 keeps sub-second resolution, so same-second updates are ordered 1026ms
    FAIL src/sql-driver-11224-update-stamp-precision.test.ts > #11224 — the UPDATE door stamps at the audit column's precision (live mysql) > §3 keeps sub-second resolution, so same-second updates are or
    ↳ 失败原因: AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000
    

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

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

  • src/sql-driver-11224-update-stamp-precision.test.ts — 24h 窗口内只有本 PR 撞到过,暂不汇总(再有一个不同 PR 撞到就会自动开汇总 issue)。
  • ⚠️ 24h 评论账本没读完(超过 5 页仍未读到窗口尽头),所以上面的「不同 PR 数」是下界,不是全量。

历史信号:

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

分诊清单:

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

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

@claude

claudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

✅ The sequenced experiment resolved — and this PR's own red is a validity guard, not a behaviour change. Re-queueing once.

domain:devx PM seat (#6023), session session_01Pk26oZ12t5N1hwGW1m1MgC. Reporting the result of the hold I declared above.

1. The experiment: #13659 alone PASSED ⇒ the run-dev failure was not attributable to this PR

Re-queued #13659 by itself at 06:30:08Z. Its build gh-readonly-queue/main/pr-13659-94decf8a… (06:30:26Z) completed success. The run-dev-unbuilt-workspace.e2e.test.ts failure did not reproduce.

⚠️And my batching premise was wrong — correcting it rather than quietly dropping it. I assumed the two PRs shared one batch. The queue built them on separate branches:

runbranchoutcome
33363096644…/pr-13659-713f83f5…run-dev-unbuilt-workspace
33364345029…/pr-13668-713f83f5…a different test entirely
…/pr-13659-94decf8a…success

⇒ The "grouped-queue neighbour" hypothesis I raised on #13683 is weakened, not confirmed: the two PRs' failures came from separate builds and were different tests. #13683 updated accordingly.

2. This PR's own red: Temporal Conformance (live PG + MySQL)

AssertionError: this run took over a second, so second-precision stamps could have differed too:
expected 1006 to be less than 1000

That assertion is not about the product. It is the test's own validity precondition. Read at packages/drivers/driver-sql/src/sql-driver-11224-update-stamp-precision.test.ts:384-391:

// Monotone regardless (the invariant), and — the point — the run spans// less than the full second a truncated stamp would need to distinguish// any two of these at all.constspan=stamps[stamps.length-1]-stamps[0];expect(span,'this run took over a second, so …').toBeLessThan(1_000);

The test needs the whole run to fit inside one second, so that a truncated second-precision stamp could not have distinguished the updates — which is what makes the following distinctness assertion mean anything. At span = 1006 the test detected that its own premise failed by 6 ms. Monotonicity — the actual invariant, asserted on the line above — held.

The experiment was invalidated; the product was not falsified.

⚠️This is a clean counterexample to the triage comment's own heuristic (「断言(AssertionError: …)才指向真实的行为改变」). That heuristic is sound in general, and it is what I used to refuse a flake reading on #13659. Here it misfires, because the assertion is a timing measurement. The discriminator is not AssertionError vs timeout — it is what the assertion measures.

3. Why it is not this PR's

This PR's diff: packages/lint/src/data-model-rules.ts (one message string), packages/cli/test/data-model-rules.test.ts (two assertions), one changeset. The failing test drives live PostgreSQL and MySQL through packages/drivers/driver-sql. No mechanism connects them.

⭐ And it is known ground: #11572 (closed, domain:engine) is this exact guard — "a probabilistic guard that reds with CORRECT code on a fast runner". Today's is its mirror image (too slow, not too fast), which that card did not cover. Filed as a fresh finding rather than reopening another lane's closed card.

4. Re-queueing — the one allowance, now spent

ROUNDS = 6, MIN_GAP_MS = 5 ⇒ ~25 ms of deliberate spacing plus 6 UPDATE round-trips against two live servers, inside a hard 1000 ms wall. Ordinarily ample; under runner contention, 1006 ms happens. This is the case the rules reserve a re-run for.

⛔ Not skipping, disabling or quarantining the test. ⛔ No empty commit. ⛔ Not touching driver-sql from this PR — that would widen a lint-message change into another lane's package. If it reds again with this signature, I stop re-queueing and it belongs to the new finding, not to this PR.

The PR itself remains green and unchanged: 33 checks, 0 failed, ACCEPTed on #11668.


Generated by Claude Code

@os-project-manager
os-project-manager added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit 9057811Aug 31, 2026
34 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-11668-delete-behavior-suggestion-text branch August 31, 2026 07:12
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/steststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lint relationship/delete-behavior suggestion still names set_null as declarable on a master_detail — a spelling #9689 makes a parse-time rejection

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(lint): relationship/delete-behavior suggestion drops set_null on master_detail by os-project-manager · Pull Request #13668 · objectstack-ai/objectstack · GitHub
Skip to content

fix(lint): relationship/delete-behavior suggestion drops set_null on master_detail - #13668

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-11668-delete-behavior-suggestion-text
Aug 31, 2026
Merged

fix(lint): relationship/delete-behavior suggestion drops set_null on master_detail#13668
os-project-manager merged 1 commit into
mainfrom
claude/issue-11668-delete-behavior-suggestion-text

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#11668

What

lintDataModel's relationship/delete-behavior suggestion (packages/lint/src/data-model-rules.ts) told an author an undeclared master_detail.deleteBehavior could be cascade, restrict, or set_null. Since #9689 (PR #11406, maintainer ruling 2026-08-19), an authored deleteBehavior: 'set_null' on a master_detail field is a named parse-time rejection (packages/spec/src/data/field.zod.ts) — a detail row cannot outlive its master, so the engine resolves every value except restrict to cascade on this type. Following this suggestion's own enumeration literally walked an author into that rejection at publish time.

Before:

master_detail "${obj.name}.${fieldName}" → ${parent} should declare deleteBehavior (cascade/restrict/set_null)

After:

master_detail "${obj.name}.${fieldName}" → ${parent} should declare deleteBehavior (cascade/restrict — set_null is not honored on master_detail; use a lookup field if children must survive the parent)

The corrected enumeration (cascade/restrict) is not my own choice — it is read off two existing authorities that already agree:

The message keeps the issue's suggested "outcome-naming courtesy": it still mentionsset_null, but only to say it is not honored on this type and to point at lookup as the alternative — it no longer offers it as a declarable choice.

⛔ Not touched, per the card's Zone 1: the rule's logic (still consumes RAW authored objects — def.deleteBehavior === undefined still means "not authored"; #9689's idempotent-materialization change never reaches this rule), and the fix payload (deleteBehavior: 'cascade'), which was already correct.

Tests

Generated by Claude Code


Generated by Claude Code

…master_detail
The `relationship/delete-behavior` suggestion in `lintDataModel` told an
author an undeclared `master_detail.deleteBehavior` could be `cascade`,
`restrict`, or `set_null`. Since #9689 (PR #11406, maintainer ruling
2026-08-19), an authored `deleteBehavior: 'set_null'` on a `master_detail`
field is a named parse-time rejection, so following the suggestion literally
walked an author into a publish-time error.
The message now enumerates only the two values FieldSchema actually accepts
on a master_detail (cascade/restrict — matching object.form.ts's
master_detail deleteBehavior options), and keeps the outcome-naming courtesy
of the parse-time rejection message: it still names set_null to say plainly
it is not honored on this type, and points to `lookup` for the case where
children must survive the parent. The `fix` payload was already correct and
is unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to listnot 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
  • 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 — 5 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 967402a12d6c7f43361df68357e29bba93465e47packageMentionDocs.

Which tree this was computed on

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

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

@claude

claudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

⏸ Evicted in the same batch as #13659deliberately holding the re-queue for a few minutes, and saying why

domain:devx PM seat (#6023), session session_01Pk26oZ12t5N1hwGW1m1MgC. Stating the hold rather than leaving it silent: a wait that is not written down does not exist.

What happened

Queue build 33363096644 went red and evicted both PRs in its batch — this one (enqueued 06:15:02Z) and #13659 (06:09:43Z). Failure: Test Core (1/6)@objectstack/clitest/run-dev-unbuilt-workspace.e2e.test.ts, two assertions. Full diagnosis and mechanism: #13683.

⚠️ Why I am not simply re-queueing this one immediately

I cannot honestly claim "not this PR's" here, the way I can for #13659.

That does not make it the cause. Its change is two added assertions about a lint message string, with no relationship to run-dev.js, oclif resolution, or packages/cli/src/utils/format.ts. But the failing fixture works by mutating a shared path inside packages/cli/node_modules (renaming @objectstack/spec/dist aside so an import fails on purpose), and a fixture like that is perturbable by changes to what else runs in the same package's test process. ⚠️Available mechanism ≠ demonstrated one — I am not asserting it.

The hold, and what it buys

#13659 is re-queued alone. If I re-queue this one now, the two likely batch together again and a green or red tells me nothing about which PR it belonged to.

Sequencing them costs nothing extra — the same two queue attempts, run in an order that discriminates:

#13659 alonereadingthis PR's next step
passesthe failure is not #13659's, and the batch is the remaining variablere-queue this one alone; a red then points at this PR and is mine to root-cause
fails again, same signaturereproducible and independent of this PR entirelyre-queue this one normally; the defect is packages/cli's, per #13683

⛔ Not a flake dismissal, ⛔ not an empty commit, ⛔ no test skipped or quarantined, ⛔ no re-run spent yet on this PR — its single allowance is intact.

The PR itself is unchanged and green: 33 checks, 0 failed, mergeable_state: clean, ACCEPTed on #11668. Nothing here is a defect in the change.

Re-queueing as soon as #13659 resolves.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

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

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

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

  • Temporal Conformance (live PG + MySQL) — 失败步骤: Run driver-sql suite against both live servers

    × §3 keeps sub-second resolution, so same-second updates are ordered 1026ms
    FAIL src/sql-driver-11224-update-stamp-precision.test.ts > #11224 — the UPDATE door stamps at the audit column's precision (live mysql) > §3 keeps sub-second resolution, so same-second updates are or
    ↳ 失败原因: AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000
    

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

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

  • src/sql-driver-11224-update-stamp-precision.test.ts — 24h 窗口内只有本 PR 撞到过,暂不汇总(再有一个不同 PR 撞到就会自动开汇总 issue)。
  • ⚠️ 24h 评论账本没读完(超过 5 页仍未读到窗口尽头),所以上面的「不同 PR 数」是下界,不是全量。

历史信号:

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

分诊清单:

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

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

@claude

claudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

✅ The sequenced experiment resolved — and this PR's own red is a validity guard, not a behaviour change. Re-queueing once.

domain:devx PM seat (#6023), session session_01Pk26oZ12t5N1hwGW1m1MgC. Reporting the result of the hold I declared above.

1. The experiment: #13659 alone PASSED ⇒ the run-dev failure was not attributable to this PR

Re-queued #13659 by itself at 06:30:08Z. Its build gh-readonly-queue/main/pr-13659-94decf8a… (06:30:26Z) completed success. The run-dev-unbuilt-workspace.e2e.test.ts failure did not reproduce.

⚠️And my batching premise was wrong — correcting it rather than quietly dropping it. I assumed the two PRs shared one batch. The queue built them on separate branches:

runbranchoutcome
33363096644…/pr-13659-713f83f5…run-dev-unbuilt-workspace
33364345029…/pr-13668-713f83f5…a different test entirely
…/pr-13659-94decf8a…success

⇒ The "grouped-queue neighbour" hypothesis I raised on #13683 is weakened, not confirmed: the two PRs' failures came from separate builds and were different tests. #13683 updated accordingly.

2. This PR's own red: Temporal Conformance (live PG + MySQL)

AssertionError: this run took over a second, so second-precision stamps could have differed too:
expected 1006 to be less than 1000

That assertion is not about the product. It is the test's own validity precondition. Read at packages/drivers/driver-sql/src/sql-driver-11224-update-stamp-precision.test.ts:384-391:

// Monotone regardless (the invariant), and — the point — the run spans// less than the full second a truncated stamp would need to distinguish// any two of these at all.constspan=stamps[stamps.length-1]-stamps[0];expect(span,'this run took over a second, so …').toBeLessThan(1_000);

The test needs the whole run to fit inside one second, so that a truncated second-precision stamp could not have distinguished the updates — which is what makes the following distinctness assertion mean anything. At span = 1006 the test detected that its own premise failed by 6 ms. Monotonicity — the actual invariant, asserted on the line above — held.

The experiment was invalidated; the product was not falsified.

⚠️This is a clean counterexample to the triage comment's own heuristic (「断言(AssertionError: …)才指向真实的行为改变」). That heuristic is sound in general, and it is what I used to refuse a flake reading on #13659. Here it misfires, because the assertion is a timing measurement. The discriminator is not AssertionError vs timeout — it is what the assertion measures.

3. Why it is not this PR's

This PR's diff: packages/lint/src/data-model-rules.ts (one message string), packages/cli/test/data-model-rules.test.ts (two assertions), one changeset. The failing test drives live PostgreSQL and MySQL through packages/drivers/driver-sql. No mechanism connects them.

⭐ And it is known ground: #11572 (closed, domain:engine) is this exact guard — "a probabilistic guard that reds with CORRECT code on a fast runner". Today's is its mirror image (too slow, not too fast), which that card did not cover. Filed as a fresh finding rather than reopening another lane's closed card.

4. Re-queueing — the one allowance, now spent

ROUNDS = 6, MIN_GAP_MS = 5 ⇒ ~25 ms of deliberate spacing plus 6 UPDATE round-trips against two live servers, inside a hard 1000 ms wall. Ordinarily ample; under runner contention, 1006 ms happens. This is the case the rules reserve a re-run for.

⛔ Not skipping, disabling or quarantining the test. ⛔ No empty commit. ⛔ Not touching driver-sql from this PR — that would widen a lint-message change into another lane's package. If it reds again with this signature, I stop re-queueing and it belongs to the new finding, not to this PR.

The PR itself remains green and unchanged: 33 checks, 0 failed, ACCEPTed on #11668.


Generated by Claude Code

@os-project-manager
os-project-manager added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit 9057811Aug 31, 2026
34 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-11668-delete-behavior-suggestion-text branch August 31, 2026 07:12
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/steststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lint relationship/delete-behavior suggestion still names set_null as declarable on a master_detail — a spelling #9689 makes a parse-time rejection

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); fix(lint): relationship/delete-behavior suggestion drops set_null on master_detail by os-project-manager · Pull Request #13668 · objectstack-ai/objectstack · GitHub
Skip to content

fix(lint): relationship/delete-behavior suggestion drops set_null on master_detail - #13668

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-11668-delete-behavior-suggestion-text
Aug 31, 2026
Merged

fix(lint): relationship/delete-behavior suggestion drops set_null on master_detail#13668
os-project-manager merged 1 commit into
mainfrom
claude/issue-11668-delete-behavior-suggestion-text

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#11668

What

lintDataModel's relationship/delete-behavior suggestion (packages/lint/src/data-model-rules.ts) told an author an undeclared master_detail.deleteBehavior could be cascade, restrict, or set_null. Since #9689 (PR #11406, maintainer ruling 2026-08-19), an authored deleteBehavior: 'set_null' on a master_detail field is a named parse-time rejection (packages/spec/src/data/field.zod.ts) — a detail row cannot outlive its master, so the engine resolves every value except restrict to cascade on this type. Following this suggestion's own enumeration literally walked an author into that rejection at publish time.

Before:

master_detail "${obj.name}.${fieldName}" → ${parent} should declare deleteBehavior (cascade/restrict/set_null)

After:

master_detail "${obj.name}.${fieldName}" → ${parent} should declare deleteBehavior (cascade/restrict — set_null is not honored on master_detail; use a lookup field if children must survive the parent)

The corrected enumeration (cascade/restrict) is not my own choice — it is read off two existing authorities that already agree:

The message keeps the issue's suggested "outcome-naming courtesy": it still mentionsset_null, but only to say it is not honored on this type and to point at lookup as the alternative — it no longer offers it as a declarable choice.

⛔ Not touched, per the card's Zone 1: the rule's logic (still consumes RAW authored objects — def.deleteBehavior === undefined still means "not authored"; #9689's idempotent-materialization change never reaches this rule), and the fix payload (deleteBehavior: 'cascade'), which was already correct.

Tests

Generated by Claude Code


Generated by Claude Code

…master_detail
The `relationship/delete-behavior` suggestion in `lintDataModel` told an
author an undeclared `master_detail.deleteBehavior` could be `cascade`,
`restrict`, or `set_null`. Since #9689 (PR #11406, maintainer ruling
2026-08-19), an authored `deleteBehavior: 'set_null'` on a `master_detail`
field is a named parse-time rejection, so following the suggestion literally
walked an author into a publish-time error.
The message now enumerates only the two values FieldSchema actually accepts
on a master_detail (cascade/restrict — matching object.form.ts's
master_detail deleteBehavior options), and keeps the outcome-naming courtesy
of the parse-time rejection message: it still names set_null to say plainly
it is not honored on this type, and points to `lookup` for the case where
children must survive the parent. The `fix` payload was already correct and
is unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to listnot 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
  • 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 — 5 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 967402a12d6c7f43361df68357e29bba93465e47packageMentionDocs.

Which tree this was computed on

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

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

@claude

claudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

⏸ Evicted in the same batch as #13659deliberately holding the re-queue for a few minutes, and saying why

domain:devx PM seat (#6023), session session_01Pk26oZ12t5N1hwGW1m1MgC. Stating the hold rather than leaving it silent: a wait that is not written down does not exist.

What happened

Queue build 33363096644 went red and evicted both PRs in its batch — this one (enqueued 06:15:02Z) and #13659 (06:09:43Z). Failure: Test Core (1/6)@objectstack/clitest/run-dev-unbuilt-workspace.e2e.test.ts, two assertions. Full diagnosis and mechanism: #13683.

⚠️ Why I am not simply re-queueing this one immediately

I cannot honestly claim "not this PR's" here, the way I can for #13659.

That does not make it the cause. Its change is two added assertions about a lint message string, with no relationship to run-dev.js, oclif resolution, or packages/cli/src/utils/format.ts. But the failing fixture works by mutating a shared path inside packages/cli/node_modules (renaming @objectstack/spec/dist aside so an import fails on purpose), and a fixture like that is perturbable by changes to what else runs in the same package's test process. ⚠️Available mechanism ≠ demonstrated one — I am not asserting it.

The hold, and what it buys

#13659 is re-queued alone. If I re-queue this one now, the two likely batch together again and a green or red tells me nothing about which PR it belonged to.

Sequencing them costs nothing extra — the same two queue attempts, run in an order that discriminates:

#13659 alonereadingthis PR's next step
passesthe failure is not #13659's, and the batch is the remaining variablere-queue this one alone; a red then points at this PR and is mine to root-cause
fails again, same signaturereproducible and independent of this PR entirelyre-queue this one normally; the defect is packages/cli's, per #13683

⛔ Not a flake dismissal, ⛔ not an empty commit, ⛔ no test skipped or quarantined, ⛔ no re-run spent yet on this PR — its single allowance is intact.

The PR itself is unchanged and green: 33 checks, 0 failed, mergeable_state: clean, ACCEPTed on #11668. Nothing here is a defect in the change.

Re-queueing as soon as #13659 resolves.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

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

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

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

  • Temporal Conformance (live PG + MySQL) — 失败步骤: Run driver-sql suite against both live servers

    × §3 keeps sub-second resolution, so same-second updates are ordered 1026ms
    FAIL src/sql-driver-11224-update-stamp-precision.test.ts > #11224 — the UPDATE door stamps at the audit column's precision (live mysql) > §3 keeps sub-second resolution, so same-second updates are or
    ↳ 失败原因: AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000
    

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

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

  • src/sql-driver-11224-update-stamp-precision.test.ts — 24h 窗口内只有本 PR 撞到过,暂不汇总(再有一个不同 PR 撞到就会自动开汇总 issue)。
  • ⚠️ 24h 评论账本没读完(超过 5 页仍未读到窗口尽头),所以上面的「不同 PR 数」是下界,不是全量。

历史信号:

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

分诊清单:

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

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

@claude

claudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

✅ The sequenced experiment resolved — and this PR's own red is a validity guard, not a behaviour change. Re-queueing once.

domain:devx PM seat (#6023), session session_01Pk26oZ12t5N1hwGW1m1MgC. Reporting the result of the hold I declared above.

1. The experiment: #13659 alone PASSED ⇒ the run-dev failure was not attributable to this PR

Re-queued #13659 by itself at 06:30:08Z. Its build gh-readonly-queue/main/pr-13659-94decf8a… (06:30:26Z) completed success. The run-dev-unbuilt-workspace.e2e.test.ts failure did not reproduce.

⚠️And my batching premise was wrong — correcting it rather than quietly dropping it. I assumed the two PRs shared one batch. The queue built them on separate branches:

runbranchoutcome
33363096644…/pr-13659-713f83f5…run-dev-unbuilt-workspace
33364345029…/pr-13668-713f83f5…a different test entirely
…/pr-13659-94decf8a…success

⇒ The "grouped-queue neighbour" hypothesis I raised on #13683 is weakened, not confirmed: the two PRs' failures came from separate builds and were different tests. #13683 updated accordingly.

2. This PR's own red: Temporal Conformance (live PG + MySQL)

AssertionError: this run took over a second, so second-precision stamps could have differed too:
expected 1006 to be less than 1000

That assertion is not about the product. It is the test's own validity precondition. Read at packages/drivers/driver-sql/src/sql-driver-11224-update-stamp-precision.test.ts:384-391:

// Monotone regardless (the invariant), and — the point — the run spans// less than the full second a truncated stamp would need to distinguish// any two of these at all.constspan=stamps[stamps.length-1]-stamps[0];expect(span,'this run took over a second, so …').toBeLessThan(1_000);

The test needs the whole run to fit inside one second, so that a truncated second-precision stamp could not have distinguished the updates — which is what makes the following distinctness assertion mean anything. At span = 1006 the test detected that its own premise failed by 6 ms. Monotonicity — the actual invariant, asserted on the line above — held.

The experiment was invalidated; the product was not falsified.

⚠️This is a clean counterexample to the triage comment's own heuristic (「断言(AssertionError: …)才指向真实的行为改变」). That heuristic is sound in general, and it is what I used to refuse a flake reading on #13659. Here it misfires, because the assertion is a timing measurement. The discriminator is not AssertionError vs timeout — it is what the assertion measures.

3. Why it is not this PR's

This PR's diff: packages/lint/src/data-model-rules.ts (one message string), packages/cli/test/data-model-rules.test.ts (two assertions), one changeset. The failing test drives live PostgreSQL and MySQL through packages/drivers/driver-sql. No mechanism connects them.

⭐ And it is known ground: #11572 (closed, domain:engine) is this exact guard — "a probabilistic guard that reds with CORRECT code on a fast runner". Today's is its mirror image (too slow, not too fast), which that card did not cover. Filed as a fresh finding rather than reopening another lane's closed card.

4. Re-queueing — the one allowance, now spent

ROUNDS = 6, MIN_GAP_MS = 5 ⇒ ~25 ms of deliberate spacing plus 6 UPDATE round-trips against two live servers, inside a hard 1000 ms wall. Ordinarily ample; under runner contention, 1006 ms happens. This is the case the rules reserve a re-run for.

⛔ Not skipping, disabling or quarantining the test. ⛔ No empty commit. ⛔ Not touching driver-sql from this PR — that would widen a lint-message change into another lane's package. If it reds again with this signature, I stop re-queueing and it belongs to the new finding, not to this PR.

The PR itself remains green and unchanged: 33 checks, 0 failed, ACCEPTed on #11668.


Generated by Claude Code

@os-project-manager
os-project-manager added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit 9057811Aug 31, 2026
34 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-11668-delete-behavior-suggestion-text branch August 31, 2026 07:12
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/steststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lint relationship/delete-behavior suggestion still names set_null as declarable on a master_detail — a spelling #9689 makes a parse-time rejection

2 participants

@os-project-manager@claude