Skip to content

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

Description

@os-elon

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 requiredtarget: 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.tsplanEndpointTarget 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.tstargetGate 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.

Why this is worth a decision rather than a shrug

  1. It is the declared-but-inert shape, one level down. Not a whole key nothing reads (the usual form), but a key that is inert for one of the two types that execute. The same class as spec(data): mapping lookup transform params (object / fromField / toField / autoCreate) are authorable and read by nothing #10329.
  2. 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.
  3. 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"> & { … }'

Options (for triage — no obvious answer)

The vocabulary question belongs to whoever owns ADR-0121 / #5040 §0, not to a docs card.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions