Observation from #11984, which made RestServer.normalizeConfig parse and consume crud / metadata / batch / routes against the schemas that declare them. Executing the declared contract settles accept/reject; it does not make a key LIVE. While measuring which keys the method reads (the consume decision), the other half of the census came out: which normalized keys anything then reads.
What was measured
On origin/main @ 08e49496f, packages/rest/src non-test sources, every read site outside NormalizedRestServerConfig's type declaration and normalizeConfig itself (grep per key, comments excluded; the overrides hits are a request body and a test builder, not the config):
Read by the runtime — crud.operations.create|read|update|delete|list (route registration), crud.dataPrefix, metadata.prefix, metadata.enableCache, metadata.maskObjectFields, metadata.endpoints.types|items|item, batch.maxBatchSize, batch.enableBatchEndpoint, batch.operations.createMany|updateMany|deleteMany.
Normalized and never read (0 sites each) — ten declared keys:
| sub-object |
key |
declared as |
routes |
includeObjects |
z.array(z.string()).optional() |
routes |
excludeObjects |
z.array(z.string()).optional() |
routes |
nameTransform |
enum none / plural / kebab-case / camelCase, default none |
routes |
overrides |
per-object enabled / basePath / operations record |
crud |
patterns |
enum-keyed record of CrudEndpointPattern |
crud |
objectParamStyle |
enum path / query, default path |
metadata |
cacheTtl |
z.number().int().default(3600) |
metadata |
endpoints.schema |
z.boolean().default(true) |
batch |
defaultAtomic |
z.boolean().default(true) |
batch |
operations.upsertMany |
z.boolean().default(true) |
No other package reads them off the REST config either (repo-wide grep for nameTransform / objectParamStyle / defaultAtomic outside packages/spec and this file: 0). None of the four schemas has an entry in packages/spec/liveness/api.json.
So, for example, routes.nameTransform: 'plural' is accepted, validated, normalized — and every route still mounts under the object's raw name; routes.excludeObjects: ['sys_log'] excludes nothing; metadata.cacheTtl: 60 changes no header; batch.defaultAtomic: false changes no batch. Declared-but-unenforced is the ADR-0049 class; the spec seat's own RestServerConfigSchema @example advertises routes: { excludeObjects: ['system_log'] }, so the docs promise a capability the runtime does not deliver (Prime Directive #10).
Not prejudged
Each key wants its own enforce-or-remove call — routes.* reads like a designed-but-never-wired feature (enforce would be real work in route generation), metadata.cacheTtl like a knob that lost its consumer (if kept, it also wants a lower bound: the schema declares .int() only, so a negative TTL is accepted — #11984 pins -1 as accepted because that is what the contract says), and endpoints.schema / upsertMany / defaultAtomic like switches whose routes were never built. The spec-property-retirement playbook covers the remove route; the enforce route is a feature per key. Related: #14365 (the patterns / overrides.*.operations record declarations) and #14366 (consume the parsed api output).
Observation from #11984, which made
RestServer.normalizeConfigparse and consumecrud/metadata/batch/routesagainst the schemas that declare them. Executing the declared contract settles accept/reject; it does not make a key LIVE. While measuring which keys the method reads (the consume decision), the other half of the census came out: which normalized keys anything then reads.What was measured
On
origin/main@08e49496f,packages/rest/srcnon-test sources, every read site outsideNormalizedRestServerConfig's type declaration andnormalizeConfigitself (grep per key, comments excluded; theoverrideshits are a request body and a test builder, not the config):Read by the runtime —
crud.operations.create|read|update|delete|list(route registration),crud.dataPrefix,metadata.prefix,metadata.enableCache,metadata.maskObjectFields,metadata.endpoints.types|items|item,batch.maxBatchSize,batch.enableBatchEndpoint,batch.operations.createMany|updateMany|deleteMany.Normalized and never read (0 sites each) — ten declared keys:
routesincludeObjectsz.array(z.string()).optional()routesexcludeObjectsz.array(z.string()).optional()routesnameTransformnone / plural / kebab-case / camelCase, defaultnoneroutesoverridesenabled/basePath/operationsrecordcrudpatternsCrudEndpointPatterncrudobjectParamStylepath / query, defaultpathmetadatacacheTtlz.number().int().default(3600)metadataendpoints.schemaz.boolean().default(true)batchdefaultAtomicz.boolean().default(true)batchoperations.upsertManyz.boolean().default(true)No other package reads them off the REST config either (repo-wide grep for
nameTransform/objectParamStyle/defaultAtomicoutsidepackages/specand this file: 0). None of the four schemas has an entry inpackages/spec/liveness/api.json.So, for example,
routes.nameTransform: 'plural'is accepted, validated, normalized — and every route still mounts under the object's raw name;routes.excludeObjects: ['sys_log']excludes nothing;metadata.cacheTtl: 60changes no header;batch.defaultAtomic: falsechanges no batch. Declared-but-unenforced is the ADR-0049 class; the spec seat's ownRestServerConfigSchema@exampleadvertisesroutes: { excludeObjects: ['system_log'] }, so the docs promise a capability the runtime does not deliver (Prime Directive #10).Not prejudged
Each key wants its own enforce-or-remove call —
routes.*reads like a designed-but-never-wired feature (enforce would be real work in route generation),metadata.cacheTtllike a knob that lost its consumer (if kept, it also wants a lower bound: the schema declares.int()only, so a negative TTL is accepted — #11984 pins-1as accepted because that is what the contract says), andendpoints.schema/upsertMany/defaultAtomiclike switches whose routes were never built. Thespec-property-retirementplaybook covers the remove route; the enforce route is a feature per key. Related: #14365 (thepatterns/overrides.*.operationsrecord declarations) and #14366 (consume the parsedapioutput).