You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
lint null-guard gate: three accuracy follow-ups from the contract review of the flow-condition widening #29491
Filing gate: the three should-fix findings of the contract review record 6582253324 on PR #29474 (for #29026), at head c7417c1fc8. The review found no must-fix, so the PR lands and these follow. Filed by the domain:devx seat that lands #29474. ⛔ Not a claim. No grade: triage sets it.
All three are in packages/lint (validate-null-guards.ts, validate-expressions.ts) and its changeset prose, read as of PR #29474. Re-read them on main once that PR merges.
1. Build and runtime publish doors can disagree (runtime stricter)
At os validate, a start condition budget != null (bare) proves record.budget, so an edge comparing record.budget with an ordering operator passes. At the runtime publish gate the bare set is empty (perWriteSnapshotEntrance opens the scope), the presumption is not derived, and the same edge is refused. Deriving the presumption from a bare guard over an unprovable bound set would be unsound, so the stricter side is defensible, but a flow that os validate accepts and the publish gate refuses is a trap nobody records. Either record it in the ledger's flow row and the changeset's runtime-gate sentence, or have the runtime gate stand down on a presumption-dependent finding the way the entrance judgment does.
2. isAlwaysValuedSummary overstates the backfill
Its doc comment says the engine "backfills a stored NULL", which reads as automatic. The backfill is the one-off os migrate summary-nulls (summary-backfill.ts, #6063); initializeSummaryFields is create-time only. A parent from before #6013 that was never migrated still reads NULL, and on a validation rule that fault fails the write closed. The comment (and any doc that repeats it) should say the always-valued judgment for count / sum presumes the migration ran.
3. The != null hint is incomplete for previous.x on a create leg
The gate reports an unguarded previous.x and prescribes != null. On an insert, previous is bound to null (seedRunVariables), so previous.x != null itself faults; the full guard is previous != null && previous.x != null. The hint could name previous != null && for a previous. operand.
Done when
Each item is fixed in code or comment, or answered in the ledger with the reason it stands, and a test pins item 3's hint text if it changes.
Claim: PM loop round 3
Session: session_01JQwxbaBjisxkQo9hht9bFB
Account: os-warren
Branch: claude/project-thread-u2dp08
Worktree: objectstack-issue-29491
Domain: domain:devx
Seat: domain:devx#1
File surface: packages/lint/src/validate-null-guards.ts, packages/lint/src/validate-expressions.ts and their tests; .changeset/29026-flow-condition-null-guard.md (pending; an edit is a DELIBERATE CORRECTION) or a new @objectstack/lint patch changeset
Container & model: M lint accuracy, mode:thread, model: default tier (dispatch-gates --tier: no path-derived mandate), effort: default
Clause-②: no (item 1 is recorded, not narrowed; the runtime stand-down arm, if measured sound and taken, re-posts this line as a narrowing and owes contract review)
Metadata-protocol: none
Responsibility: three should-fixes from review 6582253324 on PR #29474: a flow os validate accepts and the publish gate refuses goes unrecorded; a doc comment reads the summary backfill as automatic; the != null hint for previous.x on a create leg names a guard that itself faults
Thread-read: 6584817890
Premise: holds (seat read on main208d3e2e: isAlwaysValuedSummary doc ~L503 says "backfills a stored NULL"; NULL_GUARD_HINT names != null only; the changeset's L15 says bare names are not judged at the runtime gate, without the consequence)
Serial constraints cleared: none (34 open non-mailbox PRs read; none touches the surface)
NULL_GUARD_HINT is verbatim parity with packages/objectql rule-validator, so item 3 adds a previous.-only clause and leaves the shared sentence alone. Lane order at pick: #29520 (p2, held for domain:devx#2 per the round-3 split), #29561 (p2, even, serial after PR #29556), #29491 (p3, odd).
{
"issue": 29491,
"status": "done",
"branch": "claude/project-thread-u2dp08",
"pr": "https://github.com/objectstack-ai/objectstack/pull/29582",
"session": "session_01JQwxbZNEUhFEaL9RRQWJRR",
"premise_still_valid": true,
"summary": "Premise held on main ff827bcf. Item 1 took the order's default arm (record, no runtime stand-down): the validate-null-guards.ts ledger flow row and wiring point 3 now say the runtime publish gate refuses an edge comparing record.budget when the start condition guards a BARE budget, because perWriteSnapshotEntrance leaves the bound set unprovable there, and that the record.budget spelling of the guard passes both doors; a new test pins both doors. No sound stand-down was measured, so none was taken; Clause-2 stays no. Item 2: the isAlwaysValuedSummary doc comment no longer says the engine backfills; it says the count/sum judgment presumes the one-off os migrate summary-nulls ran. Item 3: nullGuardMessage appends a module-private PREVIOUS_GUARD_CLAUSE after the unchanged NULL_GUARD_HINT for a previous.FIELD operand only, naming previous != null && previous.FIELD != null; it applies on every surface because rule-validator's previousBinding and the flow create leg both bind previous to null on an insert. Pending changeset 29026 is DELIBERATELY CORRECTED in two sentences (L15, L17; before/after in deliberate_correction); new patch changeset 29491-previous-guard-hint.md covers the message change.",
"deliberate_correction": [
"L15 before: At the runtime publish gate bare names are not judged.",
"L15 after: At the runtime publish gate bare names are not judged, so a bare start-condition guard proves nothing there: a flow whose start condition is `budget != null` and whose edge compares `record.budget > 200000` passes `os validate` and is refused by the runtime publish gate. Spell the start guard `record.budget != null` and both accept it.",
"L17 before: A `count` or `sum` roll-up summary is now always valued for this gate (zero children is zero), on validation rules and hook conditions too; `min` / `max` / `avg` roll-ups stay nullable.",
"L17 after: the same sentence, then: That presumes `os migrate summary-nulls` ran: on a database upgraded in place without it, a parent row stored before roll-ups were seeded at create time, and never given a child since, still reads `null`."
],
"tests": "At d4f21b71: pnpm --filter @objectstack/lint exec vitest run src/validate-null-guards.test.ts src/validate-expressions.flow-null-guards.test.ts: Test Files 2 passed, Tests 46 passed. pnpm --filter @objectstack/lint test: Test Files 192 passed (192), Tests 7882 passed, 5 skipped. pnpm --filter @objectstack/lint typecheck (tsc + check:test-typecheck): exit 0 (lock VERDICT command-exit 0). Ablation on the committed fix via scripts/ablation-replace.mjs with a trap restore: the clause expression replaced by an empty string (anchor x1 to x0, marker x0 to x1), validate-null-guards.test.ts went red, Tests 1 failed / 33 passed, the failing case being the new previous-operand one; restore proved by blob a4f2bae1 equal to HEAD and an empty git diff HEAD. Lint tests read src, so no dist rebuild leg applies. The item-1 test pins existing behaviour (no code change), so it has no ablation.",
"gates": "dispatch-gates --commands --repo objectstack-ai/objectstack at d4f21b71 (merge base ff827bcf): 58 derived. Prereqs: git fetch origin 621a4876; turbo run build --filter=@objectstack/lint... --filter=@objectstack/objectql (exit 0). 56 exit 0. check-empty-changeset --base origin/main exit 1: DELIBERATE CORRECTION class on the 29026 changeset, expected and stated in the PR body. check:dual-build-cjs-loads exit 3 PREREQUISITE NOT MET (needs every package built): NOT MEASURED, left to CI. --ran: 58 derived famil(ies) accounted for, 57 run, 1 NOT-MEASURED. CI check-runs: in_progress at report time; Check Changeset is expected red for the same deliberate correction.",
"dogfood_battery": "n/a, no ! in the PR title and no (narrowing) arm",
"mcp_calls": "0",
"api_writes": "2 relay writes: pr_create (fleet-write run 41514615435, read-back 9235 bytes sent and stored, identical) and this post-stamped comment on #29491. Plus git push of the branch. REST reads only otherwise.",
"body_amendments": "none",
"deviations": [
"origin/main moved six commits past the merge base while the gates ran; none touches packages/lint, packages/formula, packages/objectql or the 29026 changeset, and git merge-tree is clean. Not merged in before pr_create; CI judges the merge ref.",
"Session context asks for a model-named Co-Authored-By trailer; the commit uses the model-free pair AGENTS.md requires.",
"The PR body carries the seat's Thread-read and Claim ids on a line spelled Dispatch: so no PR-body line begins with Claim:."
],
"open_questions": [],
"security_findings": "none",
"out_of_scope_findings": []
}
Landed · 2026-11-02T21:21Z · PR #29582 merged as e7d971d451 (reviewed head d4f21b7167, contract review PASS 6587299006 with Regen-provenance 6587360221 to 7bf8e16f67, seat record ACCEPT 6587381095). Item 1 is recorded, not narrowed: the ledger and the pending 29026 changeset now say that the runtime publish gate refuses an edge leaning on a bare start-condition guard that os validate accepts (where the flow's bound set is provable), and that record.budget != null passes both doors. A test pins both doors. Item 2: the count/sum always-valued judgment now says it presumes os migrate summary-nulls ran. Item 3: a previous.FIELD operand's message adds the previous != null && previous.FIELD != null guard after the unchanged shared hint, pinned by a test. Check Changeset merged red by design (DELIBERATE CORRECTION, confirmed in the record and the ACCEPT). pm:dispatched is removed in this stroke.
Activity
objectstack-fleet commentedon Nov 2, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: p3 ·
domain:devx·pm:blocked· 2026-11-02T16:25Z⛔ Not a claim.
!= nullhint is incomplete forprevious.xon a create leg.Blocked-by: #29026. Re-read onmainafter PR feat(lint): the publish-time null-guard gate judges a record-triggered flow's start, edge and decision-branch conditions #29474 merges.domain:devx, the seat landing feat(lint): the publish-time null-guard gate judges a record-triggered flow's start, edge and decision-branch conditions #29474 in the same files, while the maintainer rules on thepackages/lintrouting conflict (lanes/server.mdL18 against the SKILL.md table, reported in R843). If the ruling gives spec-contract lint rules todomain:spec, this card moves with that ruling.Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: unblocked ·
pm:queue(p3 kept) · 2026-11-02T18:18Z⛔ Not a claim.
Blocked-by: #29026is closed.pm:blocked→pm:queue.Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
domain:devx#1 · os-warren · session_01JQwxbaBjisxkQo9hht9bFB
Claim: PM loop round 3
Session:
session_01JQwxbaBjisxkQo9hht9bFBAccount:
os-warrenBranch:
claude/project-thread-u2dp08Worktree:
objectstack-issue-29491Domain:
domain:devxSeat:
domain:devx#1File surface:
packages/lint/src/validate-null-guards.ts,packages/lint/src/validate-expressions.tsand their tests;.changeset/29026-flow-condition-null-guard.md(pending; an edit is a DELIBERATE CORRECTION) or a new@objectstack/lintpatch changesetContainer & model:
M lint accuracy,mode:thread,model: default tier (dispatch-gates --tier: no path-derived mandate),effort: defaultClause-②: no (item 1 is recorded, not narrowed; the runtime stand-down arm, if measured sound and taken, re-posts this line as a narrowing and owes contract review)
Metadata-protocol: none
Responsibility: three should-fixes from review 6582253324 on PR #29474: a flow
os validateaccepts and the publish gate refuses goes unrecorded; a doc comment reads the summary backfill as automatic; the!= nullhint forprevious.xon a create leg names a guard that itself faultsThread-read: 6584817890
Premise: holds (seat read on
main208d3e2e:isAlwaysValuedSummarydoc ~L503 says "backfills a storedNULL";NULL_GUARD_HINTnames!= nullonly; the changeset's L15 says bare names are not judged at the runtime gate, without the consequence)Serial constraints cleared: none (34 open non-mailbox PRs read; none touches the surface)
NULL_GUARD_HINTis verbatim parity withpackages/objectqlrule-validator, so item 3 adds aprevious.-only clause and leaves the shared sentence alone. Lane order at pick: #29520 (p2, held for domain:devx#2 per the round-3 split), #29561 (p2, even, serial after PR #29556), #29491 (p3, odd).objectstack-fleet commentedon Nov 2, 2026
domain:devx#1 · os-warren · session_01JQwxbZNEUhFEaL9RRQWJRR
os-dev-report
{ "issue": 29491, "status": "done", "branch": "claude/project-thread-u2dp08", "pr": "https://github.com/objectstack-ai/objectstack/pull/29582", "session": "session_01JQwxbZNEUhFEaL9RRQWJRR", "premise_still_valid": true, "summary": "Premise held on main ff827bcf. Item 1 took the order's default arm (record, no runtime stand-down): the validate-null-guards.ts ledger flow row and wiring point 3 now say the runtime publish gate refuses an edge comparing record.budget when the start condition guards a BARE budget, because perWriteSnapshotEntrance leaves the bound set unprovable there, and that the record.budget spelling of the guard passes both doors; a new test pins both doors. No sound stand-down was measured, so none was taken; Clause-2 stays no. Item 2: the isAlwaysValuedSummary doc comment no longer says the engine backfills; it says the count/sum judgment presumes the one-off os migrate summary-nulls ran. Item 3: nullGuardMessage appends a module-private PREVIOUS_GUARD_CLAUSE after the unchanged NULL_GUARD_HINT for a previous.FIELD operand only, naming previous != null && previous.FIELD != null; it applies on every surface because rule-validator's previousBinding and the flow create leg both bind previous to null on an insert. Pending changeset 29026 is DELIBERATELY CORRECTED in two sentences (L15, L17; before/after in deliberate_correction); new patch changeset 29491-previous-guard-hint.md covers the message change.", "deliberate_correction": [ "L15 before: At the runtime publish gate bare names are not judged.", "L15 after: At the runtime publish gate bare names are not judged, so a bare start-condition guard proves nothing there: a flow whose start condition is `budget != null` and whose edge compares `record.budget > 200000` passes `os validate` and is refused by the runtime publish gate. Spell the start guard `record.budget != null` and both accept it.", "L17 before: A `count` or `sum` roll-up summary is now always valued for this gate (zero children is zero), on validation rules and hook conditions too; `min` / `max` / `avg` roll-ups stay nullable.", "L17 after: the same sentence, then: That presumes `os migrate summary-nulls` ran: on a database upgraded in place without it, a parent row stored before roll-ups were seeded at create time, and never given a child since, still reads `null`." ], "tests": "At d4f21b71: pnpm --filter @objectstack/lint exec vitest run src/validate-null-guards.test.ts src/validate-expressions.flow-null-guards.test.ts: Test Files 2 passed, Tests 46 passed. pnpm --filter @objectstack/lint test: Test Files 192 passed (192), Tests 7882 passed, 5 skipped. pnpm --filter @objectstack/lint typecheck (tsc + check:test-typecheck): exit 0 (lock VERDICT command-exit 0). Ablation on the committed fix via scripts/ablation-replace.mjs with a trap restore: the clause expression replaced by an empty string (anchor x1 to x0, marker x0 to x1), validate-null-guards.test.ts went red, Tests 1 failed / 33 passed, the failing case being the new previous-operand one; restore proved by blob a4f2bae1 equal to HEAD and an empty git diff HEAD. Lint tests read src, so no dist rebuild leg applies. The item-1 test pins existing behaviour (no code change), so it has no ablation.", "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack at d4f21b71 (merge base ff827bcf): 58 derived. Prereqs: git fetch origin 621a4876; turbo run build --filter=@objectstack/lint... --filter=@objectstack/objectql (exit 0). 56 exit 0. check-empty-changeset --base origin/main exit 1: DELIBERATE CORRECTION class on the 29026 changeset, expected and stated in the PR body. check:dual-build-cjs-loads exit 3 PREREQUISITE NOT MET (needs every package built): NOT MEASURED, left to CI. --ran: 58 derived famil(ies) accounted for, 57 run, 1 NOT-MEASURED. CI check-runs: in_progress at report time; Check Changeset is expected red for the same deliberate correction.", "dogfood_battery": "n/a, no ! in the PR title and no (narrowing) arm", "mcp_calls": "0", "api_writes": "2 relay writes: pr_create (fleet-write run 41514615435, read-back 9235 bytes sent and stored, identical) and this post-stamped comment on #29491. Plus git push of the branch. REST reads only otherwise.", "body_amendments": "none", "deviations": [ "origin/main moved six commits past the merge base while the gates ran; none touches packages/lint, packages/formula, packages/objectql or the 29026 changeset, and git merge-tree is clean. Not merged in before pr_create; CI judges the merge ref.", "Session context asks for a model-named Co-Authored-By trailer; the commit uses the model-free pair AGENTS.md requires.", "The PR body carries the seat's Thread-read and Claim ids on a line spelled Dispatch: so no PR-body line begins with Claim:." ], "open_questions": [], "security_findings": "none", "out_of_scope_findings": [] }objectstack-fleet commentedon Nov 2, 2026
domain:devx#1 · os-warren · session_01JQwxbaBjisxkQo9hht9bFB
Landed · 2026-11-02T21:21Z · PR #29582 merged as
e7d971d451(reviewed headd4f21b7167, contract review PASS 6587299006 with Regen-provenance 6587360221 to7bf8e16f67, seat record ACCEPT 6587381095). Item 1 is recorded, not narrowed: the ledger and the pending 29026 changeset now say that the runtime publish gate refuses an edge leaning on a bare start-condition guard thatos validateaccepts (where the flow's bound set is provable), and thatrecord.budget != nullpasses both doors. A test pins both doors. Item 2: the count/sum always-valued judgment now says it presumesos migrate summary-nullsran. Item 3: aprevious.FIELDoperand's message adds theprevious != null && previous.FIELD != nullguard after the unchanged shared hint, pinned by a test.Check Changesetmerged red by design (DELIBERATE CORRECTION, confirmed in the record and the ACCEPT).pm:dispatchedis removed in this stroke.