From d50e4d74df6ab640c1a2a04479cf30ce3cf0bba6 Mon Sep 17 00:00:00 2001 From: Fernando Fernandes Date: Sat, 19 Sep 2026 14:55:46 +0200 Subject: [PATCH 1/3] Add Swift package compiler settings parity --- .../skills/agent-guidelines-audit/SKILL.md | 15 +++ CHANGELOG.md | 11 +++ Guidelines/Packages.md | 41 ++++++++ Guidelines/Xcode/ProjectSettings.md | 4 + README.md | 4 +- Scripts/validate_guidelines.swift | 50 ++++++++++ Tests/run_tests.swift | 93 +++++++++++++++++++ VERSION | 2 +- 8 files changed, 217 insertions(+), 3 deletions(-) diff --git a/.agents/skills/agent-guidelines-audit/SKILL.md b/.agents/skills/agent-guidelines-audit/SKILL.md index 08a0b75..a05dacb 100644 --- a/.agents/skills/agent-guidelines-audit/SKILL.md +++ b/.agents/skills/agent-guidelines-audit/SKILL.md @@ -50,6 +50,21 @@ For every checked-in `.xcodeproj`, read and apply the shared [Xcode project-sett 5. Before reporting a failure, search the nearest applicable `AGENTS.md` and durable project documentation linked from it for an exception naming the exact setting, scope, concrete incompatibility, replacement, impact, validation, and revisit condition. Accept and report an applicable documented exception; do not infer one from transient discussion, generic project prose, or the existing build setting itself. An application may use `INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = YES` only when that exact exception documents the shipped non-exempt cryptography and resulting export-compliance workflow. 6. When implementation is authorized, move or add compliant values at project level, remove redundant target copies, and rerun build-setting inspection plus relevant builds. For review-only work, report undocumented gaps without editing. +## Audit Swift package settings + +Enumerate every checked-in `Package.swift` that belongs to repository source, ignoring `.build` and other generated build artifacts, and treat each manifest's directory as a package root. For every package, read and apply [the shared package compiler-settings baseline](../../../Guidelines/Packages.md#compiler-settings-baseline); do not infer package rules solely from the Xcode guide. + +1. Identify the selected Swift/Xcode toolchain, inspect `// swift-tools-version:` and `swiftLanguageModes`, and require the newest stable language mode from the shared baseline, currently Swift 6. Reject an older effective mode unless an exact documented exception applies. +2. Run `swift package --package-path dump-package`, or the selected-toolchain equivalent, and use the evaluated manifest to enumerate locally defined targets and their effective settings. Do not rely only on textual grep because manifests may construct or mutate settings programmatically. +3. For every locally defined target that compiles Swift, including production, test, and other supported Swift target kinds, require unconditional `.treatAllWarnings(as: .error)` plus `ExistentialAny`, `InferIsolatedConformances`, `InternalImportsByDefault`, `MemberImportVisibility`, and `NonisolatedNonsendingByDefault` as `.enableUpcomingFeature(...)` settings. Do not require Swift settings on binary or system-library targets. +4. For locally compiled C or Objective-C targets, require the applicable typed `CSetting.treatAllWarnings(as: .error)`; require the corresponding `CXXSetting` where C++ is compiled. Do not require C-family settings when those languages are absent, and do not accept `unsafeFlags` in place of available typed APIs. +5. In Swift 6 mode, treat complete strict concurrency as supplied by the language mode and require a redundant explicit `StrictConcurrency` upcoming-feature setting to be removed. Require other upcoming-feature declarations to be removed when they become unconditional in the selected language mode, especially because warnings-as-errors can promote the resulting diagnostics. +6. Do not require or automatically add `.defaultIsolation(MainActor.self)`. Its absence is compliant with the shared package baseline; source-level actor-isolation review remains a separate Swift and concurrency concern. +7. Search the nearest applicable `AGENTS.md` and linked durable documentation before reporting a package-setting failure. Accept only an exception that names the exact setting or feature, package and targets, incompatibility, replacement or omission, impact, compensating validation where applicable, and revisit condition. Do not infer an exception from the manifest. +8. Run the package's documented validation workflow and, at minimum where applicable, `swift build` and `swift test`. Warnings promoted to errors must leave both warning-clean. When conditions or helper logic make the effective invocation uncertain, use verbose SwiftPM output to verify `-warnings-as-errors`, the language mode, and every `-enable-upcoming-feature` argument. + +For authorized implementation work, repair the manifest and rerun evaluated-manifest inspection plus builds. For review-only work, report the exact missing, conditional, redundant, or conflicting setting without modifying the package. + ## Audit localization When the repository contains localized targets or String Catalogs, read the shared [Localization guide](../../../Guidelines/Localization.md) and the consumer's local translation guidance: diff --git a/CHANGELOG.md b/CHANGELOG.md index 019e186..dcb012e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ All notable changes to this project are documented in this file. +## [0.0.33] - 2026-09-19 + +### Added + +- Added a Swift Package compiler-settings baseline aligned with the applicable Xcode project warning, Swift language-mode, concurrency, and upcoming-feature policies. +- Added completion-audit enforcement for compiler settings in checked-in Swift packages. + +### Changed + +- Required future Xcode compiler-policy changes to evaluate and update Swift Package Manager parity when an equivalent package setting is applicable. + ## [0.0.32] - 2026-09-13 ### Changed diff --git a/Guidelines/Packages.md b/Guidelines/Packages.md index 55b1b17..e7c9f7b 100644 --- a/Guidelines/Packages.md +++ b/Guidelines/Packages.md @@ -69,6 +69,47 @@ The common package baseline is Swift, Xcode, Platforms, License, and CI. Add opt - Put sources under `Sources//` and tests under `Tests/Tests/`. - Keep resources in the target that owns them and use the package bundle for lookup. +## Compiler settings baseline + +Host Xcode build settings are not a substitute for package configuration. Each package must express its applicable compiler policy in `Package.swift` so independently invoked SwiftPM builds, Xcode builds, and dependency builds receive the same strictness. This baseline is the SwiftPM representation of the applicable compiler policy in [Xcode project settings](Xcode/ProjectSettings.md), not a mechanical copy of Xcode build-setting names. + +New packages must use the newest supported Swift tools version. Every package must use the newest stable Swift language mode supported by the selected toolchain, currently Swift 6, preferably owned once at package level: + +```swift +swiftLanguageModes: [.v6] +``` + +Do not repeat `.swiftLanguageMode(.v6)` target by target when package-level ownership is sufficient. A target may specialize the language mode only under a documented package exception. An older manifest that cannot express this baseline must first modernize its tools version; `.treatAllWarnings(as:)` requires PackageDescription 6.2 or later. + +Every locally defined target that compiles Swift, including test targets and other supported Swift target kinds, must unconditionally use the typed PackageDescription API: + +```swift +swiftSettings: [ + .treatAllWarnings(as: .error), + .enableUpcomingFeature("ExistentialAny"), + .enableUpcomingFeature("InferIsolatedConformances"), + .enableUpcomingFeature("InternalImportsByDefault"), + .enableUpcomingFeature("MemberImportVisibility"), + .enableUpcomingFeature("NonisolatedNonsendingByDefault"), +] +``` + +A setting restricted only to Debug, Release, a platform, or another build condition does not satisfy this package-wide baseline unless a documented exception covers that scope. Do not use `unsafeFlags` for warning handling when the typed API is available. + +When a locally compiled target contains C or Objective-C, require `cSettings: [.treatAllWarnings(as: .error)]`. Where C++ settings apply, require `cxxSettings: [.treatAllWarnings(as: .error)]`. Pure-Swift packages do not need C or C++ settings, and packages must not introduce `-Werror` through `unsafeFlags` when the typed APIs are available. + +Swift 6 language mode enables complete concurrency checking unconditionally, so a Swift 6 package must not add `.enableUpcomingFeature("StrictConcurrency")`. For an older package, modernize to the required language mode instead of preserving the legacy mode with a compatibility flag. + +Do not invent a direct SwiftPM equivalent for `SWIFT_APPROACHABLE_CONCURRENCY`. Its applicable opt-in language behavior is represented by the individual upcoming features above, including `InferIsolatedConformances` and `NonisolatedNonsendingByDefault`. + +`.defaultIsolation(MainActor.self)` is intentionally not part of this shared package baseline. Reusable packages must express actor isolation according to their public and internal API semantics rather than inherit an application's default isolation policy. A package may choose a default isolation as an intentional package-specific architectural decision, but the completion audit must not add or require it merely for Xcode-project parity. + +Require the listed upcoming features on every applicable locally defined Swift target, but not on binary or system-library targets that SwiftPM does not compile as Swift source. Reevaluate the list whenever the selected Xcode/Swift toolchain or language mode changes. When a feature becomes unconditional in the selected language mode, remove its `.enableUpcomingFeature(...)` declaration from packages, remove it from this baseline, and update the audit contract in the same change. Retain no redundant upcoming features merely for historical consistency because they can produce diagnostics under warnings-as-errors. + +Metal warnings-as-errors and application `Info.plist` export-compliance declarations have no package equivalent in this baseline. SwiftPM has no first-class Metal warning setting, and a reusable package does not own its consuming application's generated `Info.plist`. + +Accept a deviation only when the nearest applicable `AGENTS.md`, or durable documentation linked from it, records the exact package compiler setting or feature, affected package and targets, concrete incompatibility, replacement or omission, engineering impact, compensating validation where applicable, and condition for revisiting or removing the exception. Do not infer an exception from the existing `Package.swift`. + ## Logging Packages own any diagnostics emitted by their implementation. Follow the shared [logging guide](Logging.md) for AppLogger usage, subsystem identity, package emoji prefixes, domain-owned categories, concise messages, privacy, and test coverage. A consuming application must not reproduce package-internal logs. diff --git a/Guidelines/Xcode/ProjectSettings.md b/Guidelines/Xcode/ProjectSettings.md index 3c29542..9d00f99 100644 --- a/Guidelines/Xcode/ProjectSettings.md +++ b/Guidelines/Xcode/ProjectSettings.md @@ -51,6 +51,10 @@ The Xcode 27 inventory to evaluate is: Treat this list as discovery input for Xcode 27, not as a set of flags that must all be present and not as a permanent exhaustive list. When adopting a newer Xcode, compare its build settings with this prefix and evaluate newly exposed settings against the selected Swift language mode. Require each still-upcoming feature at project level; omit each feature already incorporated into the language mode. +## Swift package parity + +Whenever this Xcode project baseline adds, removes, or changes a Swift or Clang compiler policy, evaluate whether Swift Package Manager exposes a semantically equivalent setting and whether that policy is appropriate for reusable packages. When it is, update the [Swift package compiler-settings baseline](../Packages.md#compiler-settings-baseline), the Swift-package completion audit, and their structural validation in the same change. Do not mechanically copy application-only policy such as `Info.plist` declarations, Metal settings without a SwiftPM equivalent, or default MainActor isolation into the package baseline. + ## Audit procedure 1. Identify the selected Xcode version and its newest stable Swift language mode. diff --git a/README.md b/README.md index 271b720..cec2d20 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ From the consumer repository root, install a tagged release: git subtree add \ --prefix=AgentGuidelines \ https://github.com/thatfactory/agent-guidelines.git \ - 0.0.32 \ + 0.0.33 \ --squash ``` @@ -147,7 +147,7 @@ Review the target release's changelog, then pull it deliberately: git subtree pull \ --prefix=AgentGuidelines \ https://github.com/thatfactory/agent-guidelines.git \ - 0.0.32 \ + 0.0.33 \ --squash ``` diff --git a/Scripts/validate_guidelines.swift b/Scripts/validate_guidelines.swift index 13b0890..f349de2 100755 --- a/Scripts/validate_guidelines.swift +++ b/Scripts/validate_guidelines.swift @@ -112,6 +112,13 @@ let upcomingFeatureSettings = [ "SWIFT_UPCOMING_FEATURE_NONISOLATED_NONSENDING_BY_DEFAULT", "SWIFT_UPCOMING_FEATURE_REGION_BASED_ISOLATION", ] +let packageUpcomingFeatures = [ + "ExistentialAny", + "InferIsolatedConformances", + "InternalImportsByDefault", + "MemberImportVisibility", + "NonisolatedNonsendingByDefault", +] /// Returns all regular-expression matches in a string. func matches(_ pattern: String, in value: String) -> [NSTextCheckingResult] { @@ -525,6 +532,10 @@ func validateXcodeProjectSettingsGuideline(_ errors: inout [String]) { "unit-test and UI-test targets": "test-target effective-value audit", "nearest applicable `AGENTS.md`": "local exception source", "condition for removing or revisiting the exception": "exception lifecycle", + "## Swift package parity": "Swift package parity section", + "../Packages.md#compiler-settings-baseline": "Swift package baseline cross-reference", + "Whenever this Xcode project baseline adds, removes, or changes a Swift or Clang compiler policy": + "package applicability maintenance rule", ] for (value, description) in required where !contents.contains(value) { errors.append("Guidelines/Xcode/ProjectSettings.md: missing \(description): '\(value)'") @@ -535,6 +546,31 @@ func validateXcodeProjectSettingsGuideline(_ errors: inout [String]) { } } +/// Validates the Swift package compiler-settings contract. +func validatePackageCompilerSettingsGuideline(_ errors: inout [String]) { + guard let contents = readText(packagesGuideline, errors: &errors) else { + return + } + let required = [ + "## Compiler settings baseline": "compiler-settings baseline section", + "Xcode/ProjectSettings.md": "Xcode baseline cross-reference", + "swiftLanguageModes": "package-level Swift language mode", + ".treatAllWarnings(as: .error)": "typed warnings-as-errors policy", + "Swift 6 language mode enables complete concurrency checking unconditionally": + "Swift 6 strict-concurrency explanation", + "`.defaultIsolation(MainActor.self)` is intentionally not part": + "default MainActor isolation exclusion", + "Retain no redundant upcoming features": "redundant upcoming-feature prohibition", + "condition for revisiting or removing the exception": "package-setting exception lifecycle", + ] + for (value, description) in required where !contents.contains(value) { + errors.append("Guidelines/Packages.md: missing package compiler policy \(description): '\(value)'") + } + for feature in packageUpcomingFeatures where !contents.contains(".enableUpcomingFeature(\"\(feature)\")") { + errors.append("Guidelines/Packages.md: missing required SwiftPM upcoming feature '\(feature)'") + } +} + /// Validates the shared App Store metadata workflow. func validateAppStoreGuideline(_ errors: inout [String]) { guard let contents = readText(appStoreGuideline, errors: &errors) else { @@ -674,10 +710,23 @@ func validateAuditSkill(_ errors: inout [String]) { "Guidelines/CICD.md": "shared CI/CD guide reference", "fastlane adoption": "forbidden delivery-tooling audit", "missing Swift capability": "non-Swift script exception audit", + "## Audit Swift package settings": "Swift package-settings audit", + "Guidelines/Packages.md#compiler-settings-baseline": "shared package compiler-settings guide reference", + "Package.swift": "package manifest discovery", + "swift package --package-path dump-package": "evaluated manifest inspection", + "swiftLanguageModes": "package language-mode audit", + ".treatAllWarnings(as: .error)": "package warnings-as-errors audit", + ".enableUpcomingFeature(...)": "package upcoming-feature audit", + "complete strict concurrency as supplied by the language mode": "Swift 6 strict-concurrency handling", + ".defaultIsolation(MainActor.self)": "default MainActor isolation exclusion", + "package-setting failure": "package-setting exception lookup", ] for (value, description) in required where !contents.contains(value) { errors.append(".agents/skills/agent-guidelines-audit/SKILL.md: missing \(description): '\(value)'") } + for feature in packageUpcomingFeatures where !contents.contains(feature) { + errors.append(".agents/skills/agent-guidelines-audit/SKILL.md: missing package feature audit '\(feature)'") + } validateExecutable(markdownWrappingScript, description: "Markdown wrapping checker", errors: &errors) validateExecutable(stringCatalogInspectionScript, description: "String Catalog inspection checker", errors: &errors) if let development = readText(developmentGuideline, errors: &errors), @@ -748,6 +797,7 @@ func main() -> Int32 { validateAppStoreGuideline(&errors) validateLocalizationScripts(&errors) validateXcodeProjectSettingsGuideline(&errors) + validatePackageCompilerSettingsGuideline(&errors) validateExternalDependencyPolicy(&errors) validateGitignoreGuidance(&errors) validateExecutable(consumerSetupScript, description: "consumer setup validator", errors: &errors) diff --git a/Tests/run_tests.swift b/Tests/run_tests.swift index 6e87955..e55be74 100755 --- a/Tests/run_tests.swift +++ b/Tests/run_tests.swift @@ -146,6 +146,99 @@ let tests: [(String, () throws -> Void)] = [ } } ), + ( + "repository validator rejects missing package compiler policy", + { + try withTemporaryDirectory { temporary in + let fixture = temporary.appendingPathComponent("repository") + try copyRepositoryFixture(to: fixture) + let guideline = fixture.appendingPathComponent("Guidelines/Packages.md") + var contents = try String(contentsOf: guideline, encoding: .utf8) + contents = contents.replacingOccurrences( + of: ".treatAllWarnings(as: .error)", with: ".warningsAsErrors()") + try write(contents, to: guideline) + let result = try run([fixture.appendingPathComponent("Scripts/validate_guidelines.swift").path]) + try require(!result.succeeded, "missing package warning policy unexpectedly passed") + try require(result.output.contains("missing package compiler policy"), result.output) + } + } + ), + ( + "repository validator rejects package upcoming-feature drift", + { + try withTemporaryDirectory { temporary in + let fixture = temporary.appendingPathComponent("repository") + try copyRepositoryFixture(to: fixture) + let guideline = fixture.appendingPathComponent("Guidelines/Packages.md") + var contents = try String(contentsOf: guideline, encoding: .utf8) + contents = contents.replacingOccurrences( + of: ".enableUpcomingFeature(\"NonisolatedNonsendingByDefault\")", + with: ".enableUpcomingFeature(\"ExampleFeature\")" + ) + try write(contents, to: guideline) + let result = try run([fixture.appendingPathComponent("Scripts/validate_guidelines.swift").path]) + try require(!result.succeeded, "package upcoming-feature drift unexpectedly passed") + try require( + result.output.contains( + "missing required SwiftPM upcoming feature 'NonisolatedNonsendingByDefault'"), + result.output + ) + } + } + ), + ( + "repository validator rejects missing Xcode package-parity maintenance", + { + try withTemporaryDirectory { temporary in + let fixture = temporary.appendingPathComponent("repository") + try copyRepositoryFixture(to: fixture) + let guideline = fixture.appendingPathComponent("Guidelines/Xcode/ProjectSettings.md") + var contents = try String(contentsOf: guideline, encoding: .utf8) + contents = contents.replacingOccurrences( + of: "../Packages.md#compiler-settings-baseline", with: "package-policy") + try write(contents, to: guideline) + let result = try run([fixture.appendingPathComponent("Scripts/validate_guidelines.swift").path]) + try require(!result.succeeded, "missing Xcode package-parity maintenance unexpectedly passed") + try require(result.output.contains("missing Swift package baseline cross-reference"), result.output) + } + } + ), + ( + "repository validator rejects missing package audit section", + { + try withTemporaryDirectory { temporary in + let fixture = temporary.appendingPathComponent("repository") + try copyRepositoryFixture(to: fixture) + let skill = fixture.appendingPathComponent(".agents/skills/agent-guidelines-audit/SKILL.md") + var contents = try String(contentsOf: skill, encoding: .utf8) + contents = contents.replacingOccurrences( + of: "## Audit Swift package settings", with: "## Inspect Swift manifests") + try write(contents, to: skill) + let result = try run([fixture.appendingPathComponent("Scripts/validate_guidelines.swift").path]) + try require(!result.succeeded, "missing package audit section unexpectedly passed") + try require(result.output.contains("missing Swift package-settings audit"), result.output) + } + } + ), + ( + "repository validator rejects package audit behavior drift", + { + try withTemporaryDirectory { temporary in + let fixture = temporary.appendingPathComponent("repository") + try copyRepositoryFixture(to: fixture) + let skill = fixture.appendingPathComponent(".agents/skills/agent-guidelines-audit/SKILL.md") + var contents = try String(contentsOf: skill, encoding: .utf8) + contents = contents.replacingOccurrences( + of: "swift package --package-path dump-package", + with: "inspect the manifest" + ) + try write(contents, to: skill) + let result = try run([fixture.appendingPathComponent("Scripts/validate_guidelines.swift").path]) + try require(!result.succeeded, "package audit behavior drift unexpectedly passed") + try require(result.output.contains("missing evaluated manifest inspection"), result.output) + } + } + ), ( "repository validator rejects missing observability adoption guidance", { diff --git a/VERSION b/VERSION index 78bae5b..cd9d21e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.32 +0.0.33 From ae7fe9eec815c71bc40ee1d40f373e3b3a448410 Mon Sep 17 00:00:00 2001 From: Fernando Fernandes Date: Sat, 19 Sep 2026 15:07:42 +0200 Subject: [PATCH 2/3] Cover SwiftPM compiler policy edge cases --- .../skills/agent-guidelines-audit/SKILL.md | 4 +- Guidelines/Packages.md | 6 +-- Scripts/validate_guidelines.swift | 8 ++++ Tests/run_tests.swift | 43 +++++++++++++++++++ 4 files changed, 56 insertions(+), 5 deletions(-) diff --git a/.agents/skills/agent-guidelines-audit/SKILL.md b/.agents/skills/agent-guidelines-audit/SKILL.md index a05dacb..75510ae 100644 --- a/.agents/skills/agent-guidelines-audit/SKILL.md +++ b/.agents/skills/agent-guidelines-audit/SKILL.md @@ -56,9 +56,9 @@ Enumerate every checked-in `Package.swift` that belongs to repository source, ig 1. Identify the selected Swift/Xcode toolchain, inspect `// swift-tools-version:` and `swiftLanguageModes`, and require the newest stable language mode from the shared baseline, currently Swift 6. Reject an older effective mode unless an exact documented exception applies. 2. Run `swift package --package-path dump-package`, or the selected-toolchain equivalent, and use the evaluated manifest to enumerate locally defined targets and their effective settings. Do not rely only on textual grep because manifests may construct or mutate settings programmatically. -3. For every locally defined target that compiles Swift, including production, test, and other supported Swift target kinds, require unconditional `.treatAllWarnings(as: .error)` plus `ExistentialAny`, `InferIsolatedConformances`, `InternalImportsByDefault`, `MemberImportVisibility`, and `NonisolatedNonsendingByDefault` as `.enableUpcomingFeature(...)` settings. Do not require Swift settings on binary or system-library targets. +3. For every locally defined target that compiles Swift and for which SwiftPM exposes `swiftSettings`, including production, test, and other applicable Swift target kinds, require unconditional `.treatAllWarnings(as: .error)` plus `ExistentialAny`, `InferIsolatedConformances`, `InternalImportsByDefault`, `MemberImportVisibility`, and `NonisolatedNonsendingByDefault` as `.enableUpcomingFeature(...)` settings. Do not require Swift settings on binary targets, system-library targets, or package plug-in targets because `Target.plugin(...)` does not expose `swiftSettings`. 4. For locally compiled C or Objective-C targets, require the applicable typed `CSetting.treatAllWarnings(as: .error)`; require the corresponding `CXXSetting` where C++ is compiled. Do not require C-family settings when those languages are absent, and do not accept `unsafeFlags` in place of available typed APIs. -5. In Swift 6 mode, treat complete strict concurrency as supplied by the language mode and require a redundant explicit `StrictConcurrency` upcoming-feature setting to be removed. Require other upcoming-feature declarations to be removed when they become unconditional in the selected language mode, especially because warnings-as-errors can promote the resulting diagnostics. +5. In Swift 6 mode, treat complete strict concurrency as supplied by the language mode and require every redundant explicit `StrictConcurrency` opt-in to be removed, including `.enableUpcomingFeature("StrictConcurrency")`, `.enableExperimentalFeature("StrictConcurrency")`, and `StrictConcurrency=complete` representations found in the evaluated manifest or effective compiler arguments. Require other upcoming-feature declarations to be removed when they become unconditional in the selected language mode, especially because warnings-as-errors can promote the resulting diagnostics. 6. Do not require or automatically add `.defaultIsolation(MainActor.self)`. Its absence is compliant with the shared package baseline; source-level actor-isolation review remains a separate Swift and concurrency concern. 7. Search the nearest applicable `AGENTS.md` and linked durable documentation before reporting a package-setting failure. Accept only an exception that names the exact setting or feature, package and targets, incompatibility, replacement or omission, impact, compensating validation where applicable, and revisit condition. Do not infer an exception from the manifest. 8. Run the package's documented validation workflow and, at minimum where applicable, `swift build` and `swift test`. Warnings promoted to errors must leave both warning-clean. When conditions or helper logic make the effective invocation uncertain, use verbose SwiftPM output to verify `-warnings-as-errors`, the language mode, and every `-enable-upcoming-feature` argument. diff --git a/Guidelines/Packages.md b/Guidelines/Packages.md index e7c9f7b..c33cfbb 100644 --- a/Guidelines/Packages.md +++ b/Guidelines/Packages.md @@ -81,7 +81,7 @@ swiftLanguageModes: [.v6] Do not repeat `.swiftLanguageMode(.v6)` target by target when package-level ownership is sufficient. A target may specialize the language mode only under a documented package exception. An older manifest that cannot express this baseline must first modernize its tools version; `.treatAllWarnings(as:)` requires PackageDescription 6.2 or later. -Every locally defined target that compiles Swift, including test targets and other supported Swift target kinds, must unconditionally use the typed PackageDescription API: +Every locally defined target that compiles Swift and for which SwiftPM exposes `swiftSettings`, including test targets and other applicable Swift target kinds, must unconditionally use the typed PackageDescription API: ```swift swiftSettings: [ @@ -98,13 +98,13 @@ A setting restricted only to Debug, Release, a platform, or another build condit When a locally compiled target contains C or Objective-C, require `cSettings: [.treatAllWarnings(as: .error)]`. Where C++ settings apply, require `cxxSettings: [.treatAllWarnings(as: .error)]`. Pure-Swift packages do not need C or C++ settings, and packages must not introduce `-Werror` through `unsafeFlags` when the typed APIs are available. -Swift 6 language mode enables complete concurrency checking unconditionally, so a Swift 6 package must not add `.enableUpcomingFeature("StrictConcurrency")`. For an older package, modernize to the required language mode instead of preserving the legacy mode with a compatibility flag. +Swift 6 language mode enables complete concurrency checking unconditionally, so a Swift 6 package must not retain any explicit `StrictConcurrency` opt-in. Remove `.enableUpcomingFeature("StrictConcurrency")`, `.enableExperimentalFeature("StrictConcurrency")`, and `StrictConcurrency=complete` spellings instead of treating one representation as special. For an older package, modernize to the required language mode instead of preserving the legacy mode with a compatibility flag. Do not invent a direct SwiftPM equivalent for `SWIFT_APPROACHABLE_CONCURRENCY`. Its applicable opt-in language behavior is represented by the individual upcoming features above, including `InferIsolatedConformances` and `NonisolatedNonsendingByDefault`. `.defaultIsolation(MainActor.self)` is intentionally not part of this shared package baseline. Reusable packages must express actor isolation according to their public and internal API semantics rather than inherit an application's default isolation policy. A package may choose a default isolation as an intentional package-specific architectural decision, but the completion audit must not add or require it merely for Xcode-project parity. -Require the listed upcoming features on every applicable locally defined Swift target, but not on binary or system-library targets that SwiftPM does not compile as Swift source. Reevaluate the list whenever the selected Xcode/Swift toolchain or language mode changes. When a feature becomes unconditional in the selected language mode, remove its `.enableUpcomingFeature(...)` declaration from packages, remove it from this baseline, and update the audit contract in the same change. Retain no redundant upcoming features merely for historical consistency because they can produce diagnostics under warnings-as-errors. +Require the listed upcoming features on every applicable locally defined Swift target, but not on binary or system-library targets that SwiftPM does not compile as Swift source or package plug-in targets for which `Target.plugin(...)` does not expose `swiftSettings`. Reevaluate the list whenever the selected Xcode/Swift toolchain or language mode changes. When a feature becomes unconditional in the selected language mode, remove its `.enableUpcomingFeature(...)` declaration from packages, remove it from this baseline, and update the audit contract in the same change. Retain no redundant upcoming features merely for historical consistency because they can produce diagnostics under warnings-as-errors. Metal warnings-as-errors and application `Info.plist` export-compliance declarations have no package equivalent in this baseline. SwiftPM has no first-class Metal warning setting, and a reusable package does not own its consuming application's generated `Info.plist`. diff --git a/Scripts/validate_guidelines.swift b/Scripts/validate_guidelines.swift index f349de2..7031287 100755 --- a/Scripts/validate_guidelines.swift +++ b/Scripts/validate_guidelines.swift @@ -558,8 +558,13 @@ func validatePackageCompilerSettingsGuideline(_ errors: inout [String]) { ".treatAllWarnings(as: .error)": "typed warnings-as-errors policy", "Swift 6 language mode enables complete concurrency checking unconditionally": "Swift 6 strict-concurrency explanation", + ".enableExperimentalFeature(\"StrictConcurrency\")": + "experimental StrictConcurrency redundancy rule", + "StrictConcurrency=complete": "explicit StrictConcurrency redundancy rule", "`.defaultIsolation(MainActor.self)` is intentionally not part": "default MainActor isolation exclusion", + "package plug-in targets for which `Target.plugin(...)` does not expose `swiftSettings`": + "unsupported plug-in target exclusion", "Retain no redundant upcoming features": "redundant upcoming-feature prohibition", "condition for revisiting or removing the exception": "package-setting exception lifecycle", ] @@ -718,7 +723,10 @@ func validateAuditSkill(_ errors: inout [String]) { ".treatAllWarnings(as: .error)": "package warnings-as-errors audit", ".enableUpcomingFeature(...)": "package upcoming-feature audit", "complete strict concurrency as supplied by the language mode": "Swift 6 strict-concurrency handling", + ".enableExperimentalFeature(\"StrictConcurrency\")": "experimental StrictConcurrency removal audit", + "StrictConcurrency=complete": "explicit StrictConcurrency removal audit", ".defaultIsolation(MainActor.self)": "default MainActor isolation exclusion", + "package plug-in targets": "unsupported plug-in target exclusion", "package-setting failure": "package-setting exception lookup", ] for (value, description) in required where !contents.contains(value) { diff --git a/Tests/run_tests.swift b/Tests/run_tests.swift index e55be74..80f0ce7 100755 --- a/Tests/run_tests.swift +++ b/Tests/run_tests.swift @@ -186,6 +186,49 @@ let tests: [(String, () throws -> Void)] = [ } } ), + ( + "repository validator rejects missing package plug-in exclusion", + { + try withTemporaryDirectory { temporary in + let fixture = temporary.appendingPathComponent("repository") + try copyRepositoryFixture(to: fixture) + let guideline = fixture.appendingPathComponent("Guidelines/Packages.md") + var contents = try String(contentsOf: guideline, encoding: .utf8) + contents = contents.replacingOccurrences( + of: "package plug-in targets for which `Target.plugin(...)` does not expose `swiftSettings`", + with: "other package targets" + ) + try write(contents, to: guideline) + let result = try run([fixture.appendingPathComponent("Scripts/validate_guidelines.swift").path]) + try require(!result.succeeded, "missing package plug-in exclusion unexpectedly passed") + try require( + result.output.contains("missing package compiler policy unsupported plug-in target exclusion"), + result.output) + } + } + ), + ( + "repository validator rejects missing experimental StrictConcurrency rule", + { + try withTemporaryDirectory { temporary in + let fixture = temporary.appendingPathComponent("repository") + try copyRepositoryFixture(to: fixture) + let guideline = fixture.appendingPathComponent("Guidelines/Packages.md") + var contents = try String(contentsOf: guideline, encoding: .utf8) + contents = contents.replacingOccurrences( + of: ".enableExperimentalFeature(\"StrictConcurrency\")", + with: ".enableExperimentalFeature(\"ExampleFeature\")" + ) + try write(contents, to: guideline) + let result = try run([fixture.appendingPathComponent("Scripts/validate_guidelines.swift").path]) + try require(!result.succeeded, "missing experimental StrictConcurrency rule unexpectedly passed") + try require( + result.output.contains( + "missing package compiler policy experimental StrictConcurrency redundancy rule"), result.output + ) + } + } + ), ( "repository validator rejects missing Xcode package-parity maintenance", { From 6b8be7f06dd8ae9c74da354a536ed1db88836665 Mon Sep 17 00:00:00 2001 From: Fernando Fernandes Date: Sat, 19 Sep 2026 15:12:05 +0200 Subject: [PATCH 3/3] Pin StrictConcurrency audit forms --- Scripts/validate_guidelines.swift | 3 +++ Tests/run_tests.swift | 44 +++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/Scripts/validate_guidelines.swift b/Scripts/validate_guidelines.swift index 7031287..39b58d6 100755 --- a/Scripts/validate_guidelines.swift +++ b/Scripts/validate_guidelines.swift @@ -558,6 +558,8 @@ func validatePackageCompilerSettingsGuideline(_ errors: inout [String]) { ".treatAllWarnings(as: .error)": "typed warnings-as-errors policy", "Swift 6 language mode enables complete concurrency checking unconditionally": "Swift 6 strict-concurrency explanation", + ".enableUpcomingFeature(\"StrictConcurrency\")": + "upcoming StrictConcurrency redundancy rule", ".enableExperimentalFeature(\"StrictConcurrency\")": "experimental StrictConcurrency redundancy rule", "StrictConcurrency=complete": "explicit StrictConcurrency redundancy rule", @@ -723,6 +725,7 @@ func validateAuditSkill(_ errors: inout [String]) { ".treatAllWarnings(as: .error)": "package warnings-as-errors audit", ".enableUpcomingFeature(...)": "package upcoming-feature audit", "complete strict concurrency as supplied by the language mode": "Swift 6 strict-concurrency handling", + ".enableUpcomingFeature(\"StrictConcurrency\")": "upcoming StrictConcurrency removal audit", ".enableExperimentalFeature(\"StrictConcurrency\")": "experimental StrictConcurrency removal audit", "StrictConcurrency=complete": "explicit StrictConcurrency removal audit", ".defaultIsolation(MainActor.self)": "default MainActor isolation exclusion", diff --git a/Tests/run_tests.swift b/Tests/run_tests.swift index 80f0ce7..847eeab 100755 --- a/Tests/run_tests.swift +++ b/Tests/run_tests.swift @@ -229,6 +229,50 @@ let tests: [(String, () throws -> Void)] = [ } } ), + ( + "repository validator rejects missing upcoming StrictConcurrency rule", + { + try withTemporaryDirectory { temporary in + let fixture = temporary.appendingPathComponent("repository") + try copyRepositoryFixture(to: fixture) + let guideline = fixture.appendingPathComponent("Guidelines/Packages.md") + var contents = try String(contentsOf: guideline, encoding: .utf8) + contents = contents.replacingOccurrences( + of: ".enableUpcomingFeature(\"StrictConcurrency\")", + with: ".enableUpcomingFeature(\"ExampleFeature\")" + ) + try write(contents, to: guideline) + let result = try run([fixture.appendingPathComponent("Scripts/validate_guidelines.swift").path]) + try require(!result.succeeded, "missing upcoming StrictConcurrency rule unexpectedly passed") + try require( + result.output.contains( + "missing package compiler policy upcoming StrictConcurrency redundancy rule"), result.output + ) + } + } + ), + ( + "repository validator rejects missing explicit StrictConcurrency rule", + { + try withTemporaryDirectory { temporary in + let fixture = temporary.appendingPathComponent("repository") + try copyRepositoryFixture(to: fixture) + let guideline = fixture.appendingPathComponent("Guidelines/Packages.md") + var contents = try String(contentsOf: guideline, encoding: .utf8) + contents = contents.replacingOccurrences( + of: "StrictConcurrency=complete", + with: "StrictConcurrency=minimal" + ) + try write(contents, to: guideline) + let result = try run([fixture.appendingPathComponent("Scripts/validate_guidelines.swift").path]) + try require(!result.succeeded, "missing explicit StrictConcurrency rule unexpectedly passed") + try require( + result.output.contains( + "missing package compiler policy explicit StrictConcurrency redundancy rule"), result.output + ) + } + } + ), ( "repository validator rejects missing Xcode package-parity maintenance", {