-
Notifications
You must be signed in to change notification settings - Fork 0
P3.1: establish public contract baseline #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0c71838
97a3ffb
4c3680c
701e693
ea88362
2fb0dc8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| name: Contract compatibility | ||
|
|
||
| on: | ||
| pull_request: | ||
| paths: | ||
| - "openapi/corelink-public-v*.yaml" | ||
| - "asyncapi/**" | ||
| - "schemas/**" | ||
| - "scripts/check_openapi_compatibility.rb" | ||
| - ".github/workflows/contract-compatibility.yml" | ||
| push: | ||
| branches: [main] | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| public-openapi: | ||
| runs-on: ubuntu-24.04 | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 | ||
| - name: Parse versioned contracts | ||
| run: | | ||
| ruby -e 'require "yaml"; %w[openapi/corelink-public-v1.yaml openapi/corelink-admin-v1.yaml openapi/corelink-internal-v1.yaml asyncapi/corelink-events-v1.yaml].each { |path| YAML.safe_load(File.read(path), permitted_classes: [], aliases: false); puts "parsed #{path}" }' | ||
|
Comment on lines
+24
to
+26
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When the compatibility policy is followed by adding Useful? React with 👍 / 👎. |
||
| ruby -rjson -e 'Dir["schemas/*.json"].each { |path| JSON.parse(File.read(path)); puts "parsed #{path}" }' | ||
|
Comment on lines
+24
to
+27
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a new operation, message, or schema contains a misspelled or unresolved Useful? React with 👍 / 👎. |
||
| - name: Reject unversioned breaking public changes | ||
| if: github.event_name == 'pull_request' | ||
| run: | | ||
| git show "origin/${{ github.base_ref }}:openapi/corelink-public-v1.yaml" > /tmp/base-public.yaml || true | ||
| ruby scripts/check_openapi_compatibility.rb /tmp/base-public.yaml openapi/corelink-public-v1.yaml | ||
|
Comment on lines
+28
to
+32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a PR removes an AsyncAPI channel or narrows one of the reusable JSON Schemas, the workflow is triggered but merely parses those files; the only compatibility command compares the public OpenAPI document. Consumers generated from Useful? React with 👍 / 👎. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| asyncapi: 3.0.0 | ||
| info: | ||
| title: CoreLink Event Contract | ||
| version: 1.0.0-draft | ||
| description: Canonical event envelopes for approved public and partner deliveries. | ||
| channels: | ||
| device.lifecycle: | ||
| address: device.lifecycle | ||
| messages: | ||
| deviceLifecycle: | ||
| $ref: '#/components/messages/DeviceLifecycle' | ||
| command.status: | ||
| address: command.status | ||
| messages: | ||
| commandStatus: | ||
| $ref: '#/components/messages/CommandStatus' | ||
| operations: | ||
| receiveDeviceLifecycle: | ||
| action: receive | ||
| channel: {$ref: '#/channels/device.lifecycle'} | ||
| receiveCommandStatus: | ||
| action: receive | ||
| channel: {$ref: '#/channels/command.status'} | ||
|
Comment on lines
+18
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
For the outward public and partner deliveries described by this contract, Useful? React with 👍 / 👎. |
||
| components: | ||
| messages: | ||
| DeviceLifecycle: | ||
| name: DeviceLifecycle | ||
| payload: {$ref: '#/components/schemas/EventEnvelope'} | ||
| CommandStatus: | ||
| name: CommandStatus | ||
| payload: {$ref: '#/components/schemas/EventEnvelope'} | ||
|
Comment on lines
+28
to
+31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Both channel-specific messages reference the same unconstrained envelope, whose only Useful? React with 👍 / 👎.
Comment on lines
+29
to
+31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
For a Useful? React with 👍 / 👎. |
||
| schemas: | ||
| EventEnvelope: | ||
| type: object | ||
| additionalProperties: false | ||
| required: [event_id, event_type, occurred_at, tenant_id, data] | ||
| properties: | ||
| event_id: {type: string, format: uuid} | ||
| event_type: {type: string, examples: [command.completed]} | ||
| occurred_at: {type: string, format: date-time} | ||
| tenant_id: {type: string, format: uuid} | ||
| correlation_id: {type: string} | ||
| data: {type: object, additionalProperties: true} | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| # API compatibility policy | ||
|
|
||
| `v1` is a public, supported contract. Its canonical source is this repository; | ||
| runtime implementation, SDKs, the CLI, documentation and the mock server must | ||
| be verified against it before release. | ||
|
Comment on lines
+3
to
+5
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This policy labels Useful? React with 👍 / 👎. |
||
|
|
||
| ## Compatibility promise | ||
|
|
||
| - A `v1` operation, path parameter, required request field, response field or | ||
| documented error code is not removed or changed incompatibly within v1. | ||
| - New optional fields, optional query parameters, new enum values and new | ||
| operations are additive changes. Consumers must ignore unknown response | ||
| fields and handle unknown enum values safely. | ||
|
Comment on lines
+11
to
+13
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The policy classifies new enum values as additive, but the public schemas model Useful? React with 👍 / 👎. |
||
| - New required request fields, tighter validation, changed semantics, response | ||
| type changes and authentication/authorization expansion are breaking. | ||
| - Breaking public changes require a new major contract (`v2`), migration | ||
| guidance, a sunset date and compatibility tests. They cannot be hidden behind | ||
| a server flag or an SDK-only change. | ||
|
|
||
| ## Lifecycle and deprecation | ||
|
|
||
| Every public operation declares `x-corelink-stability`. Deprecated operations | ||
| remain available for at least 180 days after a dated `Deprecation` response | ||
| header and replacement documentation are published. Responses for a deprecated | ||
| operation include `Sunset` when a removal date is set. | ||
|
|
||
| ## Error and tenant rules | ||
|
|
||
| All non-success responses use `application/problem+json` and include a safe | ||
| `correlation_id`. Public resources use canonical CoreLink IDs only. A caller | ||
| must be authorized for the path tenant; an unauthorized caller is never given | ||
| integration-provider IDs or raw provider payloads. | ||
|
|
||
| ## Release gate | ||
|
|
||
| Each contract PR must validate syntax and references, classify its diff as | ||
| additive or breaking, update examples and record the contract version used by | ||
| each generated SDK release. A breaking diff without a new major document fails | ||
| the release gate. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| # P3.1 runtime-parity gate | ||
|
|
||
| The `1.0.0-draft` documents establish the target public boundary; they are not | ||
| an assertion that the current runtime is already byte-for-byte compatible. | ||
| Before a stable SDK release, the platform must close each of these gates. | ||
|
|
||
| | Contract decision | Current runtime observation | Required closure | | ||
| | --- | --- | --- | | ||
| | `corelink_device_id` is the public device field | Device responses currently serialize the persistence attribute `id` | Serialize the canonical public name while retaining the same UUID value; add response compatibility tests. | | ||
| | `corelink_device_id` is the command device field | Command responses currently serialize `device_id` | Apply the canonical name at the public boundary and test list/get/create. | | ||
| | Provider routing is not a public request concern | Command creation selects an eligible active binding through CoreLink-owned policy | Preserve the policy and its no-eligible/ambiguous conflict tests; never expose provider internals. | | ||
| | Problem Details is the error media type | FastAPI defaults currently return `{ "detail": ... }` | Add a correlation-safe exception handler and contract tests for 400/401/403/404/409. | | ||
| | `/api/v1` is stable public surface | Runtime routes include administration and internal callbacks under the same prefix | Classify routes and prevent unreviewed routes from entering the public document or generated clients. | | ||
|
|
||
| No TypeScript or Python package may be promoted beyond prerelease until these | ||
| items, a contract-diff check and generated-client compatibility tests are | ||
| green. This gate preserves current consumers while the public boundary is | ||
| normalized. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| openapi: 3.1.1 | ||
| info: | ||
| title: CoreLink Administrative API | ||
| version: 1.0.0-draft | ||
| description: Reserved for privileged administrative operations; not a public SDK input. | ||
| paths: {} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| openapi: 3.1.1 | ||
| info: | ||
| title: CoreLink Internal API | ||
| version: 1.0.0-draft | ||
| description: Service-to-service contract; never expose or generate public clients from it. | ||
| paths: {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When a pull request changes only
openapi/corelink-admin-v1.yamloropenapi/corelink-internal-v1.yaml, none of the configured path filters match, so the workflow does not run even though its parse step explicitly includes both files. A malformed administrative or internal contract can therefore merge without the advertised syntax check; include those OpenAPI paths in the trigger.Useful? React with 👍 / 👎.