@objectstack/lint@17.3.0. Measured on objectstack-ai/hotcrm at ff43321, three injections into ONE site in src/views/lead.view.ts (views[9] · formViews.status_update_modal), same run, same command (objectstack lint --json).
The reachability fact
visibility-bare-identifier reports a bare identifier in a view visibility predicate — until the SAME identifier also appears as the argument of a has() macro anywhere in that predicate. Then the rule goes silent for it entirely, including for its unwrapped occurrence.
| predicate written at that site |
lint |
finding |
status == "qualified" |
exit 1 |
visibility-bare-identifier at error — CONTROL |
has(status) && status == "qualified" |
exit 0 |
none |
has(status) |
exit 0 |
none |
Baseline for all three: passed:true · errors:0 · warnings:17 · suggestions:12 — the two silent injections moved nothing at all.
The control that makes this negative worth reading
The site is definitely walked, and the walk definitely reports there. Two OTHER injections into the byte-identical anchor at the same site fired in the same harness:
has(record.status) && && record.status == "qualified" gives visibility-predicate-syntax at error, exit 1, naming views[9] · formViews.status_update_modal;
has(record.status) && notARealFn(record.status) == "qualified" gives visibility-predicate-unknown-function at error, exit 1, same site.
So this is not "the rule cannot see that view".
Why the middle row matters — it is the shape an author actually writes
has(x) && x == "…" is the guarded spelling the totality discipline pushes authors toward. An author who correctly adds the has() guard and incorrectly forgets the record. prefix on both halves lands exactly in the silent row. The predicate then never evaluates for any record (Unknown variable: status), and on a form surface an unevaluable visibleWhen fails OPEN — the field renders, carrying its required: true into the console's submit check. That is the #688 shape, and the rule that exists to catch it is quiet for it.
The plausible cause is that a has() argument is a legitimate select target and is excluded from the bare-identifier walk — but the exclusion appears to be keyed on the identifier NAME across the whole predicate rather than on the occurrence.
⛔ Not necessarily a defect in the exclusion's intent. What it is, definitely, is a reachability fact: on this input the rule reports nothing, and the guarded-but-unprefixed spelling is the one an author is most likely to produce.
How it was found
objectstack-ai/hotcrm#1625 retires local test assertions proven duplicate of platform rules. hotcrm's test/view-predicate-dialect.test.ts carries a sweep, references only namespaces the evaluator binds, which reds on all three rows above (it reads roots through collectCelRootIdentifiers). Because the platform covers only the first row, that assertion was kept rather than retired.
Dedupe ran before filing — #13594, #13652, #12665 and #14288 read in full; none covers this. Unassigned.
@objectstack/lint@17.3.0. Measured onobjectstack-ai/hotcrmatff43321, three injections into ONE site insrc/views/lead.view.ts(views[9] · formViews.status_update_modal), same run, same command (objectstack lint --json).The reachability fact
visibility-bare-identifierreports a bare identifier in a view visibility predicate — until the SAME identifier also appears as the argument of ahas()macro anywhere in that predicate. Then the rule goes silent for it entirely, including for its unwrapped occurrence.status == "qualified"visibility-bare-identifierat error — CONTROLhas(status) && status == "qualified"has(status)Baseline for all three:
passed:true · errors:0 · warnings:17 · suggestions:12— the two silent injections moved nothing at all.The control that makes this negative worth reading
The site is definitely walked, and the walk definitely reports there. Two OTHER injections into the byte-identical anchor at the same site fired in the same harness:
has(record.status) && && record.status == "qualified"givesvisibility-predicate-syntaxat error, exit 1, namingviews[9] · formViews.status_update_modal;has(record.status) && notARealFn(record.status) == "qualified"givesvisibility-predicate-unknown-functionat error, exit 1, same site.So this is not "the rule cannot see that view".
Why the middle row matters — it is the shape an author actually writes
has(x) && x == "…"is the guarded spelling the totality discipline pushes authors toward. An author who correctly adds thehas()guard and incorrectly forgets therecord.prefix on both halves lands exactly in the silent row. The predicate then never evaluates for any record (Unknown variable: status), and on a form surface an unevaluablevisibleWhenfails OPEN — the field renders, carrying itsrequired: trueinto the console's submit check. That is the #688 shape, and the rule that exists to catch it is quiet for it.The plausible cause is that a
has()argument is a legitimate select target and is excluded from the bare-identifier walk — but the exclusion appears to be keyed on the identifier NAME across the whole predicate rather than on the occurrence.⛔ Not necessarily a defect in the exclusion's intent. What it is, definitely, is a reachability fact: on this input the rule reports nothing, and the guarded-but-unprefixed spelling is the one an author is most likely to produce.
How it was found
objectstack-ai/hotcrm#1625retires local test assertions proven duplicate of platform rules. hotcrm'stest/view-predicate-dialect.test.tscarries a sweep,references only namespaces the evaluator binds, which reds on all three rows above (it reads roots throughcollectCelRootIdentifiers). Because the platform covers only the first row, that assertion was kept rather than retired.Dedupe ran before filing — #13594, #13652, #12665 and #14288 read in full; none covers this. Unassigned.