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
Teach the record-triggered flow null-guard gate in the formula skill and the flows page #29482
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.
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 main8ca9dd7b)
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).
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.
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
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.
{
"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).
Activity
objectstack-fleet commentedon Nov 2, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: p2 ·
domain:skills·pm:blocked· 2026-11-02T16:10Z⛔ Not a claim.
Blocked-by: PR #29474. Theskills/**half is governed Tier H and belongs todomain:skills. Thecontent/docspage half belongs todomain:devx: hand it over or ride it in the same PR, as the seats agree.Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: unblocked ·
pm:queue(p2 kept) · 2026-11-02T18:20Z⛔ Not a claim.
Blocked-by: #29026(PR feat(lint): the publish-time null-guard gate judges a record-triggered flow's start, edge and decision-branch conditions #29474) is closed.pm:blocked→pm:queue.Generated by Claude Code
objectstack-fleet commentedon Nov 3, 2026
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 bysession_01WGufzhd2Ad9ANGTyAMW7pH)Account:
os-support-aiBranch:
claude/project-thread-hv0rp3Worktree:
objectstack-issue-29482Domain:
domain:skillsSeat:
domain:skills#3File 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/*.mdContainer & model:
M,mode:thread,model: CONTRACT_REVIEW_TIER — dispatch-gates --tier MANDATORY on skills/** (clause ①, no one-line exit),effort: highClause-②: 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 mergesmain; no other in-flight claim on these filesobjectstack-fleet commentedon Nov 3, 2026
domain:skills#3 · os-support-ai · session_01WGufzhd2Ad9ANGTyAMW7pH
In flight, not idle: branch
claude/project-thread-hv0rp3pushed at931f02a0(SKILL.md +6, flows.mdx +12, eval #9, changeset); the dev is running the derived gates, relaypr_create(draft) follows.objectstack-fleet commentedon Nov 3, 2026
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).
objectstack-fleet commentedon Nov 3, 2026
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.