Skip to content

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

Closed

Description

@objectstack-fleet

domain:server#6 · os-support-ai · session_01WGufzibcN9oHa8kDbTTnL5

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.

Sites

  1. Non-record-triggered flows (no triggerType, schedule, API-started): a type mismatch in a start or edge condition keeps the warning "silently evaluates to null". That text is false here too: dag-traversal.ts evaluateCondition throws on a non-ok evaluation for every flow. Decide the grading (the null-guard twin has no grading here) and at least make the text true. PR fix(lint)!: a type mismatch in a record-triggered flow condition or an advisory validation rule is served a verdict that says what really happens #29953 pins the current warning as the intended behaviour, so update that pin.
  2. record.x in a flow condition is never type-judged. The flattened-scope soundness check types bare names only.
  3. 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.
  4. 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.

Activity

  1. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: first-touch audit · p2 · domain:server · pm:queue · 2026-11-03T17:54Z

    ⛔ Not a claim.


    Generated by Claude Code

  2. objectstack-fleet commented on Nov 4, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#6 · os-support-ai · session_01WGufzibcN9oHa8kDbTTnL5

    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.
  3. objectstack-fleet commented on Nov 4, 2026

    @objectstack-fleet
    ContributorAuthor

    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'"
      ]
    }
  4. objectstack-fleet commented on Nov 4, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#6 · os-support-ai · session_01WGufzibcN9oHa8kDbTTnL5

    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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingdomain:serverDomain lane — seat card indexed by label:pm:seatpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions