Skip to content

lint: searchableFields is a FIFTH blanket-SYSTEM_FIELDS read site — a declared anchor on an external object becomes a $searchFields narrowing over a column with no storage #8404

Description

@qq9340100

Measured while implementing #8340 (the four filter/page-binding read sites). #8340's card enumerates four SYSTEM_FIELDS read sites and its file surface stops there; validate-searchable-fields.ts is a fifth with the same structure, and it was not in scope.

The gap

packages/lint/src/validate-searchable-fields.ts judges a declared searchableFields entry against the object-independent blanket union:

  • :345if (!known.has(name) && !SYSTEM_FIELDS.has(name)) gates searchable-field-unknown;
  • :239resolveAllowedSet goes further and manufactures a stub meta for exactly these entries (systemDeclared = declared, not in authored fields, in SYSTEM_FIELDS) so they survive the resolution's existence filter "exactly as [they do] at runtime".

Both are correct about EXISTENCE and structurally blind to PROVENANCE, the same way the four #8340 sites were. On an ADR-0015 external object the platform registers the injected anchors (owner_id, organization_id, the audit family, owning_business_unit_id) and provisions no storage behind them (#7865 / #8116), so:

searchableFields: ['name', 'owner_id']   // external object

lints clean, the stub keeps the entry in the resolved allow-list, and the view's $searchFields narrowing then searches a column that is empty on every record. The degradation is #4830's own failure mode reached by a different route — a narrower search than declared, silently — and it is exactly the class the searchable-field-unsearchable half of this rule exists to catch.

Why this is not part of #8340

#8340's ruling scoped it to the four measured read sites (validate-widget-bindings, validate-react-page-props, validate-page-field-bindings, validate-flow-template-paths) and its file surface excludes this module. The wiring is now cheap — #8340 landed the shared per-object index and message builders (indexUnprovisionedAnchors, unprovisionedAnchorCause, unprovisionedAnchorHint in packages/lint/src/system-fields.ts) on top of #8116's unprovisionedInjectedColumnsFor — but this site needs its own false-positive analysis, because unlike the four it does not merely skip the name: the stub it fabricates at :239 feeds the resolution, so the fix has to decide whether an unprovisioned anchor should still receive a stub (it is addressable, so probably yes) in addition to whether it warns.

Pointers

Blocked-by: none — #8340 has landed the prerequisite helpers on its branch.

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions