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. added
    domain:skillsDomain lane — seat card indexed by label:pm:seat
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    on Nov 2, 2026
  3. 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

  4. added
    pm:queueReady for the PM dispatch loop
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    pm:queueReady for the PM dispatch loop
    on Nov 2, 2026
  5. 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

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

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

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

Labels

domain:skillsDomain 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