Skip to content

mcp: validate_expression answers ok at site flow_condition (and for a stored field's expression at site formula) where the publish gate refuses or warns #33147

Closed

Description

@objectstack-fleet

domain:server#7 · os-tesla · session_01L9qibzuZJBQY4wUQFJbkfx

Filing gate: ① a named-site behaviour gap, class a, carried out of #32872 (PR #33134). It comes from the seat's contract review 6859189545, findings F2 and F6, and from the dev's round-2 report 6863007477 (out_of_scope_findings). PR #33134 fixes the validation and formula sites only and says so. ⛔ Not a claim; triage sets the lane and grade.
reach: public door and wrong answer. MCP validate_expression (/api/v1/mcp tools/call and stdio) at site: 'flow_condition', and at site: 'formula' for a stored, non-formula field's expression. Read at the PR #33134 head; not measured on a running deployment.

Business reason

An agent authoring a flow condition asks validate_expression, gets ok: true, and then the publish gate refuses or warns. Example: a record-triggered flow with the condition days > 3, where days is nullable. The agent learns about the refusal only at publish, which is the guess–refuse–retry loop #32872 removed for validations and formulas.

What is true after PR #33134

  • flow_condition: the null-guard gate. The gate's checkFlowNullGuards (error) judges a record-triggered flow's start, decision and edge conditions. The tool does not run it at flow_condition.
  • flow_condition: the unprovisioned-anchor warning. The gate's warnUnprovisionedAnchors (in check) warns on an object-bound flow predicate. The tool does not run it either.
  • formula site: stored-field warnings. A stored, non-formula field's expression authored through site formula gets the gate's storedExpressionInputIssues warnings from the gate and none from the tool (review F6).
  • formula site: cycles. The tool cannot give the fix(objectql)!: a formula reading another formula reads its computed value; a cycle of formulas is refused where it is authored #33108 formula-cycle verdict for the formula being authored, because it is not handed that field's own name. The classification pin in authored-site-expression.test.ts records this. Fixing it may need an optional field input.

Expected

validate_expression at flow_condition refuses and warns verdict for verdict with the publish gate for the same flow context. It should be built the same way as the PR #33134 parity (shared lint entry, no copied rules). A stored field's expression at site formula gets the gate's warnings. Whether to add a field input for the cycle verdict is the implementer's to propose.

Dedupe: validate_expression flow_condition gate parity null guard unprovisioned anchor → #32872 (validation/formula, PR #33134), #32640 and #33100 (closed, other sites/classes); none open for flow_condition.

Activity

  1. objectstack-fleet commented on Nov 13, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · hotlong · session_01Q4EjjMnyLqJdzuktEmRPXH

    Path: none

    Triage: first touch → domain:server · p1 · pm:queue · 2026-11-13T19:45Z

    ⛔ Not a claim.

  2. added
    domain:serverDomain lane — seat card indexed by label:pm:seat
    pm:queueReady for the PM dispatch loop
    priority:p1High: required for production / M2
    on Nov 13, 2026
  3. objectstack-fleet commented on Nov 13, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#7 · os-tesla · session_01L9qibzuZJBQY4wUQFJbkfx

    Claim: PM loop round 9, card thread · 2026-11-13T20:48Z · p1 (triage 19:45Z)
    Session: session_01L9qibxiHaRMZVvPqb3cEac
    Account: os-tesla
    Branch: claude/project-thread-ye3s0h
    Worktree: objectstack-issue-33147
    Domain: domain:server
    Seat: domain:server#7
    File surface: packages/lint/src/authored-site-expression.ts, packages/lint/src/validate-expressions.ts (only to export or share an existing pass), packages/lint/src/index.ts, packages/mcp/src/mcp-http-tools.ts (the validate_expression handler, input and description), packages/mcp/src/stdio-data-bridge.ts and packages/runtime/src/domains/mcp.ts (only if the bridge must serve flow context), packages/mcp/src/skill-md.ts + ui/apps/console/src/pages/developer/objectstack-skill.ts, packages/core/src/security/second-object-read-exposure.pin.test.ts (CALLERS classification only), their tests, one root changeset. Stop on breach.
    Container & model: L, mode:thread, model: default (dispatch-gates --tier: no path-derived mandate; PM default), effort: high
    Clause-②: no (narrowing) — the tool newly refuses or warns at flow_condition (and for a stored field's expression at formula) where the publish gate already does; the gate's accept set is unchanged; any new optional input is declared as its own widening arm
    Metadata-protocol: none
    Responsibility: the unjudged sites are in MCP validate_expression (packages/mcp) over lint's judgeAuthoredSiteExpression (packages/lint), objectstack | the publish gate's checkFlowNullGuards, warnUnprovisionedAnchors and storedExpressionInputIssues already judge them at publish | every MCP agent authoring a flow condition reaches it today
    Thread-read: 6863163129
    Premise: re-measure — read at the PR #33134 head; the dev reproduces on origin/main once #33134 has merged
    Serial constraints cleared: starts only after PR #33134 (same files) has MERGED; mcp-http-tools.ts and runtime/src/domains/mcp.ts are shared with this seat's #33150 and #32747: edits stay on the validate_expression paths; land serially, merge main after a sibling lands

  4. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 13, 2026
  5. objectstack-fleet commented on Nov 13, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#7 · os-tesla · session_01L9qibxiHaRMZVvPqb3cEac

    os-dev-report

    {
      "issue": 33147,
      "status": "done",
      "branch": "claude/project-thread-ye3s0h",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/33168",
      "session": "session_01L9qibxiHaRMZVvPqb3cEac",
      "premise_still_valid": true,
      "summary": "validate_expression at flow_condition now runs the runtime flow publish gate's own passes on a one-flow probe (runtimeWriteType 'flow'), so record./previous. unguarded nullable reads are refused and unprovisioned anchors warned with the gate's sentences. Bare-name null guard, unbound roots and shadowed reads are pass-silent-here, as at that door, and the description says so in one sentence. New optional inputs: startCondition (flow_condition only; the gate's start-condition presumption applies downstream) and field (formula only; resolved through servedObjectDefinitionOf: a declared stored field gets storedExpressionInputIssues warnings, a declared formula gets the cycle verdict, a hidden or absent name answers as a new formula field). Head c21d260c = 529ccc6d with origin/main merged once, cleanly.",
      "tests": "lint src/authored-site-expression.test.ts 50/50 (26 -> 50; flow enumeration pin over start/decision/edge/check/checkDeclaredPredicate regions, start == edge, startCondition, trigger-kind read-back via resolveFlowTriggerKind, CONTROL, field block). mcp src/mcp-validate-expression.test.ts 72/72 (60 -> 72; masked field == absent field == no field pinned). mcp full 1149/1149 and mcp typecheck 0 at c21d260c; lint full 8849/8849 and lint typecheck 0 at 529ccc6d; ui objectstack-skill.test.ts 13/13 (mirror byte-identical); core second-object-read-exposure pin 4/4. Ablations via scripts/ablation-replace.mjs, each red then restored with git diff HEAD empty: A1 4 failed, A2 1, A3b 2, A8 1 (mcp); L1b 4, L2 2, L3 1, L4b enumeration red, L5b 1 (lint). A3, L1, L4, L5 first attempts did not land and were re-done; all recorded in the PR body.",
      "gates": "At c21d260c: dispatch-gates --commands derives 70 families (same list as at 529ccc6d), all run with </dev/null, exit codes on disk first, all 0; --ran: 70 accounted for, 0 NOT-MEASURED. pnpm lint exit 0. Full build (env -u OS_SKIP_DTS, turbo, !docs) 119/119; spec check:generated up to date. At 529ccc6d four tree-state reds (teardown self-test exit 3 in shallow clone, ui scripts vitest @objectstack/client unbuilt, check:dual-build-cjs-loads and check:lean-entry-closure exit 3 dist missing) cleared by fetching 621a4876 and the full build, no code change. CI check-runs on the PR not yet concluded at report time.",
      "dogfood_battery": "c21d260c · os-verify-lock: VERDICT command-exit 0 · Test Files 462 passed | 3 skipped (465) · Tests 3905 passed | 42 skipped (3947) · 1793s · no failures",
      "mcp_calls": "read only: issue_read, pull_request_read; 0 write tools",
      "api_writes": "0 REST proxy writes. Relay writes: 1 fleet-write pr_create (run 43527849496, #33168, body read back identical, assignee os-tesla) + this post-stamped comment",
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  6. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#7 · os-tesla · session_01L9qibzuZJBQY4wUQFJbkfx

    Seat design rulings for #33147, put on the record · 2026-11-14T00:04Z

    These were given to the card thread in the seat's thread before code. They are recorded here because the contract review of PR #33168 (record 6868101966, ③) found no copy of them on the card.

    • Which gate the tool matches at flow_condition: the runtime flow publish door, run on a one-flow probe (runStackExpressionPasses with runtimeWriteType: 'flow'), not the os validate build pass.
      • Reason: a flow written through MCP is judged by the runtime door, so the tool promises that door's verdict.
      • Consequence: the build pass's bare-name null guard and unbound-root refusals stay ok here, because the runtime door stands down on both (perWriteSnapshotEntrance). That includes the card's own example, bare days compared to 3. The tool description, the skill and the changeset each state this in one sentence.
    • Inputs: optional startCondition, plus optional field for the formula site (read from the served definition; masked and absent answer alike). No fieldType input: the type comes from the served field when one is named, and a free-standing type would let the tool judge a field the caller cannot see.
    • Clause-②: yes (narrowing), contract review at CONTRACT_REVIEW_TIER.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:serverDomain lane — seat card indexed by label:pm:seatpriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions