PureXML/third_party/rules-swift/openapi-generated.md
Mihaela Mihaljevic c7ef8abfd9 chore(rules): pin rules-swift corpus, add gates and CI; drop redundant docs/rules mirror
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.
2026-06-30 14:02:40 +02:00

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:

  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.