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.
5.2 KiB
5.2 KiB
| description | globs | alwaysApply |
|---|---|---|
| Guardrails for OpenAPI-generated code and spec files (Swift projects) | 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
// 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
// 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
// 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
// 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
# 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:
- Check the file path before any edit.
- Never modify
.build/,DerivedData/, orGenerated/paths. - Create adapters for DTO-to-domain mapping.
- Document spec changes in proposals first, then change the spec and regenerate.