[Med] fix(policy): respect Rule.appliesTo in lint() to prevent false-positive errors#714
Conversation
…ve errors Rule declares an appliesTo?: TargetKind[] field documented as "empty = applies to every target kind". Two rules (bundleId, iconSizes) set it to ['mobile', 'tv', 'wearable']. But lint() called r.run(ctx) for every rule unconditionally, ignoring appliesTo entirely. Result: a manifest with a 'web' or 'api' target that incidentally carries a config.bundleId field (a legitimate pattern for internal naming, telemetry ids, etc.) receives hard 'error' findings from the mobile/bundle-id rule, causing CI gates to fail for non-applicable checks. Confirmed via grep: appliesTo is referenced in rule.ts (interface), bundle-id.ts, and icon-sizes.ts — never in linter.ts. Fix: before running rules, collect the set of target kinds present in the manifest and filter to rules whose appliesTo overlaps that set (or is empty/undefined, meaning universal). Adds no overhead for the common single-kind case.
Greptile SummaryThis PR adds an
Confidence Score: 3/5The change introduces a regression where mobile/TV/wearable manifests silently skip the bundleId and iconSizes checks that are specifically designed for them — merging as-is would leave those rules permanently inactive. The filter reads packages/policy/src/linter.ts — the new manifestKinds collection block needs to use a source that actually carries kind information. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[lint called with ctx and rules] --> B[Extract manifestKinds\nfrom ctx.manifest.targets]
B --> C{t.kind exists\non TargetSpec?}
C -- "NO (TargetSpec has no kind)\nt.kind === undefined" --> D[manifestKinds = Set of undefined]
C -- "YES (intended)" --> E[manifestKinds = Set of real TargetKind values]
D --> F[filter: r.appliesTo.some\nk => manifestKinds.has k]
E --> F
F -- "k is 'mobile' etc.\nSet has only undefined\n→ always false" --> G[bundleId and iconSizes\nfiltered OUT for ALL manifests]
F -- "k matches real kind\n→ true for mobile targets" --> H[bundleId and iconSizes\nrun for mobile manifests only]
G --> I["❌ Mobile manifests: no\nbundleId/iconSizes check\n(regression)"]
H --> J["✅ Web/api manifests: no\nfalse positives\n(desired)"]
I --> K[Promise.all on applicable rules]
J --> K
K --> L[Return LintResult]
Reviews (1): Last reviewed commit: "fix(policy): respect Rule.appliesTo in l..." | Re-trigger Greptile |
| const manifestKinds = new Set( | ||
| Object.values(ctx.manifest.targets ?? {}).map((t) => t.kind), | ||
| ); | ||
| const applicable = rules.filter( | ||
| (r) => !r.appliesTo || r.appliesTo.length === 0 || r.appliesTo.some((k) => manifestKinds.has(k)), | ||
| ); |
There was a problem hiding this comment.
t.kind does not exist on TargetSpec — manifestKinds will always be Set{undefined}
ctx.manifest.targets is typed as Record<string, TargetSpec> (see manifest.ts line 57). TargetSpec is inferred from targetSpecSchema and has only use, enabled, config, and distribute fields — there is no kind property. The kind field belongs to the Target<Config> adapter interface in target.ts, not to the manifest spec.
As a result, Object.values(...).map((t) => t.kind) produces [undefined, undefined, ...] at runtime (and is a TypeScript compilation error under strict mode). manifestKinds will be Set { undefined }, and r.appliesTo.some((k) => manifestKinds.has(k)) will always be false, meaning every rule that declares an appliesTo constraint — including bundleId and iconSizes — will be silently skipped for all manifests, not just web/api ones. Mobile and TV manifests that carry a malformed bundleId will receive no errors, which is the opposite of the intent.
|
🤖 Auto-rebase: The branch was rebased successfully locally but could not be pushed to the fork. Please enable 'Allow edits from maintainers' in the PR settings, or rebase manually: |
8 similar comments
|
🤖 Auto-rebase: The branch was rebased successfully locally but could not be pushed to the fork. Please enable 'Allow edits from maintainers' in the PR settings, or rebase manually: |
|
🤖 Auto-rebase: The branch was rebased successfully locally but could not be pushed to the fork. Please enable 'Allow edits from maintainers' in the PR settings, or rebase manually: |
|
🤖 Auto-rebase: The branch was rebased successfully locally but could not be pushed to the fork. Please enable 'Allow edits from maintainers' in the PR settings, or rebase manually: |
|
🤖 Auto-rebase: The branch was rebased successfully locally but could not be pushed to the fork. Please enable 'Allow edits from maintainers' in the PR settings, or rebase manually: |
|
🤖 Auto-rebase: The branch was rebased successfully locally but could not be pushed to the fork. Please enable 'Allow edits from maintainers' in the PR settings, or rebase manually: |
|
🤖 Auto-rebase: The branch was rebased successfully locally but could not be pushed to the fork. Please enable 'Allow edits from maintainers' in the PR settings, or rebase manually: |
|
🤖 Auto-rebase: The branch was rebased successfully locally but could not be pushed to the fork. Please enable 'Allow edits from maintainers' in the PR settings, or rebase manually: |
|
🤖 Auto-rebase: The branch was rebased successfully locally but could not be pushed to the fork. Please enable 'Allow edits from maintainers' in the PR settings, or rebase manually: |
Rule declares an
appliesTo?: TargetKind[]field documented as 'empty = applies to every target kind'. Two rules (bundleId, iconSizes) set it to['mobile', 'tv', 'wearable']. Butlint()calledr.run(ctx)for every rule unconditionally, ignoringappliesToentirely.Result: a manifest with a 'web' or 'api' target that incidentally carries a
config.bundleIdfield (a legitimate pattern for internal naming, telemetry ids, etc.) receives hard 'error' findings from the mobile/bundle-id rule, causing CI gates to fail for non-applicable checks.Confirmed via grep:
appliesTois referenced in rule.ts (interface), bundle-id.ts, and icon-sizes.ts — never in linter.ts.Fix: before running rules, collect the set of target kinds present in the manifest and filter to rules whose
appliesTooverlaps that set (or is empty/undefined, meaning universal). Adds no overhead for the common single-kind case.Severity: Medium