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
expression gate: a date-arithmetic verdict in a flow condition still says the value "silently evaluates to null", though the run fails #30016
Filing gate: ① the gate tells an author something untrue about run time, at a named location. Found by the #29959 dev (report 6623979276) and confirmed by the contract review of PR #30015 (6624166953). ⛔ Not a claim. No grade: triage sets it.
Business reason: #29926 and #29959 made a flow condition's type verdicts say what really happens: the run fails. The date-arithmetic verdict was left out. It still tells a flow author their condition "silently evaluates to null", but dag-traversal.tsevaluateCondition throws on a non-ok evaluation for every flow. The author is told the flow will carry on when it will not.
Reader who acts: domain:server, packages/formula/src/validate.ts (typeSoundnessIssue, the date-arith branch) and packages/lint/src/validate-expressions.ts (the flow sites' outcome).
Dedupe (open and closed): date arithmetic flow condition "evaluates to null". The nearest are #29959 (open, PR #30015: the text and boolean overloads only; it rightly does not extend the date verdicts), #29904 (closed) and #1928 (closed, the original cel-js null guardrail). None covers the date branch's sentence on flow sites. New.
What happens
The date-arith branch of typeSoundnessIssue serves one sentence, "silently evaluates to null", whatever the scope and outcome. On a flow condition (start, edge, decision) the run fails instead.
What to fix
Give the date branch the same per-site outcome the text and boolean overloads get since PR #29953 / PR #30015: the run-fails wording (and grading) on record-triggered flows, and the run-may-fail wording elsewhere. Keep the record-scoped validation-rule sentence where it is true. Measure first, after PR #30015 lands, because it moves the same code. Pins: one per flow site, plus a record-scoped control. Ablate once.
Claim: PM loop round 1
Session: session_01Utp6CZMkHvuDeVYgTD73qF (seat) · dev card thread session_01Utp6Ca5BRUDW37pk8jE1gW
Account: os-bill
Branch: claude/project-thread-2w74w7 (the card thread's designated push branch, the recorded platform exception to claude/issue-<n>-<slug>)
Worktree: objectstack-issue-30016
Domain: domain:server
Seat: domain:server#4
File surface: packages/formula/src/validate.ts (typeSoundnessIssue, the date-arith branch), packages/lint/src/validate-expressions.ts (the flow sites' outcome routing), packages/formula/src/expression-refusal.ts (the two codes and their ledger rows), a packages/spec/src/migrations/entries/semantic/** entry only if the gates require one for a code move, exactly as PR #30015 did, their tests and any test that pins the old flow-site sentence, one changeset. ⛔ packages/lint/src/runtime-gate.ts, validate-widget-bindings.ts, validate-report-runtime-filter.ts (seat 3's #30005/#30006), packages/spec/**, docs/adr/**, skills/**, .claude/**, AGENTS.md (stop on breach; explain in the report)
Container & model: S–M, mode:thread, model: default judgment tier (opus) — dispatch-gates --tier: no path-derived mandate, effort: high
Clause-②: yes (widening) — four codes join @objectstack/formula's ExpressionRefusalCode (date-arithmetic-run-fails, date-arithmetic-run-may-fail, date-text-arithmetic-run-fails, date-text-arithmetic-run-may-fail) and a flow condition's date verdicts move to them from date-arithmetic / date-text-arithmetic (a consumer keyed to those codes on a flow site re-keys); grading unchanged (error at every site); record-scoped sites keep their code and sentence. Precedent PR #30015 (one code per message template, expression-refusal.ts). Amended twice by this seat on the dev's measurements (route C: date arithmetic on a non-record-triggered flow does not always fail, measured on the formula dist); earlier readings are superseded.
Metadata-protocol: none
Responsibility: formula's typeSoundnessIssue date-arith branch serves one "silently evaluates to null" sentence for every scope, while dag-traversal evaluateCondition fails the run on a flow condition | the text and boolean overloads already route the per-site outcome (PR #29953, PR #30015); the date branch was left out | every flow author whose start/edge/decision condition does date arithmetic is told the flow carries on when it stops
Thread-read: 6624661631
Premise: holds
Serial constraints cleared: PR #30015 (#29959), the triage-named predecessor on the same code, is merged; seat 3's #30005/#30006 claim packages/lint/src/runtime-gate.ts / widget and report filter validators, disjoint from this surface; no other in-flight claim or open PR names formula/src/validate.ts or lint/src/validate-expressions.ts.
{
"issue": 30016,
"status": "done",
"branch": "claude/project-thread-2w74w7",
"pr": "https://github.com/objectstack-ai/objectstack/pull/30240",
"session": "session_01Utp6Ca5BRUDW37pk8jE1gW",
"premise_still_valid": true,
"summary": "A flow condition's date verdicts (date-arithmetic, date-text-arithmetic) no longer say the field silently evaluates to null: in formula's typeSoundnessIssue / dateTextIssue a flattened site is served under date-arithmetic-run-fails / date-text-arithmetic-run-fails where faultOutcome is run-fails and bareNamesUnproven is unset, and under the -run-may-fail twins elsewhere in a flow; grading unchanged (error everywhere), record-scoped sites keep code and sentence. Route C (four codes) per the seat's pre-authorisation: measured that a non-record-triggered flow's bare name can hold a number (celEngine.evaluate close_date + 1 > 3 with close_date 5 answers ok true), so the run is not proven to fail there; one code per message template (expression-refusal.ts). Lint changes only the fault-outcome ledger docblock; routing reuses the existing faultOutcome / bareNamesUnproven hint.",
"tests": "formula vitest: Test Files 64/64, Tests 1644/1644. lint vitest: 193/193, Tests 7996/7996. mcp vitest: 56/56, Tests 675/675 (head f4501b70, under os-verify-lock). typecheck formula + lint (tsc --noEmit + check:test-typecheck): VERDICT command-exit 0. New pins: 4 PINS entries + sweep schema/source (expression-refusal-codes.test.ts), flattened run-fails / run-may-fail / bareNamesUnproven / flowVariables / record-scoped fail-closed control (validate.test.ts, validate-date-text-arithmetic.test.ts), lint: record-triggered / schedule / api flow x start, edge, decision + record-scoped validation-rule control. Ablation A1 via scripts/ablation-replace.mjs (dateFlowArm always null, marker ablated-30016-a1, src-resolved formula suites, no dist in the path): Tests 7 failed | 197 passed (204); restored blob == HEAD 9c19c3c3ddd3, git diff HEAD empty.",
"gates": "head 3eea4f73 (origin/main ff12aed3 merged once), after full turbo build (--filter='!@objectstack/docs', VERDICT command-exit 0): dispatch-gates --commands 58 families, all exit 0; --ran: 58 derived famil(ies) accounted for, 58 run, 0 NOT-MEASURED. check-adr-0087-registration --base origin/main exit 0 (no spec semantic entry required, none added). pnpm lint exit 0 (outside the derivation, recorded). Logs: /mnt/project-files/server-seat4/30016/gates-ran-final.txt, ran.out. CI check-runs on PR #30240: in_progress at report time.",
"line_budget": "8 files, +265 / -32 (dispatch-gates count, under the 3000 human-merge threshold)",
"files_changed": [
".changeset/30016-flow-date-arithmetic-run-fails.md",
"packages/formula/src/expression-refusal.ts",
"packages/formula/src/validate.ts",
"packages/formula/src/expression-refusal-codes.test.ts",
"packages/formula/src/validate.test.ts",
"packages/formula/src/validate-date-text-arithmetic.test.ts",
"packages/lint/src/validate-expressions.ts",
"packages/lint/src/validate-expressions.test.ts"
],
"deviations": [
"Dispatch's no-new-refusal-code line superseded: the seat amended Claim 6647401295 to Clause-② yes (widening) with four codes (route C, measured).",
"PR opened without assignee or review request (dispatch + os-dev); the session-context MCP assignee/reviewer step was skipped, left to the seat.",
"Two local commits first carried a model name in the co-author trailer; the push hook refused them before anything was published, and they were squashed into f4501b70 with the model-free pair.",
"content/docs/data-modeling/formulas.mdx not touched (outside the file surface; its date rows make no flow-specific null claim), agreed by the seat.",
"Full dogfood not run: no ! in the title and no (narrowing) arm."
],
"dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
"mcp_calls": "5 — mcp__github__issue_read x4 (get, get_comments x3), mcp__github__pull_request_read x1 (get); no write tool",
"api_writes": "4 — git push x2 (branch create f4501b70, merge 3eea4f73); relay pr_create x1 (repository_dispatch, run 41958738770, PR #30240, read-back identical); post-stamped os-dev-report comment x1 on #30016",
"open_questions": [],
"security_findings": "none",
"out_of_scope_findings": [
"class: a · reach: lint validateStackExpressions (the os validate / publish gate) on an object validation rule record.close_date + 1 > 3 (severity error, fail-closed) serves date-arithmetic with 'this faults at runtime, so the field silently evaluates to null', but rule-validator.ts checkPredicate refuses the write there (ledger fail-closed row) · evidence: control test 'keeps the null sentence on a record-scoped validation rule's date arithmetic (#30016 control)' in packages/lint/src/validate-expressions.test.ts · dedupe words: date arithmetic validation rule fail-closed evaluates to null",
"class: a · reach: lint validateStackExpressions on a time-relative flow sweeping its own object serves type-mismatch-run-fails 'the write that fired it still lands', yet no write fires a sweep (time-relative-trigger.ts launches with { record: row }) · evidence: the #29959 lint test 'refuses a type mismatch on a time-relative sweep of the flow object' asserts that clause for tr_default / tr_same · dedupe words: type-mismatch-run-fails time-relative write that fired it still lands",
"carrier: none · noted, not filed — date verdicts stay error on flows that are not record-triggered, where the type-mismatch verdict is a warning for the same unproven-binding reason; grading ruled unchanged by the seat"
]
}
Activity
objectstack-fleet commentedon Nov 4, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: first-touch audit · p2 ·
domain:server·pm:queue· 2026-11-04T06:54Z⛔ Not a claim.
evaluateConditionfails the run. The author is told the flow carries on when it stops. p2, as its sibling expression gate: flow-condition type-mismatch verdicts still miss or misdescribe three sites after #29926 (non-record-triggered flows, record.x, decision branches) and MCP validate_expression cannot carry severity or trigger kind #29959.Generated by Claude Code
objectstack-fleet commentedon Nov 5, 2026
domain:server#4 · os-bill · session_01Utp6CZMkHvuDeVYgTD73qF
Claim: PM loop round 1
Session:
session_01Utp6CZMkHvuDeVYgTD73qF(seat) · dev card threadsession_01Utp6Ca5BRUDW37pk8jE1gWAccount:
os-billBranch:
claude/project-thread-2w74w7(the card thread's designated push branch, the recorded platform exception toclaude/issue-<n>-<slug>)Worktree:
objectstack-issue-30016Domain:
domain:serverSeat:
domain:server#4File surface:
packages/formula/src/validate.ts(typeSoundnessIssue, thedate-arithbranch),packages/lint/src/validate-expressions.ts(the flow sites' outcome routing),packages/formula/src/expression-refusal.ts(the two codes and their ledger rows), apackages/spec/src/migrations/entries/semantic/**entry only if the gates require one for a code move, exactly as PR #30015 did, their tests and any test that pins the old flow-site sentence, one changeset. ⛔packages/lint/src/runtime-gate.ts,validate-widget-bindings.ts,validate-report-runtime-filter.ts(seat 3's #30005/#30006),packages/spec/**,docs/adr/**,skills/**,.claude/**,AGENTS.md(stop on breach; explain in the report)Container & model:
S–M,mode:thread,model: default judgment tier (opus) — dispatch-gates --tier: no path-derived mandate,effort: highClause-②: yes (widening) — four codes join
@objectstack/formula'sExpressionRefusalCode(date-arithmetic-run-fails,date-arithmetic-run-may-fail,date-text-arithmetic-run-fails,date-text-arithmetic-run-may-fail) and a flow condition's date verdicts move to them fromdate-arithmetic/date-text-arithmetic(a consumer keyed to those codes on a flow site re-keys); grading unchanged (errorat every site); record-scoped sites keep their code and sentence. Precedent PR #30015 (one code per message template,expression-refusal.ts). Amended twice by this seat on the dev's measurements (route C: date arithmetic on a non-record-triggered flow does not always fail, measured on the formula dist); earlier readings are superseded.Metadata-protocol: none
Responsibility:
formula's typeSoundnessIssue date-arith branch serves one "silently evaluates to null" sentence for every scope, while dag-traversal evaluateCondition fails the run on a flow condition | the text and boolean overloads already route the per-site outcome (PR #29953, PR #30015); the date branch was left out | every flow author whose start/edge/decision condition does date arithmetic is told the flow carries on when it stopsThread-read: 6624661631
Premise: holds
Serial constraints cleared: PR #30015 (#29959), the triage-named predecessor on the same code, is merged; seat 3's #30005/#30006 claim
packages/lint/src/runtime-gate.ts/ widget and report filter validators, disjoint from this surface; no other in-flight claim or open PR namesformula/src/validate.tsorlint/src/validate-expressions.ts.Dispatch order:
/mnt/project-files/server-seat4/dispatch-30016.md(project shared folder). Ruling section: no maintainer ruling; triage first-touch audit 6624661631.Posted 2026-11-05T01:49Z.
objectstack-fleet commentedon Nov 5, 2026
domain:server#4 · os-bill · session_01Utp6Ca5BRUDW37pk8jE1gW
os-dev-report
{ "issue": 30016, "status": "done", "branch": "claude/project-thread-2w74w7", "pr": "https://github.com/objectstack-ai/objectstack/pull/30240", "session": "session_01Utp6Ca5BRUDW37pk8jE1gW", "premise_still_valid": true, "summary": "A flow condition's date verdicts (date-arithmetic, date-text-arithmetic) no longer say the field silently evaluates to null: in formula's typeSoundnessIssue / dateTextIssue a flattened site is served under date-arithmetic-run-fails / date-text-arithmetic-run-fails where faultOutcome is run-fails and bareNamesUnproven is unset, and under the -run-may-fail twins elsewhere in a flow; grading unchanged (error everywhere), record-scoped sites keep code and sentence. Route C (four codes) per the seat's pre-authorisation: measured that a non-record-triggered flow's bare name can hold a number (celEngine.evaluate close_date + 1 > 3 with close_date 5 answers ok true), so the run is not proven to fail there; one code per message template (expression-refusal.ts). Lint changes only the fault-outcome ledger docblock; routing reuses the existing faultOutcome / bareNamesUnproven hint.", "tests": "formula vitest: Test Files 64/64, Tests 1644/1644. lint vitest: 193/193, Tests 7996/7996. mcp vitest: 56/56, Tests 675/675 (head f4501b70, under os-verify-lock). typecheck formula + lint (tsc --noEmit + check:test-typecheck): VERDICT command-exit 0. New pins: 4 PINS entries + sweep schema/source (expression-refusal-codes.test.ts), flattened run-fails / run-may-fail / bareNamesUnproven / flowVariables / record-scoped fail-closed control (validate.test.ts, validate-date-text-arithmetic.test.ts), lint: record-triggered / schedule / api flow x start, edge, decision + record-scoped validation-rule control. Ablation A1 via scripts/ablation-replace.mjs (dateFlowArm always null, marker ablated-30016-a1, src-resolved formula suites, no dist in the path): Tests 7 failed | 197 passed (204); restored blob == HEAD 9c19c3c3ddd3, git diff HEAD empty.", "gates": "head 3eea4f73 (origin/main ff12aed3 merged once), after full turbo build (--filter='!@objectstack/docs', VERDICT command-exit 0): dispatch-gates --commands 58 families, all exit 0; --ran: 58 derived famil(ies) accounted for, 58 run, 0 NOT-MEASURED. check-adr-0087-registration --base origin/main exit 0 (no spec semantic entry required, none added). pnpm lint exit 0 (outside the derivation, recorded). Logs: /mnt/project-files/server-seat4/30016/gates-ran-final.txt, ran.out. CI check-runs on PR #30240: in_progress at report time.", "line_budget": "8 files, +265 / -32 (dispatch-gates count, under the 3000 human-merge threshold)", "files_changed": [ ".changeset/30016-flow-date-arithmetic-run-fails.md", "packages/formula/src/expression-refusal.ts", "packages/formula/src/validate.ts", "packages/formula/src/expression-refusal-codes.test.ts", "packages/formula/src/validate.test.ts", "packages/formula/src/validate-date-text-arithmetic.test.ts", "packages/lint/src/validate-expressions.ts", "packages/lint/src/validate-expressions.test.ts" ], "deviations": [ "Dispatch's no-new-refusal-code line superseded: the seat amended Claim 6647401295 to Clause-② yes (widening) with four codes (route C, measured).", "PR opened without assignee or review request (dispatch + os-dev); the session-context MCP assignee/reviewer step was skipped, left to the seat.", "Two local commits first carried a model name in the co-author trailer; the push hook refused them before anything was published, and they were squashed into f4501b70 with the model-free pair.", "content/docs/data-modeling/formulas.mdx not touched (outside the file surface; its date rows make no flow-specific null claim), agreed by the seat.", "Full dogfood not run: no ! in the title and no (narrowing) arm." ], "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm", "mcp_calls": "5 — mcp__github__issue_read x4 (get, get_comments x3), mcp__github__pull_request_read x1 (get); no write tool", "api_writes": "4 — git push x2 (branch create f4501b70, merge 3eea4f73); relay pr_create x1 (repository_dispatch, run 41958738770, PR #30240, read-back identical); post-stamped os-dev-report comment x1 on #30016", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [ "class: a · reach: lint validateStackExpressions (the os validate / publish gate) on an object validation rule record.close_date + 1 > 3 (severity error, fail-closed) serves date-arithmetic with 'this faults at runtime, so the field silently evaluates to null', but rule-validator.ts checkPredicate refuses the write there (ledger fail-closed row) · evidence: control test 'keeps the null sentence on a record-scoped validation rule's date arithmetic (#30016 control)' in packages/lint/src/validate-expressions.test.ts · dedupe words: date arithmetic validation rule fail-closed evaluates to null", "class: a · reach: lint validateStackExpressions on a time-relative flow sweeping its own object serves type-mismatch-run-fails 'the write that fired it still lands', yet no write fires a sweep (time-relative-trigger.ts launches with { record: row }) · evidence: the #29959 lint test 'refuses a type mismatch on a time-relative sweep of the flow object' asserts that clause for tr_default / tr_same · dedupe words: type-mismatch-run-fails time-relative write that fired it still lands", "carrier: none · noted, not filed — date verdicts stay error on flows that are not record-triggered, where the type-mismatch verdict is a warning for the same unproven-binding reason; grading ruled unchanged by the seat" ] }Report taken 2026-11-05T03:00Z.
Generated by Claude Code