extends: - "spectral:oas" formats: - "oas3" rules: stella-info-title: description: "OpenAPI info.title must be present" message: "Add a descriptive `info.title`" given: "$.info.title" severity: error then: function: truthy stella-info-version: description: "OpenAPI info.version must be present" message: "Set `info.version` (SemVer or release tag)" given: "$.info.version" severity: error then: function: truthy stella-servers-https: description: "Servers should use https" given: "$.servers[*].url" severity: warn then: function: pattern functionOptions: match: "^https://" operation-operationId-required: description: "Every operation must have an operationId" message: "Add an `operationId` for this operation" given: "$.paths[*][*]" severity: error then: field: operationId function: truthy stella-2xx-response-examples: description: "Every 2xx response must include at least one example" message: "Add an example or examples block to 2xx responses" given: "$.paths[*][*].responses[?(@property.match(/^2\\d\\d$/))].content.*" severity: error then: function: schema functionOptions: schema: anyOf: - required: [examples] - required: [example] stella-pagination-params: description: "Collection GETs (list/search) must expose limit/cursor parameters" message: "Add limit/cursor parameters for paged collection endpoints" given: "$.paths[*][get]" severity: warn then: function: schema functionOptions: schema: type: object properties: operationId: type: string allOf: - if: properties: operationId: pattern: "([Ll]ist|[Ss]earch|[Qq]uery)" then: required: [parameters] properties: parameters: type: array allOf: - contains: anyOf: - required: ['$ref'] properties: $ref: pattern: 'parameters/LimitParam$' - required: [name, in] properties: name: const: limit in: const: query - contains: anyOf: - required: ['$ref'] properties: $ref: pattern: 'parameters/CursorParam$' - required: [name, in] properties: name: const: cursor in: const: query stella-idempotency-header: description: "State-changing operations returning 201/202 should accept Idempotency-Key headers" message: "Add Idempotency-Key header parameter for idempotent submissions" given: "$.paths[*][?(@property.match(/^(post|put|patch)$/))]" severity: warn then: function: schema functionOptions: schema: type: object properties: responses: type: object parameters: type: array allOf: - if: properties: responses: type: object anyOf: - required: ['201'] - required: ['202'] then: required: [parameters] properties: parameters: type: array contains: type: object properties: name: const: Idempotency-Key in: const: header required: [name, in] stella-operationId-style: description: "operationId must be lowerCamelCase" given: "$.paths[*][*].operationId" severity: warn then: function: casing functionOptions: type: camel stella-jobs-idempotency-key: description: "Orchestrator job submissions must accept Idempotency-Key header" given: "$.paths['/jobs'].post.parameters" severity: warn then: function: schema functionOptions: schema: type: array contains: type: object properties: name: const: Idempotency-Key in: const: header required: [name, in] # --- Deprecation Metadata Rules (per APIGOV-63-001) --- stella-deprecated-has-metadata: description: "Deprecated operations must have x-deprecation extension with required fields" message: "Add x-deprecation metadata (deprecatedAt, sunsetAt, successorPath, reason) to deprecated operations" given: "$.paths[*][*][?(@.deprecated == true)]" severity: error then: field: x-deprecation function: schema functionOptions: schema: type: object required: - deprecatedAt - sunsetAt - successorPath - reason properties: deprecatedAt: type: string format: date-time sunsetAt: type: string format: date-time successorPath: type: string successorOperationId: type: string reason: type: string migrationGuide: type: string format: uri notificationChannels: type: array items: type: string enum: [slack, teams, email, webhook] stella-deprecated-sunset-future: description: "Sunset dates should be in the future (warn if sunset already passed)" message: "x-deprecation.sunsetAt should be a future date" given: "$.paths[*][*].x-deprecation.sunsetAt" severity: warn then: function: truthy stella-deprecated-migration-guide: description: "Deprecated operations should include a migration guide URL" message: "Consider adding x-deprecation.migrationGuide for consumer guidance" given: "$.paths[*][*][?(@.deprecated == true)].x-deprecation" severity: hint then: field: migrationGuide function: truthy stella-deprecated-notification-channels: description: "Deprecated operations should specify notification channels" message: "Add x-deprecation.notificationChannels to enable deprecation notifications" given: "$.paths[*][*][?(@.deprecated == true)].x-deprecation" severity: hint then: field: notificationChannels function: truthy