Adopt the rules-swift pin-and-codify strategy in snapshot mode (third_party/rules-swift @97dc70f + sha256), add codified gates, a conformance audit ledger, and .github/workflows/rules-swift.yml (corpus-pin integrity required, full gate report mode). Remove the docs/rules mirror it supersedes (repo-specific files kept under docs/). Style and leak gates exclude the vendored corpus.
4.8 KiB
Critical Rules
Always-loaded core. Five rules that gate every Swift package decision under ExtremePackaging.
Follow the "ExtremePackaging" architecture pattern: a monorepo with maximum granular modularization into distinct SPM packages. Each package represents a single cohesive responsibility with explicit dependencies. This enables isolated compilation, parallel builds, clear dependency graphs, and superior testability.
Rule 1: Single Responsibility per Package
Create packages with one clear purpose:
- A single, well-defined responsibility per package
- Don't mix concerns (UI + networking, models + API client)
- Name communicates the purpose
- Independently buildable and testable
Rule 2: Explicit Dependency Declaration
Declare dependencies explicitly in Package.swift:
- List all dependencies in the package manifest
- No implicit/transitive dependencies
- Minimise cross-package dependencies
- Prefer unidirectional dependency flow
Rule 3: Package Granularity
Prefer smaller, focused packages over larger ones:
- Single-file packages are acceptable when the unit is genuinely standalone (typography, color foundation, a single protocol, a single transport).
- Separate by role, not by topical bundling: foundation primitives, infrastructure, protocols, middleware, services, per-feature or per-CLI-verb operation packages, front-door binaries.
- Each cohesive responsibility (a feature, a CLI verb, a middleware, a transport, a service) gets its own package.
Rule 4: Naming Conventions
Follow a consistent naming scheme. Patterns that apply across project shapes:
- Shared foundation:
Shared*for cross-target value-types, models, utilities, configuration (e.g.SharedModels,SharedUtils,SharedConfiguration). - Core infrastructure:
Core*or descriptive single-purpose names for the foundation domain (protocols, parsers, transports, indexers). - Per-feature or per-verb operation packages: one package per user-facing flow (UI app) or one per CLI/MCP verb (CLI/MCP app). Name after the responsibility, not after a role suffix (
Distribution,Indexer,Ingestin a CLI; the feature's own name in a UI app). - Service packages:
*Serviceor aServicesaggregator for cross-layer read/write services consumed by multiple front-doors. - Aggregators: umbrella packages only when the umbrella adds real value (preview hosts, all-features aggregators for app composition); don't add one by default.
Project-specific conventions (UI component packages, API client/server splits, design-system packages, hot-reload infrastructure) live in the project's own AGENTS.md or rule files, not in this always-loaded universal rule.
Rule 5: Layer Architecture
Organize packages into clear architectural layers with unidirectional dependency flow (bottom → top). The number and naming of layers varies per project shape; the constants are:
- Foundation (bottom): shared value-types, models, primitive utilities, logging.
- Infrastructure: protocols, persistence, networking, file I/O, parsers, transports.
- Domain: services, indexers, business logic, per-feature or per-CLI-verb operation packages.
- Presentation (UI projects only): component packages, design-system packages, screens.
- Front-door (top): binary targets, apps, CLIs, MCP servers, preview hosts, test harnesses.
Every dependency edge points upward. No back-edges. For the active project's actual layer instantiation, read the active manifest: root Package.swift in a package repo, Packages/Package.swift in a project monorepo.
Acceptance check
The five rules are conforming when each is shown to hold for the active package graph:
- The graph is acyclic and resolves. SPM rejects a dependency cycle at resolution, so a clean resolve proves Rule 2's "no circular dependencies" floor:
swift package resolve && swift build
Both MUST exit 0. A cycle makes resolution fail.
-
Dependencies are declared, not transitive. Spot-check that every package a target's sources
importappears in that target'sdependencies:list (Rule 2). No target relies on a symbol reaching it only through another package's import. -
Observable checklist for the rules SPM does not enforce, each a clear pass/fail: every package has one cohesive responsibility, not a grab-bag of unrelated concerns (Rule 1); a package is split rather than grown when a second responsibility appears, and single-file packages are allowed when genuinely standalone (Rule 3); package names follow the scheme (
Shared*foundation,Core*or descriptive infrastructure, per-feature/per-verb operation packages,*Serviceservices, umbrella aggregators only when they add value) (Rule 4); and every dependency edge points upward through the foundation, infrastructure, domain, presentation, front-door layering with no back-edge (Rule 5). A back-edge passesswift buildbut fails this checklist.