Problem
Fleet-wide removal of redundant "default-bundle" Lint.swift files (swift-institute/.github#536, programme #527) has no mechanical guard preventing reintroduction. A package-local Lint.swift whose only import and only trailing-closure content is exactly one Lint.Rule.Bundle.<layer> matching the consumer's own resolved layer — no .excluding, no inline Rule.Configuration entries — reproduces the central default bundle CI and local tooling already select when Lint.swift is absent. Nothing today rejects that shape: #536's wave-2 census confirmed no rule matching this predicate exists in swift-institute-linter-rules, swift-linter-rules, swift-primitives-linter-rules, or swift-standards-linter-rules. Without this rule, the 447-file convergence wave cannot stay converged — any future package can silently reintroduce the exact redundant file the wave removed.
Proposed outcome
A mechanical rule — implemented as an AST rule in this repository, since it must compare a package's own resolved layer (primitives/standards/institute) against the correct central bundle, which is Institute-level composition knowledge — that fails closed when a package-local Lint.swift reproduces the central default for its own layer. The rule flags (and can fix-remove) the file. It ships with fixtures proving it fires only on the exact redundant shape:
- Positive: fixtures drawn from the shape of the 447 already-removed blobs (bare
Lint.Rule.Bundle.<layer> matching the consumer's own layer, .package(url:) pointed at the matching central rules repo, zero .excluding, zero Configuration overrides, zero inline rules) — must fire.
- Negative: the 8 currently-retained package-specific root configurations (
swift-carrier-primitives, swift-standard-library-extensions, swift-linter-primitives, swift-primitives-linter-rules, swift-standards-linter-rules, swift-institute-linter-rules, swift-linter, swift-linter-rules) — must NOT fire.
- Edge: bundle token present but wrapped in a
.excluding(rules: []) no-op, or with a trailing comment only — must NOT fire (composition is still nominally the default, but the file's presence is the redundancy, not a semantic difference; edge case exists to prove the rule inspects the effective composition, not surface text, without over-firing on true no-ops — implementer to confirm exact intended behavior against #536's stated predicate).
- Near-miss: bundle token correct but an extra disabled-rule override present — must NOT fire (this is package-specific composition, class 6 in #536, not redundant).
Alternatives considered
- Enforce centrally in CI (
swift-ci.yml / institute-continuous-integration) instead of as a linter rule. Rejected as primary path: the census in #536 already scoped this as a rule-pack concern (it searched the four *-linter-rules repos, not CI), and a linter rule composes with institute lint locally the same way every other Institute rule does, giving fix-on-save/pre-commit coverage that a CI-only check would not.
- Do nothing / rely on code review. Rejected — this is exactly the failure mode the wave exists to close; #536 states the wave "did not — and structurally could not — reach completion" without this rule, and manual review does not survive fleet scale (447+ repos).
- Handwritten per-package exception list. Rejected as inconsistent with #536's acceptance criterion 6 (no open-ended path exception or arbitrary local configuration admitted) and the programme's general composition discipline.
Compatibility and migration impact
- Affects every package across
swift-primitives, swift-standards, and swift-foundations that carries a root Lint.swift (currently the 8 retained configs, evaluated by the negative-control fixtures above, plus any future package).
- No impact on packages without a root
Lint.swift (the common case post-wave) — CI/local tooling's absent-file bundle selection, already shipped, is unaffected.
- Ships as an added rule in this repository's existing rule set; consuming repos pick it up through their existing
.package(url:) pin to this repo, same as any other rule addition — no separate adoption step.
Owns acceptance criterion 5 of swift-institute/.github#536 (mechanical rule + fixtures preventing Lint.swift reintroduction fleet-wide). Refs programme #527.
Problem
Fleet-wide removal of redundant "default-bundle"
Lint.swiftfiles (swift-institute/.github#536, programme #527) has no mechanical guard preventing reintroduction. A package-localLint.swiftwhose only import and only trailing-closure content is exactly oneLint.Rule.Bundle.<layer>matching the consumer's own resolved layer — no.excluding, no inlineRule.Configurationentries — reproduces the central default bundle CI and local tooling already select whenLint.swiftis absent. Nothing today rejects that shape: #536's wave-2 census confirmed no rule matching this predicate exists inswift-institute-linter-rules,swift-linter-rules,swift-primitives-linter-rules, orswift-standards-linter-rules. Without this rule, the 447-file convergence wave cannot stay converged — any future package can silently reintroduce the exact redundant file the wave removed.Proposed outcome
A mechanical rule — implemented as an AST rule in this repository, since it must compare a package's own resolved layer (primitives/standards/institute) against the correct central bundle, which is Institute-level composition knowledge — that fails closed when a package-local
Lint.swiftreproduces the central default for its own layer. The rule flags (and can fix-remove) the file. It ships with fixtures proving it fires only on the exact redundant shape:Lint.Rule.Bundle.<layer>matching the consumer's own layer,.package(url:)pointed at the matching central rules repo, zero.excluding, zeroConfigurationoverrides, zero inline rules) — must fire.swift-carrier-primitives,swift-standard-library-extensions,swift-linter-primitives,swift-primitives-linter-rules,swift-standards-linter-rules,swift-institute-linter-rules,swift-linter,swift-linter-rules) — must NOT fire..excluding(rules: [])no-op, or with a trailing comment only — must NOT fire (composition is still nominally the default, but the file's presence is the redundancy, not a semantic difference; edge case exists to prove the rule inspects the effective composition, not surface text, without over-firing on true no-ops — implementer to confirm exact intended behavior against #536's stated predicate).Alternatives considered
swift-ci.yml/institute-continuous-integration) instead of as a linter rule. Rejected as primary path: the census in #536 already scoped this as a rule-pack concern (it searched the four*-linter-rulesrepos, not CI), and a linter rule composes withinstitute lintlocally the same way every other Institute rule does, giving fix-on-save/pre-commit coverage that a CI-only check would not.Compatibility and migration impact
swift-primitives,swift-standards, andswift-foundationsthat carries a rootLint.swift(currently the 8 retained configs, evaluated by the negative-control fixtures above, plus any future package).Lint.swift(the common case post-wave) — CI/local tooling's absent-file bundle selection, already shipped, is unaffected..package(url:)pin to this repo, same as any other rule addition — no separate adoption step.Owns acceptance criterion 5 of swift-institute/.github#536 (mechanical rule + fixtures preventing
Lint.swiftreintroduction fleet-wide). Refs programme #527.