Skip to content

fix(lint,cli): a filter reference that cannot resolve fails the build, not the run (#3426, #3810) - #3861

Merged
os-zhuang merged 2 commits into
mainfrom
claude/template-tokens-filters-notifications-nicmhd
Jul 28, 2026
Merged

fix(lint,cli): a filter reference that cannot resolve fails the build, not the run (#3426, #3810)#3861
os-zhuang merged 2 commits into
mainfrom
claude/template-tokens-filters-notifications-nicmhd

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Why

validateFlowTemplatePaths (#3426) reported every {record.<path>} miss as advisory, on the reasoning that an unresolved token renders a blank and the run still completes.

Since #3810 that reasoning no longer holds in one position. Inside a CRUD node's filter, an unresolved token does not blank a value — it deletes the condition, and a removed condition matches MORE rows, not fewer. resolveNodeFilter now refuses to execute those nodes rather than run a widened query.

So the rule was warning about metadata whose runtime is already decided: os validate printed a yellow line, exited 0, and shipped a flow that cannot run. The author finds out on the first execution instead of at build time — which is exactly the shift-left this rule exists to provide.

What changed

Severity follows the runtime consequence, by position.

Position Severity Why
filter of get_record / update_record / delete_record error (new) The three nodes whose filter resolveNodeFilter guards — the runtime refuses them
Everything else (message body, http url, write payload) warning (unchanged) Token renders a blank, run completes, head object may come from another package

create_record is deliberately excluded from the gating set: it writes a payload and has no filter to widen.

Both rules split this way (flow-template-unknown-field and flow-template-lookup-traversal), so a typo and a lookup hop are gated wherever the runtime refuses them. Filter-position leaves are collected first, so a reference used in both positions on one node is reported once, at error severity.

os validate now enforces it. The step filtered this rule's findings for severity === 'warning' and dropped everything else on the floor — an error from it would have been silently invisible. It now gates on errors first (rule id, config path, errors in --json), mirroring the validateReadonlyFlowWrites step directly below it, which makes the same shift-left split: a certain runtime failure gates, a state-dependent one advises.

Error-position findings also state the consequence rather than the symptom — "the node refuses to run at execution time" and "an absent condition WIDENS the query" — so the fix is obvious without opening #3810.

Verification

  • packages/lint suite green: 514 passed, 9 new tests covering the severity split (per-node-type gating, non-filter positions staying advisory, both-position dedupe at error, create_record exclusion, valid filters clean, and config.expand still suppressing a filter traversal per Flow lookup-field template resolution ({record.account.name}) needs a read-identity design decision (follow-up to #3426) #3475).
  • Typecheck on packages/cli/src/commands/validate.ts: 0 errors.
  • ESLint clean on all three changed files.
  • Ran the rule against shipped metadata — 33 flows across app-todo, app-crm and app-showcase produce no new errors. The four pre-existing lookup-traversal warnings sit in script / notify / subflow / parallel positions and keep their advisory severity.

Compatibility

No authoring change is required for a correct filter. A filter this rule now fails is one the runtime would have refused anyway — the difference is finding out at os validate instead of at 3am.


Generated by Claude Code

…, not the run (#3426, #3810)

`validateFlowTemplatePaths` called every `{record.<path>}` miss advisory, on the
reasoning that an unresolved token renders a blank and the run completes. Since
#3810 that stopped being true in one position: inside a CRUD node's `filter` an
unresolved token does not blank a value, it DELETES the condition — and a
removed condition matches MORE rows. Those nodes now refuse to execute.

So the rule was warning about metadata whose runtime was already decided:
`os validate` printed a yellow line, exited 0, and shipped a flow that cannot
run. Severity now follows the runtime consequence, by position.

- `filter` of get_record / update_record / delete_record -> error. The three
  nodes whose filter `resolveNodeFilter` guards. The message states what the
  runtime does and why this gates rather than warns (an absent condition WIDENS
  the query). `create_record` is excluded: it writes a payload, no filter.
- Every other position -> warning, unchanged. The token still renders a blank,
  the run still completes, and the head object may come from another package.

Both rules split this way, so a typo and a lookup hop are gated wherever the
runtime refuses them. A reference used in both positions reports once, at error.

`os validate` now enforces it. The step filtered this rule for
`severity === 'warning'` and dropped everything else, so an error would have
been invisible. It gates on errors first — rule id, config path, and `errors` in
`--json` — mirroring the `validateReadonlyFlowWrites` step below it, which makes
the same shift-left split.

Verified against shipped metadata: 33 flows across app-todo, app-crm and
app-showcase produce no new errors; the four pre-existing traversal warnings sit
in script/notify/subflow/parallel positions and keep advisory severity.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdCvGtER9SzJopS4Yh3Pa1
@vercel

vercel Bot commented Jul 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Jul 28, 2026 12:07pm

Request Review

@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Jul 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/cli, @objectstack/lint.

19 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/skills-reference.mdx (via packages/cli)
  • content/docs/api/client-sdk.mdx (via @objectstack/cli)
  • content/docs/api/data-flow.mdx (via @objectstack/cli)
  • content/docs/api/environment-routing.mdx (via @objectstack/cli)
  • content/docs/api/error-catalog.mdx (via @objectstack/cli)
  • content/docs/automation/hook-bodies.mdx (via packages/cli, @objectstack/lint)
  • content/docs/deployment/backup-restore.mdx (via @objectstack/cli)
  • content/docs/deployment/cli.mdx (via @objectstack/cli)
  • content/docs/deployment/self-hosting.mdx (via @objectstack/cli)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/cli)
  • content/docs/kernel/runtime-services/data-service.mdx (via packages/cli)
  • content/docs/kernel/runtime-services/index.mdx (via packages/cli)
  • content/docs/permissions/authentication.mdx (via @objectstack/cli)
  • content/docs/permissions/authorization.mdx (via @objectstack/lint)
  • content/docs/plugins/packages.mdx (via @objectstack/cli)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/cli)
  • content/docs/protocol/kernel/realtime-protocol.mdx (via @objectstack/cli)
  • content/docs/releases/implementation-status.mdx (via @objectstack/cli)
  • content/docs/releases/v16.mdx (via @objectstack/cli)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

The query skill's filters rule described the #3810 runtime refusal as the only
signal. Two of its three causes — a mistyped field and an un-expanded lookup hop
— are statically decidable, and `objectstack validate` now fails on them in a
filter position rather than warning.

Records which half catches what, so an author reading the rule knows a filter
typo is a build failure, the same reference outside a filter is still only a
warning, and an unresolved flow variable remains run-time-only (it is not
statically checkable).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdCvGtER9SzJopS4Yh3Pa1
@os-zhuang
os-zhuang marked this pull request as ready for review July 28, 2026 12:55
@os-zhuang
os-zhuang merged commit b0e5a37 into main Jul 28, 2026
16 checks passed
@os-zhuang
os-zhuang deleted the claude/template-tokens-filters-notifications-nicmhd branch July 28, 2026 12:56
os-zhuang added a commit that referenced this pull request Jul 28, 2026
…compile` (#3583, #3810) (#3874)

`validateFlowTemplatePaths` was wired by hand into `os validate` and nowhere
else — the drift `REFERENCE_INTEGRITY_RULES` exists to end (#3583 §5 D5): one
stack, a different rule subset depending on which command the author ran.

It mattered more once #3861 gave the rule a gating severity. A `{record.<path>}`
token in a CRUD node's `filter` naming an unknown field, or hopping through an
un-expanded relation, makes the runtime REFUSE the node (#3810). `os validate`
failed on it; `os lint` and `os compile` did not look, so a CI job running
either one built and shipped a flow that cannot execute.

The rule is now a suite member — it meets the suite's own admission criterion,
since a `{record.<field>}` token is a name resolved against the bound object's
declared fields. One line reaches all three commands; the hand-wiring in
`validate.ts` is deleted rather than duplicated.

Checked the three stack shapes the suite is handed before moving the call site:
raw `config` (lint), `normalizeStackInput` output, and schema-parsed
`result.data` (validate/compile), across all three example apps. They agree
finding-for-finding, so the move does not change what is reported.

End-to-end on app-showcase: all three commands pass unchanged on the real stack,
and with one filter token corrupted to `{record.idd}` all three exit 1 — where
previously only `validate` did.

Also fixed in the same file: on a clean run `os validate --json` never reported
the suite's warnings. `refWarnings` was assembled, printed, and included in the
FAILURE payload, but omitted from the success-path `warnings` array — so adding
the rule to the suite would have silently dropped its warnings for JSON
consumers. Same bug shape as the dropped errors #3861 fixed: computed, then
discarded.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants