# 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 `.`, and set `operationId` to `..`. 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"`