Repository navigation
fix(lint): null-guard gate accuracy follow-ups: the runtime door's bare start-guard gap recorded, the summary-nulls presumption stated, an insert-safe guard for previous - #29582
Conversation
…n review - Record that the runtime publish gate refuses an edge `os validate` accepts when the start condition guards a BARE name: the ledger's flow row and the 29026 changeset's runtime-gate sentence now say so, and a test pins both doors. - `isAlwaysValuedSummary`: the count/sum judgment presumes the one-off `os migrate summary-nulls` ran; nothing backfills a stored NULL on its own. The 29026 changeset's roll-up sentence says the same. - A `previous.` operand's message now names the guard that holds on an insert, `previous != null && previous.x != null`, after the verbatim shared hint, which is unchanged. Claude-Session: https://claude.ai/code/session_01JQwxbZNEUhFEaL9RRQWJRR Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift Check2 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. What this run could not see
Coarse fallback — 4 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 30a489a4bdff260a692d968edc331b8087de6f6a && git checkout 30a489a4bdff260a692d968edc331b8087de6f6a
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin e832ddb1f5a1cd9804ec9de998a9c1b8cd35b781 7bf8e16f677d38475067a30eb247e450346ea7dd && git checkout -B drift-repro e832ddb1f5a1cd9804ec9de998a9c1b8cd35b781 && git merge --no-ff 7bf8e16f677d38475067a30eb247e450346ea7dd
node scripts/docs-audit/affected-docs.mjs --json e832ddb1f5a1cd9804ec9de998a9c1b8cd35b781 |
|
domain:devx#1 · os-warren · session_01JQwxbaBjisxkQo9hht9bFB Contract-review-claim: session_01JQwxbaBjisxkQo9hht9bFB · head d4f21b7 · 2026-11-02T20:00Z |
|
domain:devx#1 · os-warren · session_01JQwxbaBjisxkQo9hht9bFB Contract reviewServed-tier: Inputs: card #29491 body and all four comments (triage x2, the claim, the os-dev-report with ① Derived judgmentsAccept-set changes the diff implies: none, and that is right. The only executable change outside tests is in Public-surface changes, each judged:
The "every surface" claim the ledger and the clause make, checked in the cited code: a validation rule's ② Semver level
③ Boundary flags
④ Design judgmentThe narrowing face is served because the PR edits a changeset carrying
Findings (no must-fix):
Both are prose accuracy in a pending release note; neither changes a verdict, a message or an accept set, so neither is a FAIL. Implemented-by: VERDICT: PASS |
…note The runtime-gate sentence now says `os validate` passes the bare-guard flow only where its bound set is provable, and the roll-up sentence spells the stale parent as one with no child written since. Claude-Session: https://claude.ai/code/session_01JQwxbZNEUhFEaL9RRQWJRR Co-authored-by: Claude <noreply@anthropic.com>
|
domain:devx#1 · os-warren · session_01JQwxbaBjisxkQo9hht9bFB Regen-provenance: 6587299006 · A prose-only hop: the record's two should-fixes, nothing else. The two changed changeset sentences are confirmed sentence by sentence in the seat's ACCEPT. |
|
domain:devx#1 · os-warren · session_01JQwxbaBjisxkQo9hht9bFB 席位核对ACCEPT ·
|
Requested by Warren · project thread
Fixes #29491
Clause-②: no
Metadata-protocol: none
Dispatch: Claim 6586327517 · Thread-read 6584817890 · Source: contract review 6582253324 on PR #29474
维护者速读
os validate更严;roll-up 汇总恒有值的判断写明以跑过os migrate summary-nulls为前提;previous.x的报错补一句插入时也成立的完整守卫写法。previous.x != null在插入时本身就会出错,旧提示只给了半个守卫。packages/lint/src/validate-null-guards.ts的nullGuardMessage结尾与PREVIOUS_GUARD_CLAUSE;再看.changeset/29026-flow-condition-null-guard.md的两句改动;最后validate-expressions.flow-null-guards.test.ts新增的两门对照用例。pnpm --filter @objectstack/lint exec vitest run src/validate-null-guards.test.ts src/validate-expressions.flow-null-guards.test.ts,预期 46 个用例全过。What changes
os validate, where a record-triggered flow's bound set is provable, a bare start conditionbudget != nullprovesrecord.budget, so an edge comparingrecord.budget > 200000passes. The runtime publish gate judges the written flow alone (perWriteSnapshotEntrance), its bound set is not provable, no bare name is judged, the bare guard proves nothing, and the same edge is refused. That stricter side stands; the ledger now says so and says why:validate-null-guards.ts, surface ledger flow row: the verdict cell gains "the runtime publish gate is stricter on a bare start guard; see below", and wiring point 3 ("The start condition gates every run") gains the paragraph naming the two doors, the reason deriving the presumption there would be unsound, the cure (record.budget != nullin the start condition, accepted at both doors), and the test that pins it. No runtime stand-down was taken: nobody measured one sound..changeset/29026-flow-condition-null-guard.mdL15, the runtime-gate sentence (deliberate correction, quoted below).isAlwaysValuedSummaryno longer says the engine backfills.validate-expressions.tsdoc comment: the engine seeds a new parent at create time (initializeSummaryFields) and falls back on a child write's recompute; nothing backfills a storedNULLon its own. The count/sum judgment presumes the one-offos migrate summary-nulls(summary-backfill.ts) ran; on a database upgraded in place without it, such a parent readsnulland a validation rule that this gate passed refuses the write. The 29026 changeset's roll-up sentence (L17) says the same (deliberate correction, quoted below).previous.operand's message names the guard that holds on an insert.nullGuardMessageappendsPREVIOUS_GUARD_CLAUSEafter the shared hint, only for an operand spelledprevious.FIELD: "On an insertpreviousis itself null, soprevious.budget != nullalone faults there too: guard it withprevious != null && previous.budget != null."NULL_GUARD_HINTis untouched, so it still reads word for word as objectql's rule-validator sentence. The clause applies on every surface: a validation rule and a hook condition bindprevioustonullon an insert (previousBinding), as a flow's create leg does (seedRunVariables). The ledger's "Not reached" paragraph points at it. New patch changeset.changeset/29491-previous-guard-hint.md.Tests
validate-null-guards.test.ts, new case: forprevious.budget > 5, under bothfail-closedandrun-fails, the message carries the verbatim hint followed by the clause namingprevious != null && previous.budget != null; arecord.budgetoperand and a barebudgetoperand get no clause; the prescribed guard passes the gate; and evaluated withpreviousbound tonull(as on an insert) the full guard answersfalsewhileprevious.budget != null && …alone faults.validate-expressions.flow-null-guards.test.ts, new case: a start conditionbudget != nullwith an edgerecord.budget > 200000gives no finding throughvalidateStackExpressions(build door) and exactly one, onedge 'e2', throughrunStackExpressionPasses(…, { runtimeWriteType: 'flow' })(runtime door); therecord.budget != nullspelling gives none at either door.scripts/ablation-replace.mjs, trap-restored): replacing the clause expression with an empty string turned the new message case red (Tests 1 failed / 33 passed); restore proved by blob equal to HEAD and an emptygit diff HEAD.pnpm --filter @objectstack/lint testat d4f21b7: Test Files 192 passed (192), Tests 7882 passed, 5 skipped.pnpm --filter @objectstack/lint typecheck(tsc,check:test-typecheck): exit 0.Acceptance notes
.changeset/29026-flow-condition-null-guard.md(not yet released). Check Changeset is expected to read red on an edited existing changeset; that is the gate doing its job, and this paragraph is the confirmation.budget != nulland whose edge comparesrecord.budget > 200000passesos validate(where the flow's bound set is provable) and is refused by the runtime publish gate. Spell the start guardrecord.budget != nulland both accept it."countorsumroll-up summary is now always valued for this gate (zero children is zero), on validation rules and hook conditions too;min/max/avgroll-ups stay nullable."os migrate summary-nullsran: on a database upgraded in place without it, a parent row stored before roll-ups were seeded at create time, and no child of it written since, still readsnull."Clause-②: noand no contract review is owed by this PR.PREVIOUS_GUARD_CLAUSEis module-private; the package's export list is unchanged.Verification
dispatch-gates at d4f21b7: 58 derived, 57 exit 0 or the expected red, 1 NOT MEASURED
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 58 families against merge base ff827bc. Prerequisites first:git fetch origin 621a4876(plugin-teardown self-test) andturbo run build --filter='@objectstack/lint...' --filter=@objectstack/objectql(exit 0). Then every family ran once from a runner recordingCMD :: exit N:node scripts/check-empty-changeset.mjs --base origin/mainexited 1 on.changeset/29026-flow-condition-null-guard.md, naming the DELIBERATE CORRECTION class. Expected; see Acceptance notes. The 29491 changeset is new and not flagged.pnpm check:dual-build-cjs-loadsexited 3 (PREREQUISITE NOT MET: it needs every package built). NOT MEASURED locally; CI runs it.Reconciliation: "dispatch-gates --ran: 58 derived famil(ies) accounted for — 57 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3)".
origin/mainmoved six commits past the merge base while the gates ran (head ebbb5b8 and later); none touchespackages/lint,packages/formula,packages/objectqlor the 29026 changeset, andgit merge-treeagainst it is clean. Not merged in; CI judges the merge ref.Human Minimum
packages/specholds no runtime logic. (not touched)pnpm lintpasses. (CI owns the repo-wide lint run)main, one pull request.packages/spectouched: its generated artifacts are regenerated and committed. (not touched)content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.维护者速读block is the maintainer digest the dispatch order requires)Generated by Claude Code