Files
suaveplan/openspec/archive/2026-05-26-api-openapi/specs/api-openapi/spec.md
2026-05-27 23:29:53 +01:00

4.1 KiB

Capability Spec Delta: api-openapi (api-openapi)

Package: @suaveplan/api-openapi Status: PROPOSED

Every requirement below carries a **Verified by:** line at authoring time.

ADDED Requirements

Requirement: OpenAPI 3.1 document emitted from Zod schemas

The package SHALL emit an OpenAPI 3.1 document from every plugin's manifest plus every registered route's Zod schemas (request body / params / query / response). Conversion uses @asteasolutions/zod-to-openapi.

Scenario: full document covers every plugin

  • WHEN two plugins are registered each with two routes
  • THEN emitter.getJson() returns an OpenAPI 3.1 document with paths for all four routes

Verified by: src/emitter/emitter.test.ts::"emits a 3.1 document covering every registered route"


Requirement: Namespacing rules are deterministic

The package SHALL set tags to each plugin's displayName, prefix component schemas with <pluginName>.<TypeName>, and set operationId to <pluginName>.<method>.<routePath>. operationIds MUST be unique across the merged document; collisions raise OPENAPI_OPERATION_ID_COLLISION.

Scenario: operationId is deterministic

  • WHEN a billing plugin declares GET /invoices/:id
  • THEN the emitted operationId is billing.get./invoices/{id}

Scenario: operationId collision raises error

  • WHEN two plugins register routes that produce identical operationIds
  • THEN emission raises OPENAPI_OPERATION_ID_COLLISION

Verified by: src/namespacing/namespacing.test.ts::"operationId is deterministic" AND src/namespacing/namespacing.test.ts::"collision raises OPENAPI_OPERATION_ID_COLLISION"


Requirement: Security schemes inherit from the active auth adapter

The package SHALL read the active auth adapter's securitySchemes contribution and attach the default scheme to every route except those listed in any plugin's publicRoutes.

Scenario: public route has no security

  • WHEN a route is listed in publicRoutes
  • THEN the emitted operation has security: []

Scenario: non-public route inherits default scheme

  • WHEN a route is not on publicRoutes
  • THEN the emitted operation lists the auth adapter's default scheme

Verified by: src/emitter/emitter.test.ts::"public route emits empty security" AND src/emitter/emitter.test.ts::"non-public route inherits adapter scheme"


Requirement: Endpoints serve json, yaml, and per-plugin filtered views

The package SHALL expose GET /openapi.json, GET /openapi.yaml, GET /docs, and GET /docs/{plugin} handlers. The filtered view MUST return only operations owned by the named plugin; an unknown plugin name raises OPENAPI_UNKNOWN_PLUGIN_IN_FILTER (HTTP 404).

Scenario: yaml endpoint returns yaml content-type

  • WHEN a client requests GET /openapi.yaml
  • THEN response Content-Type is application/yaml AND the body parses as YAML

Scenario: filtered view excludes other plugins

  • WHEN GET /docs/billing is requested in a host with billing + users plugins
  • THEN the response document contains billing routes only AND no users routes

Verified by: src/endpoints/endpoints.test.ts::"yaml endpoint returns application/yaml" AND src/endpoints/endpoints.test.ts::"filtered view excludes other plugins"


Requirement: Missing schema is handled per strictness setting

The package SHALL read host.openapi.strictness. When "warn", missing request/response schemas log a warning AND emit with description: "schema-missing". When "refuse-install", missing schemas raise OPENAPI_MISSING_SCHEMA BEFORE the plugin reaches installed.

Scenario: warn mode emits with placeholder

  • WHEN a route has no response schema AND strictness is "warn"
  • THEN emission succeeds AND the warning counter api.openapi.missing_schema_total{plugin, route} increments

Scenario: refuse-install raises

  • WHEN strictness is "refuse-install"
  • THEN emission raises OPENAPI_MISSING_SCHEMA

Verified by: src/emitter/emitter.spec-conformance.test.ts::"warn mode emits placeholder and increments warning counter" AND src/emitter/emitter.spec-conformance.test.ts::"refuse-install raises OPENAPI_MISSING_SCHEMA"