Skip to content

Commit 702190d

Browse files
authored
chore: add AGENTS.md for AI coding agent instructions (#4177)
* chore: add AGENTS.md for AI coding agent instructions Comprehensive guide for AI agents working on the repository covering project structure, coding conventions, category/plugin architecture, the new Amplify Clients building block pattern (Foundation, Bridge, Kinesis reference implementation), testing, CI/CD, and common tasks. * chore: split AGENTS.md into root + sub-package guides Restructure to keep root AGENTS.md lean (~130 lines) and move detailed conventions into sub-package files that agents only load when working in those areas: - AGENTS.md — project overview, linting, structure, conventions - Amplify/AGENTS.md — core framework, categories, plugin protocols - AmplifyPlugins/AGENTS.md — plugin implementations, file patterns - AmplifyClients/AGENTS.md — Foundation, Bridge, Kinesis, new client guide * chore: address PR review comments - Remove detailed linting rules from root (agent just runs the linter) - Remove "Add API" and "Add plugin" from common tasks (not typical agent work) - Soften Amplify core dependency rule for clients (may use Amplify types) - Remove internal architecture section from clients guide (not helpful) - Clarify zero AWS SDK deps is specific to core, plugins have SDK deps
1 parent 18cab30 commit 702190d

4 files changed

Lines changed: 467 additions & 0 deletions

File tree

AGENTS.md

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# AGENTS.md — Amplify Library for Swift
2+
3+
## Project Info
4+
5+
- **Language**: Swift 5.9+ | **Build**: SPM (Xcode 16.0+) | **No CocoaPods**
6+
- **Platforms**: iOS 13+, macOS 12+, tvOS 13+, watchOS 9+, visionOS 1+
7+
- **Architecture**: Monorepo — core framework (`Amplify/`), category plugins (`AmplifyPlugins/`), standalone clients (`AmplifyClients/`)
8+
- **Setup**: `open Package.swift` or `swift package resolve`
9+
10+
## Sub-Package Guides
11+
12+
Detailed conventions and patterns live closer to the code. Read the relevant guide when working in that area:
13+
14+
- [`Amplify/AGENTS.md`](Amplify/AGENTS.md) — Core framework: categories, plugin protocols, error handling, configuration, Hub
15+
- [`AmplifyPlugins/AGENTS.md`](AmplifyPlugins/AGENTS.md) — Plugin implementations: Auth, API, Storage, DataStore, etc.
16+
- [`AmplifyClients/AGENTS.md`](AmplifyClients/AGENTS.md) — Standalone clients: Foundation, Bridge, Kinesis (new pattern)
17+
18+
## Linting & Formatting (MUST pass)
19+
20+
Run before committing. CI enforces both. See `.swiftlint.yml` and `.swiftformat` for full rules.
21+
22+
```bash
23+
swiftformat . # Format first
24+
swiftlint --fix # Then lint
25+
```
26+
27+
## License Header (Required on ALL Swift files)
28+
29+
```swift
30+
//
31+
// Copyright Amazon.com Inc. or its affiliates.
32+
// All Rights Reserved.
33+
//
34+
// SPDX-License-Identifier: Apache-2.0
35+
//
36+
```
37+
38+
## Repository Structure
39+
40+
```
41+
amplify-swift/
42+
├── Amplify/ # Core framework (categories, protocols, errors, config)
43+
├── AmplifyPlugins/ # AWS service plugins (Auth, API, Storage, DataStore, etc.)
44+
├── AmplifyClients/ # Standalone AWS clients (Kinesis) — new pattern
45+
├── AmplifyFoundation/ # Shared protocols (credentials, logging, errors) — no deps
46+
├── AmplifyFoundationBridge/ # Foundation ↔ AWS SDK adapters
47+
├── AmplifyTests/ # Core unit tests
48+
├── AmplifyTestCommon/ # Shared test utilities
49+
├── AmplifyAsyncTesting/ # Async test helpers
50+
├── Package.swift # All SPM targets defined here
51+
├── .swiftlint.yml / .swiftformat
52+
└── CONTRIBUTING.md / ETHOS.md
53+
```
54+
55+
## Key Architectural Concepts
56+
57+
**Two building block patterns exist in this repo:**
58+
59+
1. **Category Plugins** (`Amplify/` + `AmplifyPlugins/`) — Pluggable architecture via `Amplify.configure()`. Categories define behavior protocols, plugins implement them. See [`Amplify/AGENTS.md`](Amplify/AGENTS.md).
60+
61+
2. **Amplify Clients** (`AmplifyClients/` + `AmplifyFoundation/` + `AmplifyFoundationBridge/`) — Standalone AWS clients independent of core Amplify. Actor-based, strict concurrency, protocol-driven. See [`AmplifyClients/AGENTS.md`](AmplifyClients/AGENTS.md).
62+
63+
## Concurrency Rules
64+
65+
- **Prefer** async/await and structured concurrency for all new code
66+
- **Use** `actor` for mutable shared state
67+
- **Avoid** new `DispatchQueue` or callback patterns
68+
- **Do not** use Combine in the core library
69+
- New Amplify Clients **must** enable `StrictConcurrency`
70+
71+
## Error Handling
72+
73+
All errors conform to `AmplifyError` — requires `errorDescription`, `recoverySuggestion`, and `underlyingError`. Category-specific error enums with associated values. Never throw raw `Error` or `NSError`.
74+
75+
## Commit Conventions
76+
77+
[Conventional Commits](https://www.conventionalcommits.org) — enforced via PR title (auto-generates changelog):
78+
79+
```
80+
feat(storage): add progress stall timeout for S3 uploads
81+
fix(api): populate auth mode when parsing request response
82+
chore: update aws-swift-sdk dependency
83+
```
84+
85+
**Types**: `feat`, `fix`, `chore`, `refactor`, `test`, `docs`, `perf`, `ci`
86+
**Scopes**: `auth`, `api`, `storage`, `datastore`, `geo`, `analytics`, `logging`, `predictions`, `push`, `kinesis`, `core`, `foundation`
87+
88+
No period at end. One feature/bugfix per PR. Reference issues: `fixes #<issue>`.
89+
90+
## Testing
91+
92+
```bash
93+
swift test # All unit tests
94+
swift test --filter AWSCognitoAuthPluginUnitTests # Specific target
95+
```
96+
97+
- **Unit tests**: XCTest, defined in Package.swift (19 test targets)
98+
- **Integration tests**: Xcode host app projects under `AmplifyPlugins/<Category>/Tests/<Category>HostApp/`
99+
- **Conventions**: Mock via behavior protocols, use `AmplifyTestCommon` for shared utilities, `AmplifyAsyncTesting` for async helpers
100+
- Every change requires new or updated tests
101+
102+
## Semver
103+
104+
New enum cases = **minor** bump. Breaking API changes = **major** (rare, needs approval). API surface tracked via `api-dump/` JSON snapshots and CI checks.
105+
106+
## CI/CD
107+
108+
60+ GitHub Actions workflows in `.github/workflows/`: per-category unit tests (`unit_test_*.yml`), integration tests (`integ_test_*.yml`), platform builds, SwiftLint/SwiftFormat checks, API digester, CodeQL, Fortify. Releases via Fastlane.
109+
110+
## Common Agent Tasks
111+
112+
| Task | Key steps |
113+
|------|-----------|
114+
| **Add Amplify Client** | See [`AmplifyClients/AGENTS.md`](AmplifyClients/AGENTS.md) — use Foundation/Bridge, actors, strict concurrency |
115+
| **Fix bug** | Write failing test → fix → verify all tests pass → `fix(<scope>): <desc>` |
116+
117+
## Key Files
118+
119+
| File | Purpose |
120+
|------|---------|
121+
| `Package.swift` | All targets, deps, products |
122+
| `CONTRIBUTING.md` | Contributor guidelines |
123+
| `ETHOS.md` | Design philosophy |
124+
| `Amplify/Core/Support/AmplifyError.swift` | Base error protocol |
125+
| `Amplify/Core/Plugin/Plugin.swift` | Base plugin protocol |
126+
| `Amplify/Core/Configuration/AmplifyConfiguration.swift` | Configuration system |

Amplify/AGENTS.md

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# Amplify Core — Agent Guide
2+
3+
## Overview
4+
5+
`Amplify/` is the core framework that defines the public API surface. It contains **category interfaces**, **plugin protocols**, **error types**, **configuration**, and the **Hub event system**. The core module itself has zero AWS SDK dependencies — plugins (in `AmplifyPlugins/`) are where AWS SDK dependencies live.
6+
7+
## Category/Plugin Architecture
8+
9+
Amplify uses a **Category + Plugin** pattern. Each service domain (Auth, Storage, API, etc.) is a "category":
10+
11+
- **`<Cat>CategoryBehavior`** — protocol with client-facing API methods (all `async throws`)
12+
- **`<Cat>CategoryPlugin`** — protocol extending `Plugin` + behavior (what plugins implement)
13+
- **`<Cat>Category`** — concrete class routing calls to the registered plugin
14+
15+
```swift
16+
public protocol StorageCategoryBehavior {
17+
func getURL(path: any StoragePath, options: ...) async throws -> URL
18+
func downloadData(path: any StoragePath, options: ...) -> StorageDownloadDataTask
19+
func uploadData(path: any StoragePath, data: Data, options: ...) -> StorageUploadDataTask
20+
func remove(path: any StoragePath, options: ...) async throws -> String
21+
func list(path: any StoragePath, options: ...) async throws -> StorageListResult
22+
}
23+
24+
public protocol StorageCategoryPlugin: Plugin, StorageCategoryBehavior {}
25+
```
26+
27+
## Directory Structure
28+
29+
```
30+
Amplify/
31+
├── Categories/ # One subdir per category
32+
│ ├── Analytics/ # AnalyticsCategoryBehavior, AnalyticsCategoryPlugin
33+
│ ├── API/ # APICategoryBehavior (GraphQL + REST)
34+
│ ├── Auth/ # AuthCategoryBehavior (sign-in, sign-up, session, MFA)
35+
│ ├── DataStore/ # DataStoreCategoryBehavior (sync, query, observe)
36+
│ ├── Geo/ # GeoCategoryBehavior (search, maps)
37+
│ ├── Hub/ # Built-in pub/sub event system (no plugin needed)
38+
│ ├── Logging/ # LoggingCategoryBehavior
39+
│ ├── Notifications/ # Push notification behavior
40+
│ ├── Predictions/ # ML prediction behavior (text, vision, speech)
41+
│ └── Storage/ # StorageCategoryBehavior (upload, download, list)
42+
├── Core/
43+
│ ├── Category/ # Base Category protocol
44+
│ ├── Configuration/ # AmplifyConfiguration, category configs
45+
│ ├── Error/ # ConfigurationError, PluginError
46+
│ ├── Internal/ # Private utilities
47+
│ ├── Model/ # Model, Schema, Field definitions (DataStore)
48+
│ ├── Plugin/ # Plugin protocol, PluginKey, Resettable
49+
│ └── Support/ # AmplifyError protocol, utilities
50+
├── DefaultPlugins/ # Built-in default plugin implementations
51+
└── DevMenu/ # Developer debug menu
52+
```
53+
54+
## Plugin Protocol
55+
56+
Every plugin must conform to:
57+
58+
```swift
59+
public protocol Plugin: CategoryTypeable, Resettable {
60+
var key: PluginKey { get }
61+
func configure(using configuration: Any?) throws
62+
}
63+
64+
public protocol Resettable {
65+
func reset() async
66+
}
67+
```
68+
69+
## Error Handling
70+
71+
All errors conform to `AmplifyError`:
72+
73+
```swift
74+
public protocol AmplifyError: Error, CustomDebugStringConvertible {
75+
var errorDescription: ErrorDescription { get }
76+
var recoverySuggestion: RecoverySuggestion { get }
77+
var underlyingError: Error? { get }
78+
init(errorDescription: ErrorDescription, recoverySuggestion: RecoverySuggestion, error: Error)
79+
}
80+
```
81+
82+
Category errors are enums with associated values (e.g., `StorageError.accessDenied(desc, suggestion, error?)`). Always include actionable recovery suggestions. Wrap underlying errors; never discard them.
83+
84+
## Configuration Lifecycle
85+
86+
1. `Amplify.configure()` loads from `amplifyconfiguration.json` or programmatic config
87+
2. Logging configured **first** (all plugins depend on it)
88+
3. Hub and Auth configured **next** (other categories depend on Auth)
89+
4. Remaining categories configured in order
90+
5. `HubPayload.EventName.Amplify.configured` dispatched to all Hub channels
91+
92+
## Hub (Event System)
93+
94+
Built-in pub/sub — no plugin required:
95+
96+
```swift
97+
Amplify.Hub.dispatch(to: .auth, payload: HubPayload(eventName: .signedIn))
98+
let token = Amplify.Hub.listen(to: .auth) { payload in ... }
99+
```
100+
101+
## Naming & Documentation Conventions
102+
103+
- Extensions split into `TypeName+Concern.swift` files
104+
- Doc comments required on all public APIs (`///` style)
105+
- Source tags: `/// - Tag: StorageCategoryBehavior.getURL`
106+
- Deprecation: `@available(*, deprecated, message: "Use newMethod()")`
107+
108+
## API Surface Stability
109+
110+
API dumps in `api-dump/*.json` track the public surface. Breaking changes detected by CI (`api_digester_check.yml`). New enum cases = minor bump; removing/renaming public APIs = major bump (needs approval).
111+
112+
## Adding a New API to a Category
113+
114+
1. Add method to `Categories/<Cat>/<Cat>CategoryBehavior.swift`
115+
2. Add routing in `Categories/<Cat>/<Cat>Category+ClientBehavior.swift`
116+
3. Update plugin protocol if distinct
117+
4. Implement in the plugin (see `AmplifyPlugins/AGENTS.md`)
118+
5. Add tests, update API dump

AmplifyClients/AGENTS.md

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# AmplifyClients — Agent Guide
2+
3+
## Overview
4+
5+
Amplify Clients are **standalone AWS service clients** independent of the core `Amplify` framework. Unlike category plugins (which require `Amplify.configure()` and the plugin system), Amplify Clients are self-contained libraries that can be used directly.
6+
7+
## Architecture
8+
9+
```
10+
┌─────────────────────────────────────────────────────────┐
11+
│ Amplify Clients (e.g., AmplifyKinesisClient) │
12+
│ High-level APIs, actor-based, local caching, retry │
13+
├─────────────────────────────────────────────────────────┤
14+
│ AmplifyFoundation (Protocol Layer) │
15+
│ Pure Swift protocols — zero external deps │
16+
│ Credentials, Logging, Errors, Metadata │
17+
├─────────────────────────────────────────────────────────┤
18+
│ AmplifyFoundationBridge (Adapter Layer) │
19+
│ Foundation ↔ AWS SDK type adapters │
20+
│ Credential converters, User-Agent injection │
21+
├─────────────────────────────────────────────────────────┤
22+
│ AWS SDK (aws-sdk-swift) │
23+
└─────────────────────────────────────────────────────────┘
24+
```
25+
26+
**Current rule**: Clients depend on `AmplifyFoundation` + `AmplifyFoundationBridge`. They may eventually depend on types from `Amplify` core (e.g., error types), but should avoid depending on `AWSPluginsCore` or the plugin registration system.
27+
28+
## AmplifyFoundation (`AmplifyFoundation/Sources/`)
29+
30+
Zero-dependency protocol layer providing:
31+
32+
**Credentials**`AWSCredentials`, `AWSTemporaryCredentials`, `AWSCredentialsProvider` (async resolution)
33+
34+
**Logging**`Logger` protocol with `error`/`warn`/`info`/`debug`/`verbose` levels. `BroadcastLogger` routes to multiple `LogSinkBehavior` sinks. `AmplifyOSLogSink` provides os.log integration.
35+
36+
**Errors**`AmplifyError` protocol requiring `errorDescription` + `recoverySuggestion` + `underlyingError`
37+
38+
**Metadata**`AmplifyMetadata.version` and `.platformName`
39+
40+
Design: zero framework coupling, no external deps, async/await first, `Sendable` everywhere.
41+
42+
## AmplifyFoundationBridge (`AmplifyFoundationBridge/Sources/`)
43+
44+
Depends on: `AmplifyFoundation`, `AWSClientRuntime`
45+
46+
**Credential adapters** — Four bidirectional adapters:
47+
- `FoundationToSDKCredentialsAdapter` / `SDKToFoundationCredentialsAdapter` (Smithy)
48+
- `FoundationToCRTCredentialsAdapter` / `CRTToFoundationCredentialsAdapter` (CRT)
49+
50+
Each conforms to both Foundation and SDK protocols simultaneously.
51+
52+
**`UserAgentClientEngine`** — HTTP client wrapper that injects `lib/amplify-swift#<version>` into User-Agent headers.
53+
54+
## AmplifyKinesisClient (Reference Implementation)
55+
56+
Location: `AmplifyClients/AmplifyKinesisClient/`
57+
Deps: `AmplifyFoundation`, `AmplifyFoundationBridge`, `SQLite.swift`, `AWSKinesis`
58+
59+
### Public API
60+
61+
```swift
62+
public class AmplifyKinesisClient {
63+
init(region: String, credentialsProvider: AWSCredentialsProvider, options: Options)
64+
func record(data: Data, partitionKey: String, streamName: String) async throws -> RecordData
65+
func flush() async throws -> FlushData
66+
func enable() async / func disable() async
67+
func clearCache() async throws -> ClearCacheData
68+
func getKinesisClient() -> AWSKinesis.KinesisClient // escape hatch
69+
}
70+
```
71+
72+
### Error Type
73+
74+
```swift
75+
public enum KinesisError: AmplifyError {
76+
case cache(ErrorDescription, RecoverySuggestion, Error?)
77+
case cacheLimitExceeded(ErrorDescription, RecoverySuggestion, Error?)
78+
case validation(ErrorDescription, RecoverySuggestion, Error?)
79+
case unknown(ErrorDescription, Error?)
80+
}
81+
```
82+
83+
## Building a New Amplify Client
84+
85+
### Directory structure
86+
87+
```
88+
AmplifyClients/Amplify<Service>Client/
89+
├── Sources/
90+
│ ├── Amplify<Service>Client.swift # Public facade
91+
│ └── Support/ # Error type, actors, protocols, impls
92+
├── Tests/
93+
│ ├── UnitTests/
94+
│ └── IntegrationTests/
95+
```
96+
97+
### Package.swift target
98+
99+
```swift
100+
.target(
101+
name: "Amplify<Service>Client",
102+
dependencies: ["AmplifyFoundation", "AmplifyFoundationBridge",
103+
.product(name: "AWS<Service>", package: "aws-sdk-swift")],
104+
path: "AmplifyClients/Amplify<Service>Client/Sources",
105+
swiftSettings: [.enableUpcomingFeature("StrictConcurrency")]
106+
)
107+
```
108+
109+
### Architectural rules
110+
111+
| Rule | Details |
112+
|------|---------|
113+
| Minimal deps | Import `AmplifyFoundation` + `AmplifyFoundationBridge`; may use `Amplify` core types if needed, avoid `AWSPluginsCore` |
114+
| Actor internals | All mutable shared state in actors |
115+
| Strict concurrency | Enable flag, all types `Sendable` |
116+
| Protocol-driven | Abstract storage/network/scheduler behind protocols |
117+
| `AmplifyError` errors | Public error enum with `errorDescription` + `recoverySuggestion` |
118+
| Credentials via Foundation | Accept `AWSCredentialsProvider`, convert via Bridge adapters |
119+
| User-Agent injection | Use `UserAgentClientEngine` for HTTP requests |
120+
| Escape hatch | Expose the underlying AWS SDK client |
121+
| Configurable | `Options` struct with sensible defaults |
122+
| Testable | In-memory storage alternatives, mock senders |
123+
124+
### Wiring credentials + User-Agent
125+
126+
```swift
127+
let sdkCredentials = FoundationToSDKCredentialsAdapter(provider: credentialsProvider)
128+
var config = try AWS<Service>.<Service>Client.<Service>ClientConfiguration(
129+
region: region, credentialIdentityResolver: sdkCredentials
130+
)
131+
config.httpClientEngine = UserAgentClientEngine(
132+
target: config.httpClientEngine,
133+
additionalMetadata: ["md/amplify-<service>#\(AmplifyMetadata.version)"]
134+
)
135+
```

0 commit comments

Comments
 (0)