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

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions