Found while surveying which platform rules fire on objectstack-ai/hotcrm (its #1621, step 3 of epic #1579). Read-only survey, report-first, unassigned.
Measured against the pinned @objectstack/lint@17.3.0, hotcrm at 1670557. Every injection proven on disk before a verdict was read; every one restored by blob hash verified by observation.
What the shipped rule declares
Both ids carry a per-position severity, keyed off leaf.inFilter:
severity: inFilter ? "error" : "warning",
rule: FLOW_TEMPLATE_UNKNOWN_FIELD,
and the message body branches the same way — nodeType filter references '{record.…}' versus template references '{record.…}', the latter explaining that "the token resolves to an empty string on every run (silently)". So the non-filter position is deliberately in scope, at warning.
Measurement — one file, four injections, same run design
src/flows/billing-handoff.flow.ts carries both position kinds against the same triggering record: a get_record node with filter: { id: '{record.crm_account}' }, and a payload block that interpolates {record.id}, {record.name}, {record.amount}, {record.close_date}, … outside any filter.
| injection |
position |
result |
exit |
filter: { id: '{record.crm_account_nope}' } |
in filter |
flow-template-unknown-field at error, x2 |
1 |
filter: { id: '{record.crm_account.owner_id}' } |
in filter |
flow-template-lookup-traversal at error, x2 |
1 |
amount: '{record.amount_nope}' |
payload, not a filter |
passed: true, no finding |
0 |
amount: '{record.crm_account.name}' |
payload, not a filter |
passed: true, no finding |
0 |
The first two rows are the working control: the file is walked, the node is walked, recordRefsIn resolves {record.…} shapes, and the object's field map is loaded. Only the non-filter leaves are absent from leaves.
⚠️ Declared: my first attempt at the non-filter half used {billingAccount.phone_nope} — a variable ref, not a record ref — and so was out of the rule's class, not evidence of anything. It is reported here as a no-op rather than silently replaced; the table above is the redone measurement with {record.FIELD} in both positions.
Consequence
The half of these two rules that fires is the half whose consequence is loud — a filter token that resolves to nothing drops the condition and the node refuses at execution time. The half that is silent at authoring time is the half whose runtime consequence is also silent: per the rule's own message, the token renders as an empty string on every run. A hand-off payload that interpolates a renamed field ships an empty column to the billing system, and nothing anywhere says so.
Related, not duplicating
Dedupe
Targeted search returned 4 on-topic results (the three above plus #15793) — non-zero and on-topic, which is the control that the search was not silently answering empty.
Found while surveying which platform rules fire on
objectstack-ai/hotcrm(its #1621, step 3 of epic #1579). Read-only survey, report-first, unassigned.Measured against the pinned
@objectstack/lint@17.3.0, hotcrm at1670557. Every injection proven on disk before a verdict was read; every one restored by blob hash verified by observation.What the shipped rule declares
Both ids carry a per-position severity, keyed off
leaf.inFilter:and the message body branches the same way —
nodeType filter references '{record.…}'versustemplate references '{record.…}', the latter explaining that "the token resolves to an empty string on every run (silently)". So the non-filter position is deliberately in scope, atwarning.Measurement — one file, four injections, same run design
src/flows/billing-handoff.flow.tscarries both position kinds against the same triggering record: aget_recordnode withfilter: { id: '{record.crm_account}' }, and a payload block that interpolates{record.id},{record.name},{record.amount},{record.close_date}, … outside any filter.filter: { id: '{record.crm_account_nope}' }flow-template-unknown-fieldaterror, x2filter: { id: '{record.crm_account.owner_id}' }flow-template-lookup-traversalaterror, x2amount: '{record.amount_nope}'passed: true, no findingamount: '{record.crm_account.name}'passed: true, no findingThe first two rows are the working control: the file is walked, the node is walked,
recordRefsInresolves{record.…}shapes, and the object's field map is loaded. Only the non-filter leaves are absent fromleaves.{billingAccount.phone_nope}— a variable ref, not arecordref — and so was out of the rule's class, not evidence of anything. It is reported here as a no-op rather than silently replaced; the table above is the redone measurement with{record.FIELD}in both positions.Consequence
The half of these two rules that fires is the half whose consequence is loud — a filter token that resolves to nothing drops the condition and the node refuses at execution time. The half that is silent at authoring time is the half whose runtime consequence is also silent: per the rule's own message, the token renders as an empty string on every run. A hand-off payload that interpolates a renamed field ships an empty column to the billing system, and nothing anywhere says so.
Related, not duplicating
filter-token-unknownnever walksflows— an unresolvable{TOKEN()}in a flow node'sconfig.filteris silent, while the identical string in a view filter exits 1 #16096 (open) —filter-token-unknownnever walksflowsat all. Same family of walk-reach gaps in flow validation, different rule and opposite direction (that one reaches no flow position; this one reaches only the filter positions).registerFlow's validators walk top-level nodes only.Dedupe
Targeted search returned 4 on-topic results (the three above plus
#15793) — non-zero and on-topic, which is the control that the search was not silently answering empty.