- This template is the full bootstrap `AGENTS.md` used for new Swift package repositories.
AGENTS.md
Baseline Provenance
This template is the full bootstrap AGENTS.md used for new Swift package repositories.
It intentionally incorporates the shared Swift-package baseline from shared/agents-snippets/apple-swift-package-core.md.
Keep baseline guidance aligned with the shared snippet and use this template for deterministic scaffold output.
Repository Expectations
Use Swift Package Manager (SPM) as the source of truth for package structure and dependencies.
Use swift-package-build-run-workflow for manifest, dependency, plugin, resource, Metal-distribution, build, and run work when Package.swift is the source of truth.
Use swift-package-testing-workflow for Swift Testing, XCTest holdouts, .xctestplan, fixtures, and package test diagnosis.
Use sync-swift-package-guidance if this repo's package-specific AGENTS.md guidance later drifts and needs to be refreshed or merged forward.
Re-run sync-swift-package-guidance after substantial package-workflow or plugin updates so local guidance stays aligned.
Use scripts/repo-maintenance/validate-all.sh for local maintainer validation and scripts/repo-maintenance/sync-shared.sh for repo-local sync steps.
Use scripts/repo-maintenance/release.sh --mode standard --version vX.Y.Z from a feature branch or worktree only when the task is actually a protected-main release, publish, merge, tag, or release-PR preparation.
Do not run the standard release workflow from main; when a protected-main release is explicitly requested, let it validate, bump versions, tag, push the branch and tag, open the release PR, watch CI, address valid PR comments or record out-of-scope concerns in ROADMAP.md, merge to protected main, fast-forward local main, and clean up stale branches.
Treat scripts/repo-maintenance/config/profile.env as the installed maintain-project-repo profile marker, and keep it on the swift-package profile for plain package repos.
Prefer swift package CLI commands for structural changes whenever the command exists.
Use swift package add-dependency to add dependencies instead of hand-editing package graphs.
Use swift package add-target to add library, executable, or test targets.
For package configuration not covered by CLI commands, update Package.swift intentionally and keep edits minimal.
Edit Package.swift intentionally and keep it readable; agents may modify it when package structure, targets, products, or dependencies need to change, and should try to keep package graph updates consolidated in one change when possible.
Keep Package.swift explicit about its package-wide Swift language mode. On current Swift 6-era manifests, prefer swiftLanguageModes: [.v6] as the default declaration, treat swiftLanguageVersions as a legacy alias used only when an older manifest surface requires it, and remember that lowering the manifest's // swift-tools-version: from the bootstrap default is often appropriate when the package should support an older Swift 6 toolchain, but never below 6.0.
Keep dependency provenance concise but explicit enough for another contributor to fetch the same package: use package-manager, package-registry, GitHub URL, or other real remote repository requirements, and do not commit machine-local dependency paths such as /Users/..., ~/..., ../..., local worktrees, or private checkout paths. Avoid branch- or revision-based requirements unless the user explicitly asks for that level of control.
Treat Package.resolved and similar package-manager outputs as generated files; do not hand-edit them.
Validate package changes with:
swift build
swift test
Swift Coding Preferences
Prefer the simplest correct Swift that is easiest to read, reason about, and maintain.
Treat idiomatic Swift and Cocoa-style naming conventions as tools in service of readability, not goals by themselves.
Prefer explicit, consistent, and unambiguous names.
For public Swift APIs, treat streamlined, compact, ergonomic call sites as the only acceptable default; do not grow method families, overload sets, or loosely typed entry points when one clear typed API can express the operation.
Prefer optional parameters with explicit default values over additional methods or overloads whenever the difference is optional behavior on the same operation.
When a public function, initializer, or method reaches four or more arguments or parameters, strongly prefer a named typed struct request, options, or configuration value so call sites stay readable and future additions do not multiply overloads.
Prefer enums, enum cases with associated values, and narrow typed values over strings, booleans, sentinel values, or parallel parameters whenever the domain has a closed or meaningful set of choices.
Prefer compact and concise code; use shorthand syntax and trailing-closure syntax when readability improves.
Do not add boilerplate, helper types, or extra layers just to make code look more architectural or more "Swifty".
Strongly prefer synthesized, implicit, and framework-provided behavior over handwritten setup code.
Prefer applicable existing framework or platform error types before inventing custom error wrappers or error hierarchies.
Prefer stable, source-of-truth naming across layers when the data and meaning have not changed.
Treat naming consistency as a reliability feature: if the same data still serves the same purpose, keep the same name.
Do not rename fields just to match local style conventions when the external schema is already clear and stable.
Do not use automatic case-conversion strategies such as .convertFromSnakeCase or .convertToSnakeCase unless the project explicitly wants that behavior and it clearly improves readability overall.
This guidance is optimized for an advanced Swift reader and may prefer dense but readable modern Swift over beginner-style explicitness.
Types and Architecture
Prefer concrete, straightforward types and data flow that keep the code easy to follow.
Use struct, enum, class, actor, and protocols only when each one is the clearest fit for the actual problem.
Mark classes as final by default.
Prefer synthesized conformances (Codable, Equatable, Hashable, etc.) whenever they satisfy the actual requirements.
Prefer memberwise and otherwise synthesized initializers, default property values, and framework defaults over handwritten setup code.
Do not add CodingKeys, manual Codable, custom initializers, wrappers, helper types, protocols, coordinators, or extra layers unless they are required by a concrete constraint or make the final code clearly easier to understand.
When an API, cloud service, or wire format already provides clear names, preserve those names directly in Swift models and nearby code unless the meaning actually changes or a concrete collision must be resolved.
Preserve raw wire and persistence shapes by default; do not add DTO, domain, or view-model conversion layers unless meaning actually changes or a concrete boundary requires it.
Treat redundant wrappers, rename-and-copy layers, and duplicated logic as anti-patterns by default.
Use enums as namespaces only when they genuinely reduce clutter instead of adding indirection.
Keep code modular and cohesive without fragmenting simple logic across unnecessary files or types.
Prefer pure Swift solutions where practical.
Concurrency and Language Mode
Keep code compliant with Swift 6 language mode.
Keep strict concurrency checking enabled.
Use modern structured concurrency (async/await, task groups, actors, AsyncSequence) instead of legacy async patterns when it keeps the flow clearer and more direct.
Make async APIs cancellation-aware, prefer direct async flows over detached work unless isolation must be severed intentionally, and keep sendability concerns explicit.
Prefer compact syntax when it improves local reasoning, including shorthand syntax, ternary expressions, trailing closures, switch, map, filter, forEach, and async iteration.
Prefer explicit default values at initialization when they reduce optional-handling clutter and keep the code easier to follow.
When lines, chains, or expressions get long, prefer chopping them down into a clean vertical, top-down structure with straight visual flow.
For app-facing packages, prefer approachable concurrency defaults with main-actor isolation by default.
Introduce parallelism where it produces clear performance gains.
State, Frameworks, and Dependencies
Prefer @Observation over Combine for observation/state propagation.
For Apple app projects, prefer Apple-native logging facilities first and allow Swift Logging where it makes the project API clearer.
For packages, server-side, or cross-platform Swift, prefer Swift Logging as the primary logging API.
Prefer Swift OpenTelemetry for telemetry and instrumentation when telemetry is needed, and prefer existing ecosystem integrations over bespoke wrappers.
Prefer frameworks and packages from Swift.org, Swift on Server, Apple, and Apple Open Source ecosystems when they simplify the code and make it easier to reason about.
Commonly approved examples include packages such as swift-configuration, swift-async-algorithms, and swift-algorithms.
Testing and Tooling Baseline
Use Swift Testing (import Testing) as the default test framework.
Avoid XCTest unless an external constraint requires it.
Prefer Swift Testing suites, tags, and parameterized coverage on current toolchains, and use Swift Testing confirmations for event-driven asynchronous tests instead of fixed sleeps.
Keep XCTest only when a legacy dependency, package surface, or Apple tooling constraint still requires it.
Prefer a checked-in repo-root .swiftformat file as the Swift formatting source of truth.
Prefer a pre-commit hook such as scripts/repo-maintenance/hooks/pre-commit.sample that formats staged Swift sources and then verifies them with swiftformat --lint before commit.
Treat SwiftLint as an optional complementary signal layer for clarity, safety, and maintainability after SwiftFormat owns formatting shape.
SwiftUI and State Architecture
Treat SwiftUI views as component UI: keep them small, composable, reusable, and easy to scan from top to bottom.
Prefer straight, top-down data flow with small focused controller classes that own matching state for a view or small view cluster.
Do not build monolithic views, monolithic controllers, or broad shared mutable state when a smaller component boundary would be clearer.
Keep updates to view-driving state minimal and localized.
Prefer durable identity for types that drive SwiftUI state and view updates.
Treat App as the application entry and scene composition boundary, Scene as the container for scene-specific lifecycle and environment, and View as the component rendering layer.
Use app-level lifecycle concerns at the App boundary, scene lifecycle concerns at the Scene boundary, and view-local active or presentation behavior inside views.
Use @Binding to pass a focused writable piece of parent-owned state into a child view.
Use @Bindable when working with an observable model that should project bindings to its mutable properties in a view.
Prefer @Query for view-driven SwiftData fetching that should stay in sync with the model context; use explicit fetches only when the view should not be driven by a live query.
Prefer environment values for shared context that truly belongs to the surrounding hierarchy, not as a dumping ground for unrelated dependencies.
Prefer key-path-based APIs, predicates, and sort descriptors when they keep data access direct and readable.
Extract repeated chains of view modifiers into custom view modifiers early when that reduces clutter and clearly matches a view or family of views.
CLI Tooling Preferences
Prefer swift package for package-focused workflows (dependency graph, targets, manifest intent, and local package validation).
Prefer swift package subcommands for structural package edits before manually editing Package.swift.
Keep package manifests on the explicit Swift 6 language-mode default by preserving swiftLanguageModes: [.v6] unless the user asks for a narrower language-mode contract, and treat the generated // swift-tools-version: as a starting point that may be lowered to match the real package compatibility target as long as it stays at 6.0 or newer.
Use swift build and swift test as the default first-pass validation commands.
Keep package resources under the owning target tree, declare them intentionally with Resource.process(...), Resource.copy(...), or Resource.embedInCode(...), and load them through Bundle.module.
Keep test fixtures as test-target resources instead of relying on the working directory.
Bundle precompiled Metal artifacts such as .metallib files as explicit package resources when they ship with the package, and prefer .copy(...) when exact distribution matters.
Use xcodebuild when validating Apple platform integration details that swift package does not cover well (schemes, destinations, SDK-specific behavior, and configuration-specific builds/tests).
Use xcodebuild -showTestPlans and xcodebuild -testPlan ... when the package contract depends on .xctestplan coverage.
Validate both Debug and Release paths when optimization or packaging differences matter, and treat tagged releases as a cue to verify the Release artifact path before publishing.
Keep xcodebuild invocations explicit and reproducible (always pass scheme, destination or SDK, and configuration when relevant).
Prefer deterministic non-interactive CLI usage in automation/CI for both swift package and xcodebuild.