generation pipeline

rule

generation flows from the authoritative authored input to its consumers: schemas and routes → OpenAPI → Fern client → application build. every package that owns a generator exposes its own generate script; the root pipeline orchestrates it through Turbo and never recreates generator behavior itself.

why

every downstream artifact is machine-replaceable — any hand repair to it is silently discarded on the next generation run. a one-directional chain with one authored source keeps a single contract; parallel hand-written types or edits to generated output drift while each piece still typechecks locally.

workflow

  1. change the source schema, route graph, or generator input.
  2. run the producer's generate task.
  3. run the dependent generator, such as Fern.
  4. typecheck the generated package and consuming application.
  5. inspect the generated diff; repair its source, never its output.

authority chain

the chain is one-directional: route or schema (the consumer contract) → contract generator (machine-readable API description) → client generator (typed consumption code) → application import. only the authored upstream decision is edited by hand.

tempting shortcutfailure it causes
edit a generated client to unblock one consumerthe next generation silently discards the repair.
hand-write a parallel request typetwo contracts drift while each still typechecks locally.
run generation only in one applicationanother consumer reads stale or missing output.
generate after building consumersthe build result depends on previous local state.

good and bad

bad

a consumer breaks against a generated route, so the generated client method is edited by hand:

// apps/_client/src/generated/orders.ts — GENERATED FILE
// WRONG — hand-edited generated output
export function createOrder(input: CreateOrderInput): Promise<OrderResponse> {
// original signature didn't match; someone changed it here instead of the source
return fetch("/orders", { method: "POST", body: JSON.stringify(input) });
}

the application builds and tests pass. the next pnpm generate run regenerates the client from the backend schema and silently rewrites the hand-edited method back to its original broken form. nobody notices until production.

good

the backend route contract is corrected at its source, the producer's task runs, and both packages regenerate:

// apps/backend/src/infra/http/orderRoutes.ts — AUTHORED SOURCE
export const orderRoutes = new Hono().post("/orders", async (c) => {
const input = CreateOrderSchema.parse(await c.json());
return c.json({ orderId: await createOrder(input) });
});
// Run: pnpm --filter @scope/backend generate
// This regenerates OpenAPI from routes.
// Then: pnpm --filter @scope/client generate
// This regenerates the client from OpenAPI.
// apps/_client/src/generated/orders.ts — AUTO-REGENERATED
export async function createOrder(input: CreateOrderInput): Promise<{ orderId: string }> {
// signature matches the authored route; next generate run will keep it stable
}

verification

from a clean generated-output state, run the producer then every consumer. inspect that the generated diff corresponds only to the authored contract change, and search for handwritten duplicates of the new public type or route.

search pages

go to any page