You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@objectstack/rest overwrites discovery version with config.api.version ('v1') — the URL path segment served in the schema's "System Identity" field, masking the producer entirely #11292
Found while implementing #11235 (deriving getDiscovery()'s version instead of the '1.0' literal). Filed rather than fixed: #11235's dispatch bounded that card to the producer, and this is a different package with a real design question attached, so it is not a mechanical in-place fix.
What was measured
packages/rest/src/rest-server.ts calls the producer and then unconditionally replaces the field one line later:
constdiscovery=awaitprotocol.getDiscovery();// Override discovery information with actual server configurationdiscovery.version=this.config.api.version;
config.api.version is the API version identifier, not an artifact identity:
normalizeConfig() in the same file defaults it to 'v1' (api.version ?? 'v1').
The same value builds the mounted URL: api.apiPath ?? \${api.basePath}/${api.version}`→/api/v1`.
packages/spec/src/api/plugin-rest-api.zod.ts declares it version: z.string().default('v1').describe('API version identifier').
But DiscoverySchema (packages/spec/src/api/discovery.zod.ts) declares that field under /** System Identity */, grouped with name and environment — the "what server is this" question. #10993's ruling and its evidence (the SDK's connect() logging data.version next to apiName and services under "Connected to ObjectStack server") settled that reading.
So on any REST-served host, GET /api/v1/discovery answers version: "v1" — the path segment the caller already typed into the URL to get there.
Deleting the line is not obviously right on its own. api.version is real information a client may want (which mounted API version answered), and the schema has no field for it — DiscoverySchema declares name / version / environment / routes / locale / services / capabilities / schemaDiscovery / scoping and nothing that means "API version". Options, roughly:
A. Drop the override, let the producer's derived value through. Simplest; loses the API-version fact (which is already recoverable from routes.*, all of which are prefixed with the mounted base path).
B. Drop the override and declare a separate field for the API version. Correct-looking, but a schema change — packages/spec, domain:spec seat, and a new field on a machine-readable surface needs its own justification under startup scope discipline.
Recommend A unless someone can name a consumer of the API-version fact that routes does not already serve; B only if such a consumer exists. Either way it is a triage call, not a dev call.
No consumer reads discovery versionby value — no version comparison, feature gate, or cache key — in objectstack, objectui or cloud. Controls: an org-wide code search for a known-present string (x-objectstack-build-sha) returned 10 hits across objectstack and cloud, so the queries were live in both repos. The only by-value assertions found are packages/adapters/hono/src/hono.test.ts (pinning its own mock of the dispatcher producer), packages/client/tests/integration/01-discovery.test.ts (toMatch(/^v?\d+/) — which passes on 'v1' today and would also pass on a semver), and packages/spec/src/api/discovery.test.ts (a schema-parse fixture). "I found no consumer" is not "there is no consumer": embedder code outside these three repos is not visible from here.
Not in scope of this card
Neither producer is at fault here — packages/runtime/src/http-dispatcher.ts (#10993) and packages/metadata-protocol/src/protocol.ts (#11235) both derive the value correctly. This is the serving seam in @objectstack/rest only.
Found while implementing #11235 (deriving
getDiscovery()'sversioninstead of the'1.0'literal). Filed rather than fixed: #11235's dispatch bounded that card to the producer, and this is a different package with a real design question attached, so it is not a mechanical in-place fix.What was measured
packages/rest/src/rest-server.tscalls the producer and then unconditionally replaces the field one line later:config.api.versionis the API version identifier, not an artifact identity:normalizeConfig()in the same file defaults it to'v1'(api.version ?? 'v1').api.apiPath ?? \${api.basePath}/${api.version}`→/api/v1`.packages/spec/src/api/plugin-rest-api.zod.tsdeclares itversion: z.string().default('v1').describe('API version identifier').But
DiscoverySchema(packages/spec/src/api/discovery.zod.ts) declares that field under/** System Identity */, grouped withnameandenvironment— the "what server is this" question. #10993's ruling and its evidence (the SDK'sconnect()loggingdata.versionnext toapiNameandservicesunder "Connected to ObjectStack server") settled that reading.So on any REST-served host,
GET /api/v1/discoveryanswersversion: "v1"— the path segment the caller already typed into the URL to get there.Two consequences worth separating
/healthand the runtime dispatcher's/discoveryboth report a real derived artifact version after [finding] /api/v1/health reports a hardcoded version: '1.0.0' — a field that exists and lies, so no consumer can use it for artifact identity #10993; the REST/discoveryreports"v1"on every build of every release forever. A consumer reading one document cannot tell which producer answered, and gets a useless answer from the one that most clients hit.versionliteral —getDiscovery()inpackages/metadata-protocol/src/protocol.ts, identical defect to #10993, different producer/package #11235 fixedgetDiscovery()'s hardcoded'1.0', but that value is overwritten before it reaches the wire — the producer fix is correct and pins the class at the producer level, and is observable to embedders callingObjectStackProtocol.getDiscovery()directly, but it changes nothing on the REST endpoint while this line stands. The two fixes are independent and this one is where the wire-visible behaviour lives.Why this is a decision, not a codemod
Deleting the line is not obviously right on its own.
api.versionis real information a client may want (which mounted API version answered), and the schema has no field for it —DiscoverySchemadeclaresname/version/environment/routes/locale/services/capabilities/schemaDiscovery/scopingand nothing that means "API version". Options, roughly:routes.*, all of which are prefixed with the mounted base path).packages/spec,domain:specseat, and a new field on a machine-readable surface needs its own justification under startup scope discipline.versionmeans the API version on the REST producer and the artifact version on the dispatcher producer — i.e. accepting exactly the two-dialects-of-one-field situation 两个 discovery 生产者都在线上返回 schema 未声明的顶层字段(scoping / features / endpoints),且 REST 形状永远无法通过 DiscoverySchema #4828 / SDK 的 client.capabilities 声明为 WellKnownCapabilities,但两个 discovery 生产者填的是互不相交的键集 #5672 / [finding] /api/v1/health reports a hardcoded version: '1.0.0' — a field that exists and lies, so no consumer can use it for artifact identity #10993 / [finding] a THIRD hardcoded discoveryversionliteral —getDiscovery()inpackages/metadata-protocol/src/protocol.ts, identical defect to #10993, different producer/package #11235 have been closing.Recommend A unless someone can name a consumer of the API-version fact that
routesdoes not already serve; B only if such a consumer exists. Either way it is a triage call, not a dev call.Consumer survey (same one run for #11235)
No consumer reads discovery
versionby value — no version comparison, feature gate, or cache key — inobjectstack,objectuiorcloud. Controls: an org-wide code search for a known-present string (x-objectstack-build-sha) returned 10 hits acrossobjectstackandcloud, so the queries were live in both repos. The only by-value assertions found arepackages/adapters/hono/src/hono.test.ts(pinning its own mock of the dispatcher producer),packages/client/tests/integration/01-discovery.test.ts(toMatch(/^v?\d+/)— which passes on'v1'today and would also pass on a semver), andpackages/spec/src/api/discovery.test.ts(a schema-parse fixture). "I found no consumer" is not "there is no consumer": embedder code outside these three repos is not visible from here.Not in scope of this card
Neither producer is at fault here —
packages/runtime/src/http-dispatcher.ts(#10993) andpackages/metadata-protocol/src/protocol.ts(#11235) both derive the value correctly. This is the serving seam in@objectstack/restonly.Generated by Claude Code