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
spec(api): ApiEndpoint.target is required on every endpoint but read only for type: 'flow' — an object_operation author must write a value nothing consumes #10338
Found while writing the developer page for declarative endpoints (#10215 / PR #10333). Not fixed there — that card is documentation-only and forbids touching packages/spec/**.
The fact
ApiEndpointSchema.target is required — target: z.string(), no .optional(), no .default() (packages/spec/src/api/endpoint.zod.ts). But only one of the two executable types reads it:
packages/runtime/src/endpoint-executor.ts — planEndpointTarget reads endpoint.target inside the type === 'flow' branch only; the object_operation branch is addressed entirely by objectParams.object / .operation.
packages/rest/src/openapi-endpoints.ts — same split: endpoint.target is consulted only on the flow branch.
packages/spec/src/api/endpoint-publish-gate.ts — targetGate requires target for flow and never looks at it for object_operation.
The key's own .describe() already records this ("neither the publish gate nor the executor reads target for that type"), so the state is known — what is missing is any consequence. An object_operation author is required by the type system to write a string that reaches no consumer.
It invites a value that reads as configuration and is not. Convention in the repo is to repeat the object name (target: 'showcase_task' in examples/app-showcase/src/system/apis/index.ts, target: 'acme_lead' in content/docs/protocol/kernel/http-protocol.mdx). Nothing checks that it agrees with objectParams.object. A declaration whose target says acme_lead and whose objectParams.object says acme_contact publishes green and serves acme_contact — the author reads the first line.
AI-authoring safety. A required key with no consumer is exactly where a generated declaration puts a plausible-looking wrong value, and there is no gate to catch it.
Measured, not recalled
check:skill-examples is what surfaced it: an object_operation example written without target — which is what the executor semantics suggest — fails to type-check against the current spec:
error TS2322: Type '{ name: string; path: string; method: "GET"; … objectParams: { object: string; operation: "find"; }; … }'
is not assignable to type 'Omit<{ … target: string; … }, "name"> & { … }'
B. Keep it required, add a gate: for object_operation, refuse a target that disagrees with objectParams.object. Cheaper, no vocabulary change, closes the disagreement hole in (2) — but it makes a redundant key mandatory and checked, which is a strange contract to defend.
Found while writing the developer page for declarative endpoints (#10215 / PR #10333). Not fixed there — that card is documentation-only and forbids touching
packages/spec/**.The fact
ApiEndpointSchema.targetis required —target: z.string(), no.optional(), no.default()(packages/spec/src/api/endpoint.zod.ts). But only one of the two executable types reads it:packages/runtime/src/endpoint-executor.ts—planEndpointTargetreadsendpoint.targetinside thetype === 'flow'branch only; theobject_operationbranch is addressed entirely byobjectParams.object/.operation.packages/rest/src/openapi-endpoints.ts— same split:endpoint.targetis consulted only on theflowbranch.packages/spec/src/api/endpoint-publish-gate.ts—targetGaterequirestargetforflowand never looks at it forobject_operation.The key's own
.describe()already records this ("neither the publish gate nor the executor readstargetfor that type"), so the state is known — what is missing is any consequence. Anobject_operationauthor is required by the type system to write a string that reaches no consumer.Why this is worth a decision rather than a shrug
lookuptransform params (object / fromField / toField / autoCreate) are authorable and read by nothing #10329.target: 'showcase_task'inexamples/app-showcase/src/system/apis/index.ts,target: 'acme_lead'incontent/docs/protocol/kernel/http-protocol.mdx). Nothing checks that it agrees withobjectParams.object. A declaration whosetargetsaysacme_leadand whoseobjectParams.objectsaysacme_contactpublishes green and servesacme_contact— the author reads the first line.Measured, not recalled
check:skill-examplesis what surfaced it: anobject_operationexample written withouttarget— which is what the executor semantics suggest — fails to type-check against the current spec:Options (for triage — no obvious answer)
targetoptional in the vocabulary and require it at the gate fortype: 'flow'. Contract-first: declared = enforced, and the author of anobject_operationstops writing a dead string. Cost: it edits the vocabulary 17.x 立项:建设声明式 ApiEndpoint 执行器(挂载 + matchEndpoint + authRequired/cacheTtl/inputMapping/outputMapping 逐键接线) #5040 §0 froze, andtargetis currently non-optional in every stored row and fixture.object_operation, refuse atargetthat disagrees withobjectParams.object. Cheaper, no vocabulary change, closes the disagreement hole in (2) — but it makes a redundant key mandatory and checked, which is a strange contract to defend.apis:endpoints (ADR-0121) #10333, which states the requirement and tells the author to keep the two in step. This issue exists because documentation is the weakest of the three.The vocabulary question belongs to whoever owns ADR-0121 / #5040 §0, not to a docs card.