Mirror the shared Swift rules canon (rules-swift, commit 4ebb892) into docs/rules-swift/ as a read-only reference alongside the repo's tailored docs/rules/. PROVENANCE.md records the source, commit, and what was included or excluded; the upstream repo's own VCS, CI, and lint tooling is omitted as machinery rather than rule content. Exclude the vendored tree from the style gate (scripts/check-style.sh) and the pre-commit hook: the canon's enforcement docs quote the forbidden attribution phrases to define them, the same reason the repo's own enforcement files are already excluded.
196 lines
5.2 KiB
Markdown
196 lines
5.2 KiB
Markdown
---
|
|
description: Guardrails for OpenAPI-generated code and spec files (Swift projects)
|
|
globs: []
|
|
alwaysApply: true
|
|
---
|
|
# OpenAPI Generated Code and Spec Guardrails
|
|
|
|
Never hand-edit OpenAPI-generated Swift sources or OpenAPI specs. The spec is the
|
|
source of truth: generated code is a build artifact derived from it, and any hand
|
|
edit is lost on the next generation. When a change seems necessary, change the
|
|
spec, regenerate, and put your own code in hand-written modules, or propose a spec
|
|
change without touching the generated artifacts.
|
|
|
|
## Core rules
|
|
|
|
### Rule 1: Do-not-touch zones
|
|
|
|
Do not edit files that are:
|
|
|
|
- Generated by the OpenAPI generator.
|
|
- Located under typical generation paths:
|
|
- `Sources/**/Generated/**`
|
|
- `.build/**`
|
|
- `DerivedData/**`
|
|
- `**/OpenAPI/Generated/**`
|
|
- The OpenAPI spec(s): `openapi.yaml`, `openapi.yml`, `**/openapi/*.yaml`.
|
|
|
|
### Rule 2: Analysis-only
|
|
|
|
Analyse, do not change. If asked to "analyse only", do not offer diffs for the
|
|
spec or the generated files. Provide findings plus actionable recommendations for
|
|
where to apply changes safely.
|
|
|
|
### Rule 3: Safe change targets
|
|
|
|
Direct code changes to:
|
|
|
|
- Hand-written modules (the API client, the API server, the shared model module).
|
|
- Adapters, mappers, and extensions in non-generated targets.
|
|
- Dependency-injected services and repositories.
|
|
- A separate notes or proposals file for spec-change suggestions.
|
|
|
|
### Rule 4: Change requests to specs
|
|
|
|
If a spec change is unavoidable:
|
|
|
|
- Produce a proposal file (for example
|
|
`Docs/OpenAPI-Proposals/YYYY-MM-DD-what-why.md`).
|
|
- Include the motivation, example payloads, and migration steps.
|
|
- Do **not** modify the spec inline as part of that proposal.
|
|
|
|
## Decision tree
|
|
|
|
```
|
|
Need to modify API behaviour?
|
|
├─ Is it in generated code?
|
|
│ ├─ YES → Do NOT modify
|
|
│ │ ├─ Create an extension in a hand-written module
|
|
│ │ ├─ Add an adapter / mapper layer
|
|
│ │ └─ Use dependency injection
|
|
│ └─ NO → Safe to modify (the API client, the API server, and so on)
|
|
├─ Is it the OpenAPI spec?
|
|
│ ├─ YES → Do NOT modify inline
|
|
│ │ ├─ Create a proposal under Docs/OpenAPI-Proposals/
|
|
│ │ ├─ Include motivation and examples
|
|
│ │ └─ Wait for approval, then change the spec and regenerate
|
|
│ └─ NO → Safe to modify
|
|
└─ Is it a DTO mapping?
|
|
├─ Generated DTO → Create a domain model plus a mapper
|
|
└─ Domain model → Safe to modify
|
|
```
|
|
|
|
## Implementation patterns
|
|
|
|
### Pattern 1: Extension on generated types
|
|
|
|
```swift
|
|
// CORRECT: extend in a hand-written module
|
|
// File: ApiClient/Extensions/User+Domain.swift
|
|
import ApiShared
|
|
|
|
extension Components.Schemas.UserDTO {
|
|
func toDomain() -> User {
|
|
User(
|
|
id: UUID(uuidString: self.id) ?? UUID(),
|
|
name: self.name,
|
|
email: self.email
|
|
)
|
|
}
|
|
}
|
|
```
|
|
|
|
### Pattern 2: Adapter layer
|
|
|
|
```swift
|
|
// CORRECT: adapter in the API client package
|
|
// File: ApiClient/Adapters/UserAdapter.swift
|
|
import ApiShared
|
|
import SharedModels
|
|
|
|
public struct UserAdapter {
|
|
public static func toDomain(_ dto: Components.Schemas.UserDTO) -> User {
|
|
User(
|
|
id: UUID(uuidString: dto.id) ?? UUID(),
|
|
name: dto.name,
|
|
email: dto.email
|
|
)
|
|
}
|
|
|
|
public static func toDTO(_ user: User) -> Components.Schemas.UserDTO {
|
|
Components.Schemas.UserDTO(
|
|
id: user.id.uuidString,
|
|
name: user.name,
|
|
email: user.email
|
|
)
|
|
}
|
|
}
|
|
```
|
|
|
|
### Pattern 3: Spec change proposal
|
|
|
|
````markdown
|
|
// File: Docs/OpenAPI-Proposals/2025-01-15-add-user-avatar.md
|
|
|
|
# Proposal: add a user avatar field
|
|
|
|
## Motivation
|
|
Users need profile pictures for the social features.
|
|
|
|
## Proposed change
|
|
Add an `avatarUrl` field to the UserDTO schema.
|
|
|
|
## Example payload
|
|
```yaml
|
|
UserDTO:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: string
|
|
name:
|
|
type: string
|
|
avatarUrl: # NEW
|
|
type: string
|
|
format: uri
|
|
nullable: true
|
|
```
|
|
|
|
## Migration steps
|
|
1. Update the OpenAPI spec.
|
|
2. Regenerate the Swift code.
|
|
3. Update the domain model.
|
|
4. Add a migration for existing users.
|
|
````
|
|
|
|
## Anti-patterns
|
|
|
|
### Do NOT modify generated files
|
|
|
|
```swift
|
|
// WRONG: editing Generated/Types.swift
|
|
// This file is overwritten on the next generation.
|
|
extension Components.Schemas.UserDTO {
|
|
var fullName: String { ... } // Lost on regenerate.
|
|
}
|
|
```
|
|
|
|
### Do NOT edit the spec directly without a proposal
|
|
|
|
```yaml
|
|
# WRONG: a direct, undocumented edit to the spec
|
|
UserDTO:
|
|
properties:
|
|
newField: # Added without a proposal
|
|
type: string
|
|
```
|
|
|
|
## Checklist
|
|
|
|
Before modifying API-related code:
|
|
|
|
- [ ] Verified the target file is NOT in a `Generated/` folder.
|
|
- [ ] Verified the target file is NOT the OpenAPI spec.
|
|
- [ ] Changes go to hand-written modules (the API client, the API server).
|
|
- [ ] Extensions or adapters created for generated-type customisation.
|
|
- [ ] Spec changes documented in a proposal file first.
|
|
- [ ] Domain models separated from DTOs.
|
|
|
|
## Verification
|
|
|
|
When working with OpenAPI:
|
|
|
|
1. Check the file path before any edit.
|
|
2. Never modify `.build/`, `DerivedData/`, or `Generated/` paths.
|
|
3. Create adapters for DTO-to-domain mapping.
|
|
4. Document spec changes in proposals first, then change the spec and regenerate.
|