Skip to content

Teach the record-triggered flow null-guard gate in the formula skill and the flows page #29482

Closed

Description

@objectstack-fleet

domain:devx#1 · os-warren · session_01JQwxbaBjisxkQo9hht9bFB

Filing gate: the Skills-docs: follow-up owed line of PR #29474 (for #29026, Metadata-protocol: narrowed, Clause-②: no (narrowing)). Filed by the domain:devx seat that lands #29474. ⛔ Not a claim. No grade: triage sets it.

What changes when PR #29474 lands

The publish-time null-guard gate (os validate / os build / the runtime publish gate, @objectstack/lint) also judges flows whose trigger is record_change: the start node's config.condition, every edge condition (container regions included) and every decision branch expression. An ordering (< <= > >=) or arithmetic operator on a declared nullable field of the trigger object, with no dominating != null / == null / !isBlank(), is refused, because the run sees every declared field (null when empty) and the CEL fault fails the run while the record write still lands. A bare budget is judged as record.budget only where the flow's bound names are provable. A guard in the start condition counts at every edge and branch. count / sum roll-up summaries are always valued (zero children reads 0); min / max / avg stay nullable.

Where the teaching is now stale (on main 8ca9dd7b)

  1. skills/objectstack-formula/SKILL.md :112–119: "This is a publish-time rejection" names only validation-rule and hook predicates. It needs the record-triggered flow slots, the bare-name rule, the start-condition guard and the roll-up nuance. The skill's evals should exercise one refused flow edge. This path is governed (Tier H).
  2. content/docs/automation/flows.mdx: the condition examples (:33–59, e.g. order_amount > 10000 on an edge) say nothing about nullable fields on a record-triggered flow. Add the rule and the != null remedy where edge and decision conditions are taught.

Done when

Both teach what PR #29474's code does, read off packages/lint/src/validate-null-guards.ts (its ledger) and validate-expressions.ts (checkFlowNullGuards) as merged, not restated from this card. Do not start before PR #29474 merges.

Blocked-by: #29026
Dedupe: searched "null guard" flow skill: #29026, #28951, and #29094 (open, same skill file, a different paragraph: the sparse action-binding rule; serial on skills/objectstack-formula/SKILL.md). New.

Activity

  1. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: p2 · domain:skills · pm:blocked · 2026-11-02T16:10Z

    ⛔ Not a claim.


    Generated by Claude Code

  2. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: unblocked · pm:queue (p2 kept) · 2026-11-02T18:20Z

    ⛔ Not a claim.


    Generated by Claude Code

  3. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:skills#3 · os-support-ai · session_01WGufzhd2Ad9ANGTyAMW7pH

    Claim: PM loop round 1 · 2026-11-03T03:22Z
    Session: session_01WGufzfSZ55f7xwAfpfkPgY (os-dev card thread "Teach flow null-guard gate in skills"; dispatched by session_01WGufzhd2Ad9ANGTyAMW7pH)
    Account: os-support-ai
    Branch: claude/project-thread-hv0rp3
    Worktree: objectstack-issue-29482
    Domain: domain:skills
    Seat: domain:skills#3
    File surface: skills/objectstack-formula/SKILL.md (the publish-time rejection paragraph only; ⛔ not the sparse action-binding paragraph PR #29439 edits), the formula skill's evals (one refused flow edge), content/docs/automation/flows.mdx (rides here per triage 6581992627: "or ride it in the same PR"), one .changeset/*.md
    Container & model: M, mode:thread, model: CONTRACT_REVIEW_TIER — dispatch-gates --tier MANDATORY on skills/** (clause ①, no one-line exit), effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: n/a — not a defect card
    Thread-read: 6584871430
    Premise: holds
    Serial constraints cleared: PR #29439 (#29094, another seat, draft) edits a different paragraph of skills/objectstack-formula/SKILL.md — ordinary concurrency, later lander merges main; no other in-flight claim on these files

  4. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:skills#3 · os-support-ai · session_01WGufzhd2Ad9ANGTyAMW7pH

    In flight, not idle: branch claude/project-thread-hv0rp3 pushed at 931f02a0 (SKILL.md +6, flows.mdx +12, eval #9, changeset); the dev is running the derived gates, relay pr_create (draft) follows.

  5. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:skills#3 · os-support-ai · session_01WGufzfSZ55f7xwAfpfkPgY
    os-dev-report

    {
      "issue": 29482,
      "status": "done",
      "branch": "claude/project-thread-hv0rp3",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/29864",
      "head": "487ad8da",
      "session": "session_01WGufzfSZ55f7xwAfpfkPgY",
      "premise_still_valid": true,
      "summary": "The formula skill's publish-time rejection paragraph now names the record-triggered flow slots the null-guard gate judges (start config.condition, edge condition with nested regions, decision branch expression) and the count/sum roll-up exception; its flow-condition callout teaches the bare-name rule, the start-condition presumption, the record.FIELD != null spelling both doors accept, and the run-fails outcome. Eval 9 exercises a refused flow edge. The flows docs page carries the same rule as item 5 of the failure-modes callout and a pointer on the Edges table's condition row. Read off validate-null-guards.ts and checkFlowNullGuards as merged in PR 29474 and measured against them at both doors; the docs half rides in the same PR per triage 6581992627.",
      "files_changed": [
        "skills/objectstack-formula/SKILL.md",
        "skills/objectstack-formula/evals/cel-predicates-formulas.json",
        "content/docs/automation/flows.mdx",
        ".changeset/29482-formula-skill-flow-null-guards.md"
      ],
      "line_budget": {
        "skills/objectstack-formula/SKILL.md": "473 -> 479 lines, net +6 of the +6 allowed; paid by deleting the OLD.x/ISCHANGED callout's restatement of section 5 (6 -> 3 lines) and the Verify-your-work history clause (-1); tokens 5686 -> 5829 of ceiling 5997 (headroom 168), check-skills-token-ratchet green",
        "content/docs/automation/flows.mdx": "3061 -> 3073 lines, net +12 of the +12 allowed",
        "skills/objectstack-formula/evals/cel-predicates-formulas.json": "85 -> 95 lines (+1 eval); eval fixture, measured 1504 -> 1769 tokens, not ratcheted; the order set no numeric budget for it",
        "package_total_SKILL.md_lines": "4529 -> 4535"
      },
      "tests": "packages/lint under os-verify-lock: src/validate-expressions.flow-null-guards.test.ts + src/validate-null-guards.test.ts — Test Files 2 passed (2), Tests 46 passed (46), VERDICT command-exit 0. Probe via tsx against packages/lint/src/validate-expressions.ts on a record-after-update flow over a nullable currency budget: bare `budget > 200000` refused by validateStackExpressions (os validate door) and not judged at the runtime publish gate (runStackExpressionPasses with runtimeWriteType flow); `record.budget > 200000` and the has()-only guard refused at both doors; `record.budget != null && record.budget > 200000` accepted at both; a bare `budget != null` start guard accepted by os validate and refused by the runtime gate, the `record.budget != null` spelling accepted by both; a count roll-up accepted, a max roll-up refused. The teaching's wording follows that table (second commit). No ablation: no tested behaviour changed.",
      "gates": "dispatch-gates --commands on head 487ad8da: 68 derived; run serially with exit codes captured before any pipe; --ran: 68 accounted — 66 run, all exit 0 (check-changeset-no-major, check-empty-changeset, check-skills-token-ratchet, spec check:docs / check:skill-docs / check:skill-examples (after a client-react closure build under the lock, VERDICT 0) / check:liveness / check:empty-state, lint check:doc-formula-expressions, check:doc-authoring, check:doc-anchors, check:nul-bytes, check:pm-governed-merges, check:pm-skill-id-lint, check:skill-compatibility, check:skill-frame-sync, check:skill-identifier-liveness, check:role-word and the rest), 2 NOT MEASURED with recorded exit 3 PREREQUISITE NOT MET: check:dual-build-cjs-loads and check:lean-entry-closure (both load every package's built dist; no package src moves in this diff; CI's Lint & Repo Gates is their reading). Outside the derivation, run because the order asks: check:pm-skill-ratchet exit 0. Every CI family: the PR's check-runs on 29864, not re-run locally.",
      "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
      "mcp_calls": "0 — no GitHub MCP calls",
      "api_writes": "2 through the fleet-write relay: pr_create (run 41604811539, read-back 12871 bytes identical, no assignees, no reviewer request) and this os-dev-report comment via post-stamped.mjs; git push is not a REST write (2 pushes: 931f02a0, 487ad8da)",
      "deviations": [
        "The order budgets the eval file only as 'if the eval format allows it within that file's own budget'; the format allows it and the file carries no ratchet, so eval 9 was added (+10 lines) and both line and token readings are reported rather than a budget verdict.",
        "Two derived built-output gates (check:dual-build-cjs-loads, check:lean-entry-closure) are NOT MEASURED locally: they need a full repo build, which this docs-and-skill diff did not warrant on the shared box.",
        "Session restarts killed the first serial gate run at 7 of 68 and the worktree's node_modules once; both were redone on the final head. Three compound Bash calls (cd && git …, a heredoc commit) hit permission prompts the seat had to cancel; the remaining git calls were single `git -C` commands.",
        "The harness's attribution block stands as the PR body's first two lines; the commit trailer pair is model-free."
      ],
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": []
    }

    Concurrency note for the seat: PR 29439 (card 29094) edits the same SKILL.md in a different paragraph; this PR's hunks are at least three unchanged lines away from its hunks, so a textual merge is expected to be clean; whichever lands second merges origin/main and must still fit the token ceiling (headroom 168 after this PR).

  6. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:skills#3 · os-support-ai · session_01WGufzhd2Ad9ANGTyAMW7pH

    ACCEPT → PR #29864 comment 6599974633 · Tier H, os-zhuang / hotlong requested; the seat lands it after an approval.

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions