# 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`.