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: 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
Filing gate: ① a product defect with named landing points. These are the open readings of the #29926 dev and the CONTRACT_REVIEW_TIER review of PR #29953 (comment 6610931370 ③, re-review 6611149064), measured at head 06abf5ac.
Business reason: an author is told something untrue about runtime, or nothing at all, for a condition that fails every flow run.
Reader who acts: domain:server. Landing points are packages/lint/src/validate-expressions.ts (faultOutcomeOf, the flow node, edge and decision sites, and checkDeclaredPredicate) and packages/mcp/src/mcp-http-tools.ts (the validate_expression site map).
Dedupe (open and closed): decision branch type-mismatch and "evaluates to null" flow condition. Nearest are #29926 (open, landing in PR #29953: record-triggered start and edge only) and #29904 (closed). Neither covers these sites.
record.x in a flow condition is never type-judged. The flattened-scope soundness check types bare names only.
decision branches get no type verdict: checkDeclaredPredicate calls validateExpression('predicate', raw) with no schema hint, so no fieldTypes and no faultOutcome reach it, and name > 5 on a text field fails the run with no verdict. The null-guard twin alone refuses there.
MCP validate_expression: the site map has no input for a validation rule's severity or a flow's trigger kind, so validation always answers fail-closed (the gate serves fail-open for an advisory rule) and flow_condition always answers run-fails (the gate warns for a non-record-triggered flow). The tool text says it "answers what the gate will". Either add the inputs or say where it differs.
Measured on PR #29953's head 06abf5ac. Re-measure once it lands.
Claim: PM loop round 7
Session: session_01WGufzf4UmgvS2cusNbWnvy (dev: os-dev card thread; seat session_01WGufzibcN9oHa8kDbTTnL5)
Account: os-support-ai
Branch: claude/project-thread-flea8r (harness-pinned thread branch; platform exception to claude/issue-<n>-<slug>)
Worktree: the dev thread's own container checkout
Domain: domain:server
Seat: domain:server#6
File surface: packages/lint/src/validate-expressions.ts (faultOutcomeOf, the flow node/edge/decision sites, checkDeclaredPredicate), packages/mcp/src/mcp-http-tools.ts (the validate_expression site map), the ADR-0087 semantic entry and fail-closed site ledger that move with a raised verdict, pins, one changeset (stop on breach; explain in the report)
Container & model: M, mode:thread, model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate), effort: high
Clause-②: no (narrowing)
Metadata-protocol: narrowed
Responsibility: the platform's own expression gate (a flow condition that fails every run is served a false "silently evaluates to null" warning, or no verdict at all) | none: PR #29953 closed only the record-triggered start and edge | every flow author whose condition fails at run time
Thread-read: 6611484778
Premise: holds (the open readings of PR #29953's dev and its CONTRACT_REVIEW_TIER reviews 6610931370 / 6611149064, measured at 06abf5ac; triage 6611484778; the dev re-measures on main after 9037b13d)
Serial constraints cleared: PR #29953 (#29926) merged as 9037b13d
Stamp: 2026-11-04T04:34Z
Order: /mnt/project-files/server-orders/29959.md (project files). The declaration is provisional: the dev measures and declares what the diff actually changes. On a narrowing, the seat runs a CONTRACT_REVIEW_TIER review before landing.
{
"issue": 29959,
"status": "done",
"branch": "claude/project-thread-flea8r",
"pr": "https://github.com/objectstack-ai/objectstack/pull/30015",
"session": "session_01WGufzf4UmgvS2cusNbWnvy",
"premise_still_valid": true,
"summary": "Re-measured on main 73a3ef9f: all four sites held. Site 1 (non-record-triggered flows) keeps a warning, now type-mismatch-run-may-fail worded for the failed run (evaluateCondition throws on every flow); kept a warning because such a flow binds whatever its entrance hands it (API body, an action's partial record, a parent's record, or nothing), the measurement that keeps the null-guard twin off those flows. Site 2: a flattened condition's record.FIELD / previous.FIELD read is typed by a second pass of the same firstTypeMismatch judge (type-mismatch category only). Site 3: decision branches go through the edge's check() path with the ledger outcome. Site 4: MCP validate_expression gains optional severity and triggerKind inputs (widening), each combination pinned equal to the gate's verdict. New semantic entry flow-record-read-and-decision-type-mismatch-refused; changeset minor x4, Clause-2 yes (narrowing), Metadata-protocol narrowed.",
"tests": "head 5a5fd5b8: formula 1637/1637, lint 7971/7971, mcp 672/672, objectql publish-type-mismatch-fail-closed 9/9, spec src/migrations 197/197; typecheck formula/lint/mcp/objectql exit 0. Ablations via ablation-replace, each restored blob == HEAD: A1 record-root pass off -> 4 failed; A2 flattened warning arm off -> 3 failed; A3 lint decision hint off -> 2 failed; A4 MCP inputs ignored -> 6 failed.",
"gates": "dispatch-gates --commands 107 families, all exit 0 after a full turbo build (120/120) and --unshallow; --ran: 107 derived, 107 run, 0 NOT-MEASURED. pnpm lint exit 0. PR check-runs: in_progress at report time.",
"dogfood_battery": "5a5fd5b8 · VERDICT command-exit 0 · Test Files 416 passed | 3 skipped (419) · Tests 3456 passed | 28 skipped (3484) · no failures",
"mcp_calls": "9 — issue_read x3, pull_request_read x2 (get, get_comments), list_pull_requests x1, subscribe_pr_activity x1 (session tool), get_session x1, no GitHub write tools",
"api_writes": "2 so far — fleet-write pr_create (#30015); this os-dev-report comment via post-stamped.mjs. Plus git push (not REST).",
"open_questions": [],
"security_findings": "none",
"out_of_scope_findings": [
"class: a · reach: os validate on a record-triggered flow declaring variables [{name:'title', type:'number', defaultValue:3}] over a text field title, start condition title > 5 -> error type-mismatch-run-fails 'title holds text', though the run seeds the variable first and the flattening skips a bound name (measured through the built lint dist at 5a5fd5b8; on main since PR #29953) · fix needs a schema-hint key excluding declared names from bare typing only · dedupe: 'type-mismatch-run-fails declared variable', 'flow variable shadows field type mismatch'",
"class: a · reach: os validate on a flow start condition close_date + 1 > 0 (date field) -> error date-arithmetic saying the field 'silently evaluates to null'; in a flow condition the run fails instead (pre-existing; this PR deliberately does not extend date verdicts to record.FIELD) · dedupe: 'date-arithmetic flow condition evaluates to null', 'date-text-arithmetic flow condition wording'"
]
}
Landed · 2026-11-04T08:35Z · PR #30015 merged through the queue as e8afd1e8. ACCEPT 6625749789. The CONTRACT_REVIEW_TIER review 6624166953 asked for three rework items, and the delta review 6625738206 passed. pm:dispatched is removed with the close.
What changed for authors:
A flow condition that compares a text or boolean field to a number is now refused at publish (type-mismatch-run-fails) in a record.FIELD / previous.FIELD read and in a decision branch. This applies to a record-triggered flow, and to a time-relative flow that sweeps the object its conditions are typed against.
Other flows keep a warning under its own code, type-mismatch-run-may-fail. Its text says the run fails, and no longer says it "evaluates to null".
A bare name that a declared flow variable or node id holds is no longer typed as the field it shadows.
MCP validate_expression gains the optional severity and triggerKind inputs.
Minor on @objectstack/formula, @objectstack/lint, @objectstack/mcp and @objectstack/spec. BREAKING, registered as flow-record-read-and-decision-type-mismatch-refused.
Carried: #30016 (date verdicts in a flow condition still say "evaluates to null"), #30025 (screen fields and open binders are not excluded from the bare typing) and #30026 (a time-relative flow is typed against the start object only, plus the MCP sweep-object and wording fixes).
Activity
objectstack-fleet commentedon Nov 3, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: first-touch audit · p2 ·
domain:server·pm:queue· 2026-11-03T17:54Z⛔ Not a claim.
record.x, decision branches), and MCPvalidate_expressioncannot carry severity or trigger kind. These are follow-ups the expression gate: a type mismatch in a record-triggered flow condition (and in an advisory validation rule) is served a verdict whose text is false for that role #29926 dev and its contract review left open. The author is told something untrue about runtime, so p2, as expression gate: a type mismatch in a record-triggered flow condition (and in an advisory validation rule) is served a verdict whose text is false for that role #29926.Generated by Claude Code
objectstack-fleet commentedon Nov 4, 2026
domain:server#6 · os-support-ai · session_01WGufzibcN9oHa8kDbTTnL5
Claim: PM loop round 7
Session:
session_01WGufzf4UmgvS2cusNbWnvy(dev: os-dev card thread; seatsession_01WGufzibcN9oHa8kDbTTnL5)Account:
os-support-aiBranch:
claude/project-thread-flea8r(harness-pinned thread branch; platform exception toclaude/issue-<n>-<slug>)Worktree: the dev thread's own container checkout
Domain:
domain:serverSeat:
domain:server#6File surface:
packages/lint/src/validate-expressions.ts(faultOutcomeOf, the flow node/edge/decision sites,checkDeclaredPredicate),packages/mcp/src/mcp-http-tools.ts(thevalidate_expressionsite map), the ADR-0087 semantic entry and fail-closed site ledger that move with a raised verdict, pins, one changeset (stop on breach; explain in the report)Container & model:
M,mode:thread,model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate),effort: highClause-②: no (narrowing)
Metadata-protocol: narrowed
Responsibility:
the platform's own expression gate (a flow condition that fails every run is served a false "silently evaluates to null" warning, or no verdict at all) | none: PR #29953 closed only the record-triggered start and edge | every flow author whose condition fails at run timeThread-read: 6611484778
Premise: holds (the open readings of PR #29953's dev and its CONTRACT_REVIEW_TIER reviews 6610931370 / 6611149064, measured at
06abf5ac; triage 6611484778; the dev re-measures on main after9037b13d)Serial constraints cleared: PR #29953 (#29926) merged as
9037b13dStamp: 2026-11-04T04:34Z
/mnt/project-files/server-orders/29959.md(project files). The declaration is provisional: the dev measures and declares what the diff actually changes. On a narrowing, the seat runs aCONTRACT_REVIEW_TIERreview before landing.objectstack-fleet commentedon Nov 4, 2026
domain:server#6 · os-support-ai · session_01WGufzf4UmgvS2cusNbWnvy
os-dev-report
Stamp: 2026-11-04T06:09Z
{ "issue": 29959, "status": "done", "branch": "claude/project-thread-flea8r", "pr": "https://github.com/objectstack-ai/objectstack/pull/30015", "session": "session_01WGufzf4UmgvS2cusNbWnvy", "premise_still_valid": true, "summary": "Re-measured on main 73a3ef9f: all four sites held. Site 1 (non-record-triggered flows) keeps a warning, now type-mismatch-run-may-fail worded for the failed run (evaluateCondition throws on every flow); kept a warning because such a flow binds whatever its entrance hands it (API body, an action's partial record, a parent's record, or nothing), the measurement that keeps the null-guard twin off those flows. Site 2: a flattened condition's record.FIELD / previous.FIELD read is typed by a second pass of the same firstTypeMismatch judge (type-mismatch category only). Site 3: decision branches go through the edge's check() path with the ledger outcome. Site 4: MCP validate_expression gains optional severity and triggerKind inputs (widening), each combination pinned equal to the gate's verdict. New semantic entry flow-record-read-and-decision-type-mismatch-refused; changeset minor x4, Clause-2 yes (narrowing), Metadata-protocol narrowed.", "tests": "head 5a5fd5b8: formula 1637/1637, lint 7971/7971, mcp 672/672, objectql publish-type-mismatch-fail-closed 9/9, spec src/migrations 197/197; typecheck formula/lint/mcp/objectql exit 0. Ablations via ablation-replace, each restored blob == HEAD: A1 record-root pass off -> 4 failed; A2 flattened warning arm off -> 3 failed; A3 lint decision hint off -> 2 failed; A4 MCP inputs ignored -> 6 failed.", "gates": "dispatch-gates --commands 107 families, all exit 0 after a full turbo build (120/120) and --unshallow; --ran: 107 derived, 107 run, 0 NOT-MEASURED. pnpm lint exit 0. PR check-runs: in_progress at report time.", "dogfood_battery": "5a5fd5b8 · VERDICT command-exit 0 · Test Files 416 passed | 3 skipped (419) · Tests 3456 passed | 28 skipped (3484) · no failures", "mcp_calls": "9 — issue_read x3, pull_request_read x2 (get, get_comments), list_pull_requests x1, subscribe_pr_activity x1 (session tool), get_session x1, no GitHub write tools", "api_writes": "2 so far — fleet-write pr_create (#30015); this os-dev-report comment via post-stamped.mjs. Plus git push (not REST).", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [ "class: a · reach: os validate on a record-triggered flow declaring variables [{name:'title', type:'number', defaultValue:3}] over a text field title, start condition title > 5 -> error type-mismatch-run-fails 'title holds text', though the run seeds the variable first and the flattening skips a bound name (measured through the built lint dist at 5a5fd5b8; on main since PR #29953) · fix needs a schema-hint key excluding declared names from bare typing only · dedupe: 'type-mismatch-run-fails declared variable', 'flow variable shadows field type mismatch'", "class: a · reach: os validate on a flow start condition close_date + 1 > 0 (date field) -> error date-arithmetic saying the field 'silently evaluates to null'; in a flow condition the run fails instead (pre-existing; this PR deliberately does not extend date verdicts to record.FIELD) · dedupe: 'date-arithmetic flow condition evaluates to null', 'date-text-arithmetic flow condition wording'" ] }objectstack-fleet commentedon Nov 4, 2026
domain:server#6 · os-support-ai · session_01WGufzibcN9oHa8kDbTTnL5
Landed · 2026-11-04T08:35Z · PR #30015 merged through the queue as
e8afd1e8. ACCEPT 6625749789. TheCONTRACT_REVIEW_TIERreview 6624166953 asked for three rework items, and the delta review 6625738206 passed.pm:dispatchedis removed with the close.What changed for authors:
type-mismatch-run-fails) in arecord.FIELD/previous.FIELDread and in adecisionbranch. This applies to a record-triggered flow, and to a time-relative flow that sweeps the object its conditions are typed against.type-mismatch-run-may-fail. Its text says the run fails, and no longer says it "evaluates to null".validate_expressiongains the optionalseverityandtriggerKindinputs.Minor on
@objectstack/formula,@objectstack/lint,@objectstack/mcpand@objectstack/spec. BREAKING, registered asflow-record-read-and-decision-type-mismatch-refused.Carried: #30016 (date verdicts in a flow condition still say "evaluates to null"), #30025 (screen fields and open binders are not excluded from the bare typing) and #30026 (a time-relative flow is typed against the start object only, plus the MCP sweep-object and wording fixes).