-
Notifications
You must be signed in to change notification settings - Fork 6
CEXT-6138: spec process setup and capability discovery design #411
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
base: main
Are you sure you want to change the base?
Changes from all commits
7dc738b
c4f26d6
df04930
e69051e
fbb6593
a00ccf6
36cf9b3
fa71521
fe7a753
544cb0b
46d49b7
852c219
3da1b43
47ea7e3
ebdd6a2
17a6c91
c853855
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,76 @@ | ||
| # Spec Conventions | ||
|
|
||
| ## Why specs | ||
|
|
||
| The spec process provides a consistent path for substantial changes to the SDK so | ||
| that all stakeholders can be confident about its direction. | ||
|
|
||
| Many changes — bug fixes, internal refactoring, documentation improvements — can | ||
| go straight to a pull request. But some changes are substantial enough to warrant | ||
| a design process and explicit alignment before implementation begins. | ||
|
|
||
| A spec is not a living document. It represents the design agreed upon at the | ||
| time the spec PR was merged. If a subsequent ticket changes the design, it | ||
| produces a new spec file — the original remains unchanged as a record of what | ||
| was decided and why. This avoids spec drift: the spec always reflects the | ||
| intent at the time it was written, not whatever was eventually built. | ||
|
|
||
| ## When to write a spec | ||
|
|
||
| Write a spec when the change is "substantial". This includes: | ||
|
|
||
| - New public API surface (new exports, new config fields, new lifecycle hooks) | ||
| - Changes to existing public API that affect SDK consumers | ||
| - New SDK-level features that span multiple packages | ||
| - Removal of existing public API | ||
|
|
||
| A spec is not required for: | ||
|
|
||
| - Bug fixes with no API surface change | ||
| - Internal refactoring that doesn't affect SDK consumers | ||
| - Test or documentation additions | ||
| - Dependency updates | ||
|
|
||
| If in doubt, write a spec. A short spec is better than a surprise. A spec | ||
| should be detailed enough to align on the design and unblock implementation, | ||
| but not so detailed that it describes code — leave implementation choices to | ||
| the implementor. | ||
|
|
||
| ## Files | ||
|
|
||
| Specs live in `specs/features/` and are named with the Jira ticket prefix so | ||
| they are easy to find: | ||
|
|
||
| ``` | ||
| specs/features/CEXT-6138-capability-discovery.md | ||
| ``` | ||
|
|
||
| Use `specs/features/_template.md` as the starting point. | ||
|
|
||
| Spec files are auto-formatted by Prettier on every commit (via lint-staged) and | ||
| by `pnpm format:markdown`. Focus on content — don't worry about line wrapping or | ||
| whitespace. | ||
|
|
||
| ## Workflow | ||
|
|
||
| A feature goes through two pull requests: | ||
|
|
||
| 1. **Spec PR** — contains only the spec file. Reviewers align on the design | ||
| before any implementation begins. Once merged, the spec is considered | ||
| approved and the feature is ready to implement. | ||
| 2. **Implementation PR** — contains the code. References the approved spec. | ||
|
obarcelonap marked this conversation as resolved.
|
||
| Once merged, the spec status is updated to _Implemented_. | ||
|
|
||
| This separation ensures design decisions are made explicitly and are not | ||
| shaped retroactively by implementation details. | ||
|
|
||
| ### Status | ||
|
|
||
| A spec has no explicit status until it is implemented. The repo state is the | ||
| source of truth: | ||
|
|
||
| - **Spec PR open** — the spec is under review. | ||
| - **Spec PR merged** — the spec is approved and implementation can begin. | ||
| - **Implementation PR merged** — check the `Implemented` box in the spec | ||
| metadata as part of the implementation PR. This is the only status transition | ||
| that requires an explicit change. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,158 @@ | ||
| # Capability Discovery | ||
|
|
||
| - **Ticket:** [CEXT-6138](https://jira.corp.adobe.com/browse/CEXT-6138) | ||
| - **Created:** 2026-04-23 | ||
| - [ ] **Implemented** | ||
|
|
||
| ## Summary | ||
|
|
||
| Provide a formal discoverability mechanism for the features and endpoints exposed | ||
| by a Commerce SDK-based app, so that any authenticated client can reliably | ||
| determine what is available for a given deployment without needing SDK-specific | ||
| knowledge. | ||
|
|
||
| ## Motivation | ||
|
|
||
| As the Commerce SDK grows in scope, clients need to know which features and | ||
| endpoints are active for a given deployment. Without a formal mechanism, clients | ||
| are forced to infer feature availability at runtime by calling endpoints and | ||
| inspecting response shapes — a fragile pattern that couples client logic to | ||
| internal API details. | ||
|
|
||
| `commerce-app-management` is a concrete example: it currently calls `getAppConfig` | ||
| and inspects the response shape to decide which UI features to show or hide. Every | ||
| new SDK capability requires a corresponding change in `commerce-app-management` to | ||
| detect it. This does not scale as the number of clients and SDK features grows. | ||
|
|
||
| **Goals:** | ||
|
|
||
| - Give any authenticated client a stable, versioned description of which endpoints are active for a given deployment. | ||
| - Remove the need for clients to infer feature availability by parsing response shapes or probing endpoints. | ||
| - Keep the SDK as the single source of truth for the API contract. | ||
|
|
||
| **Non-goals:** | ||
|
|
||
| - Providing a general API documentation portal or developer-facing reference — the full spec in the SDK repository serves that purpose. | ||
| - Runtime introspection of config-dependent availability — endpoint availability is determined at build time and is fixed for a given deployment. | ||
| - Client code generation from the spec. | ||
|
|
||
| ## Developer experience | ||
|
|
||
| An authenticated client fetches `/__metadata__/openapi.json` with a valid IMS | ||
| bearer token: | ||
|
|
||
| ```http | ||
| GET /__metadata__/openapi.json | ||
| Authorization: Bearer <ims-token> | ||
| ``` | ||
|
|
||
| The response is an OpenAPI 3.x document listing only the endpoints active for | ||
| that deployment. A client uses it to adapt its behaviour — no SDK-specific | ||
| knowledge required. | ||
|
|
||
| **Example: `commerce-app-management`** | ||
|
|
||
| On load, `commerce-app-management` already holds an IMS token from the user | ||
| session. It fetches `/__metadata__/openapi.json` and drives UI visibility based on | ||
| which endpoints are present — showing or hiding features accordingly. This | ||
| replaces the current pattern of calling `getAppConfig` and inferring availability | ||
| from the response shape. | ||
|
|
||
| Other clients (CLI tools, integrations, third-party UIs) follow the same pattern: | ||
| obtain an IMS token, fetch the spec, adapt accordingly. | ||
|
|
||
| ## Design | ||
|
|
||
| ### 1. Full spec in the SDK package | ||
|
|
||
| `aio-commerce-lib-app` ships an `openapi.json` (OpenAPI 3.x) documenting **all** | ||
| endpoints the SDK can expose. This file is included in the published npm package | ||
| under a well-known path (e.g. `dist/openapi.json`) and is the single source of | ||
| truth for the SDK's API contract. | ||
|
obarcelonap marked this conversation as resolved.
|
||
|
|
||
| ### 2. Filtered spec generated by `pre-app-build` | ||
|
|
||
| The SDK's `pre-app-build` hook already reads `app.commerce.config.{ts,js}` to | ||
| determine which features are active for the app being built, and uses this to | ||
| generate `ext.config.yaml` with only the required actions. | ||
|
|
||
| The same hook generates a **filtered `openapi.json`** — a subset of the full spec | ||
| containing only the endpoints that correspond to the active config domains. The | ||
| generated file includes the SDK version and the app version for traceability. | ||
|
|
||
| Availability is determined by what the app developer declares in | ||
| `app.commerce.config.ts`, not by which SDK modules are imported. The | ||
| `pre-app-build` hook is the correct and only insertion point since it is the only | ||
| place where both the full SDK spec and the app's config are available | ||
| simultaneously. | ||
|
|
||
| ### 3. Metadata action | ||
|
|
||
| The filtered `openapi.json` is served by a dedicated `metadata` runtime action | ||
| protected by the same IMS authentication as the rest of the SDK endpoints. | ||
|
|
||
| Suggested path: `/__metadata__/openapi.json` | ||
|
|
||
| The filtered spec is bundled into the action at build time, following the same | ||
| pattern used by other SDK actions (e.g. `app-config`, `config`): `pre-app-build` | ||
| writes `openapi.json` to a well-known path, and the generated action template | ||
| imports it statically: | ||
|
|
||
| ```js | ||
| import openApiSpec from "../../openapi.json" with { type: "json" }; | ||
| ``` | ||
|
|
||
| The bundler inlines the JSON at build time; no disk read occurs at request time. | ||
| The action factory receives the spec object and returns it in the response body. | ||
|
|
||
| ### 4. SDK upgrades | ||
|
|
||
| When an app upgrades its `aio-commerce-lib-app` dependency, the full `openapi.json` | ||
| in the new package version may include new endpoints or updated schemas. On the | ||
| next build, `pre-app-build` derives the active config domains from | ||
| `app.commerce.config.ts`, applies the same filtering, and deploys an updated | ||
| `openapi.json` with the `metadata` action. | ||
|
|
||
| No manual spec maintenance is required. New SDK features are automatically included | ||
| if their config domain is already active. If a new feature introduces a new config | ||
| domain, the app developer opts in by declaring it in `app.commerce.config.ts` and | ||
| rebuilding. | ||
|
|
||
| ## Drawbacks | ||
|
|
||
| - Adds a new `metadata` runtime action to every SDK-based deployment. | ||
|
Collaborator
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. I remember we discussed how to reduce the number of runtime actions.
Collaborator
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 only place this would make sense to add is the
Member
Author
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. what if we use
Collaborator
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. Didn't think of that, sounds good to me |
||
| - Clients must hold a valid IMS token before they can discover what is available, | ||
| which adds a bootstrap step for clients that don't already have one. | ||
|
|
||
| ## Rationale and alternatives | ||
|
|
||
| The filtered spec is auth-gated to prevent **deployment fingerprinting**: it | ||
| reveals which features a specific deployment has enabled, which is | ||
| deployment-specific metadata that should not be publicly enumerable. Schema | ||
| disclosure is not a concern since the full spec is already public in the | ||
| `aio-commerce-sdk` repository. | ||
|
|
||
| Alternatives considered: | ||
|
|
||
| - **Runtime inspection of `getAppConfig`** — current approach; fragile and | ||
| requires SDK-specific knowledge in every client. | ||
| - **Unauthenticated static file** — simpler to consume but exposes | ||
| deployment-specific configuration without auth. | ||
| - **Capabilities list endpoint** — a simpler boolean feature map instead of a | ||
| full OpenAPI spec; less powerful and doesn't give clients the full contract. | ||
|
|
||
| ## Unresolved questions | ||
|
|
||
| - Which config domains map to which endpoints in the filtered spec — to be | ||
| defined during implementation based on the state of the SDK at that time. | ||
| - Whether the full `openapi.json` in the SDK package is hand-authored or | ||
| generated from the `HttpActionRouter` schemas (e.g. via Standard Schema + | ||
| `@standard-community/standard-openapi`) — to be decided during implementation. | ||
|
|
||
| ## Future possibilities | ||
|
|
||
| - The `metadata` action could be extended to expose additional deployment | ||
| information beyond the OpenAPI spec (e.g. SDK version, configured domains) | ||
| under a common `/__metadata__/` namespace. | ||
| - The full `openapi.json` in the SDK package could be used to power generated | ||
| client libraries or documentation automatically. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| # Feature Name | ||
|
|
||
| - **Ticket:** [CEXT-XXXX](https://jira.corp.adobe.com/browse/CEXT-XXXX) | ||
| - **Created:** YYYY-MM-DD | ||
| - [ ] **Implemented** | ||
|
|
||
| ## Summary | ||
|
|
||
| One paragraph explanation of the feature. | ||
|
|
||
| ## Motivation | ||
|
|
||
| Describe the problem this feature solves for developers building Commerce apps | ||
| with the SDK. Include necessary background and specific use cases where this | ||
| feature helps. This is the most important section — be concrete about the pain | ||
| point before proposing a solution. | ||
|
|
||
| Close the section with explicit goals (what success looks like) and non-goals | ||
| (what is explicitly out of scope), so that the design that follows is bounded. | ||
|
|
||
| ## Developer experience | ||
|
|
||
| Explain the feature as if it were already shipped and you were showing it to an | ||
| app developer using the SDK. This means: | ||
|
|
||
| - What new concepts or APIs does it introduce? | ||
| - Show concrete usage examples (config, code, CLI output). | ||
| - How should developers think about this feature and how does it change how they | ||
| build apps? | ||
| - If applicable, show what error messages or warnings look like. | ||
| - Does this affect how developers read or maintain their existing Commerce app? | ||
|
|
||
| ## Design | ||
|
|
||
| The technical detail of the feature. Cover enough that: | ||
|
|
||
| - Interactions with existing SDK features (lib-app, lib-auth, lib-config, etc.) | ||
| are clear. | ||
| - It is reasonably clear how the feature would be implemented. | ||
| - Edge cases are called out explicitly. | ||
|
|
||
| Return to the examples from the Developer experience section and explain how the | ||
| design makes them work. | ||
|
|
||
| ## Drawbacks | ||
|
|
||
| Why should we _not_ do this? Consider implementation cost, maintenance burden, | ||
| impact on SDK consumers, and whether it adds complexity for the common case. | ||
|
|
||
| ## Rationale and alternatives | ||
|
|
||
| - Why is this the best design in the space of possible designs? | ||
| - What other approaches were considered and why were they rejected? | ||
| - What is the impact of not doing this? | ||
| - Could this be achieved in user-space (i.e. without SDK changes)? | ||
|
|
||
| ## Unresolved questions | ||
|
|
||
| - What needs to be resolved through discussion before this spec is accepted? | ||
| - What needs to be resolved during implementation before the feature ships? | ||
| - What related problems are explicitly out of scope for this spec? | ||
|
|
||
| ## Future possibilities | ||
|
obarcelonap marked this conversation as resolved.
|
||
|
|
||
| What is the natural evolution of this feature? How might it interact with other | ||
| planned SDK features? Use this section to capture related ideas that are out of | ||
| scope now but worth tracking. | ||
Uh oh!
There was an error while loading. Please reload this page.