docs(website): settle the OpenAPI fragment merge and the curated UI kit
Both approved by the org lead 2026-08-10. §6.1 moves from open question to decision A with the per-side obligations spelled out in a new §6.1a: modules ship a swagger-fragment.json with fully-qualified paths and namespaced schema keys, core merges started modules' fragments into /api/docs.json at request time and always wins a key collision, and swagger-output.json stays exactly what core's own routes generate. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -251,6 +251,13 @@ them leaves an operator with orphaned data and no supported way to remove it.
|
||||
- Call `process.exit`, install signal handlers, or start a listener.
|
||||
- Write outside `ctx.paths.moduleRoot` and the upload directory.
|
||||
|
||||
### 2.8 The OpenAPI fragment
|
||||
|
||||
Every module that registers routes ships `swagger-fragment.json` in its bundle root. Core merges the
|
||||
fragments of started modules into `/api/docs.json`; the full reasoning and the collision rules are
|
||||
§6.1a. In short: fully-qualified paths, namespaced schema keys, module CI fails if a registered
|
||||
route has no path in the fragment, and core wins every key collision.
|
||||
|
||||
---
|
||||
|
||||
## Part 3 — The client contract
|
||||
@@ -347,7 +354,8 @@ no-op, because no core nav item carries a `feature` today.
|
||||
|
||||
### 3.4 `ui` — the shared component kit
|
||||
|
||||
**This is the largest addition Phase 1 makes to the plan, and it is not optional** (§6.2). The atlas
|
||||
**This is the largest addition Phase 1 makes to the plan, and it is not optional** (§6.2; approved
|
||||
2026-08-10). The atlas
|
||||
pages alone import five core modules that are not React and not the router: `PublicLayout`,
|
||||
`PageHeader`, `Loading` / `ErrorState` / `EmptyState`, and `useAsync`. Without a shared kit a module
|
||||
either reaches into core's tree (violating the zero-import rule) or ships its own copies, which
|
||||
@@ -504,7 +512,7 @@ diff the whole workstream is allowed.
|
||||
Four things the survey found that the design of record gets wrong or does not cover. The first
|
||||
needs a decision.
|
||||
|
||||
### 6.1 OpenAPI generation does not survive a dynamic loader — **open question**
|
||||
### 6.1 OpenAPI generation does not survive a dynamic loader — **settled: fragment merge**
|
||||
|
||||
`MODULE_SYSTEM.md` §1.12 treats `scripts/routeManifest.js` and `swagger/swagger.js` as the same
|
||||
problem, because both require `app.js` with no database. They are not the same problem.
|
||||
@@ -522,12 +530,30 @@ spec*. Three ways out:
|
||||
|
||||
| Option | How | Cost |
|
||||
| --- | --- | --- |
|
||||
| **A. Fragment merge** *(recommended)* | Module CI runs swagger-autogen against its own `server/index.js` and ships `swagger-fragment.json` in the bundle. Core deep-merges the fragments of started modules into `/api/docs.json` at request time. | one merge helper in core (~40 lines); the module owns its own spec, which matches "one repo, one bundle" |
|
||||
| **A. Fragment merge** ✅ | Module CI runs swagger-autogen against its own `server/index.js` and ships `swagger-fragment.json` in the bundle. Core deep-merges the fragments of started modules into `/api/docs.json` at request time. | one merge helper in core (~40 lines); the module owns its own spec, which matches "one repo, one bundle" |
|
||||
| B. Glob the modules dir | Core's `swagger.js` adds `modules/*/server/index.js` to `routes` when present. | core's *committed* spec then depends on which modules the developer had checked out — a spec that differs per machine |
|
||||
| C. Hand-write module paths into core's spec | — | a second source of truth; drifts on the first module release |
|
||||
|
||||
A is the only one that keeps the spec correct on an operator's box, where core is a prebuilt image
|
||||
and the module arrived afterwards. **This needs sign-off before Phase 2 item 2.**
|
||||
**Decided 2026-08-10: A.** It is the only one that keeps the spec correct on an operator's box,
|
||||
where core is a prebuilt image and the module arrived afterwards. The obligation this puts on a
|
||||
module is §2.8; the one it puts on core is Phase 2 item 2.
|
||||
|
||||
### 6.1a The obligation, on each side
|
||||
|
||||
**Module:** a `swagger-fragment.json` in the bundle root, generated by its own CI with the same
|
||||
`swagger-autogen` tooling pointed at its own entry point, carrying only `paths`, `tags` and
|
||||
`components.schemas`. Its paths must be fully qualified (`/api/v1/public/atlas/creatures`), because
|
||||
the module knows its own mount prefixes and core does not re-derive them. Its `components.schemas`
|
||||
keys are namespaced (`UoAtlasCreature`, not `AtlasCreature`) so two modules cannot collide in the
|
||||
merged spec. CI fails the module build if a route it registers has no path in its fragment — the
|
||||
per-module form of "never ship a route that isn't in the spec".
|
||||
|
||||
**Core:** `/api/docs.json` merges the fragments of **started** modules over its own committed spec at
|
||||
request time (cached, invalidated on a module state change). Merge is shallow-per-section and
|
||||
**core always wins a key collision** — a module cannot redefine a core path, tag or schema by
|
||||
shipping one with the same name; the collision is logged and the module's version dropped.
|
||||
`swagger-output.json` itself stays exactly what core's own routes generate, so `npm run swagger`
|
||||
remains reproducible on any machine regardless of what is installed.
|
||||
|
||||
### 6.2 The client contract is much larger than §2.1 says
|
||||
|
||||
|
||||
@@ -508,6 +508,8 @@ Conventional Commits, the AI-disclosure trailer, branches cut from an up-to-date
|
||||
| 3 | Core declares an extension slot on `/admin/users/:id`; all six URLs preserved | §1.9 |
|
||||
| 4 | Phase 1 spike targets `/api/v1/public/atlas/*` | §2.7 |
|
||||
| 4a | The contract lives in [`MODULE_API.md`](MODULE_API.md); it is normative where the two differ | §2.7 |
|
||||
| 4b | Modules ship an OpenAPI **fragment**; core merges started modules' fragments into `/api/docs.json` | API §6.1 |
|
||||
| 4c | Core exposes a **curated, closed** UI kit + request primitive on `window.__rg`, versioned by `MODULE_API_VERSION` | API §3.4 |
|
||||
| 5 | Install surfaces: admin panel and the Docker environment; never a build step | §2.5 |
|
||||
| 6 | One repo, one bundle — server and client halves version together | §2.3 |
|
||||
| 7 | Android app is a separate plan; core owes it `/api/v1/public/modules` | §2.5, §2.7 |
|
||||
|
||||
Reference in New Issue
Block a user