generated-artifact boundary

rule

generated output is owned by its generator input and command. fix the source, regenerate, review the result, never hand-edit the artifact.

why

a hand-edit to a generated file is a temporary local fork: the next generation overwrites the repair without repairing its cause. two versions of truth exist (generator input vs edited artifact) and no one knows which is authoritative; consumers build against stale edited code while the authored input moves on. durable fix: change the authored owner, regenerate all dependents, prove the output correct. a fresh generation from unchanged input must produce the same artifact — otherwise the generator is nondeterministic and the authority boundary is broken.

applies to

OpenAPI documents, Fern SDKs, generated indexes, Worker bindings, migration metadata, ORM declarations, build output.

repair direction

trace backward: generated file → generator command/config → authored source input. repair the first authored owner that explains the wrong output, then regenerate every dependent. a generated diff is evidence of source behavior, not a second implementation surface.

observationrepair owner
generated client lacks an operationmounted route/schema/OpenAPI source input.
generated type has a wrong field or closed valuenamed source contract/schema.
generated name contains marketing identitygenerator input, API title, schema, or organization metadata.
generation fails or produces at the wrong pathgenerator configuration and task graph.
consumer cannot import generated outputpackage public export/build boundary.
generated content differs after no source changenondeterministic generator input, environment, or tool version.

decision process

shortcutfailureselected direction
edit a generated linenext generation removes the repair.edit source and regenerate.
hand-write a missing consumer typeparallel contracts drift while local checks stay green.expose/regenerate the public generated type.
commit unexplained generated diffsource authority and tool behavior remain unknown.inspect the source-to-output chain before accepting it.
regenerate only at release timeconsumers build against stale local history.make generation an owned lifecycle task.

verification

  • run the owner generator, typecheck the produced package, inspect whether the generated diff corresponds to the authored input.
  • from a clean derived-output state, run the direct consumer — proves the task graph creates output before import and no untracked hand edit or local cache carries the build.

good and bad

bad

// route schema (authored)
export const ReadWorkflowStatusResponseSchema = z.object({
phase: z.string(), // oops: should be an enum
output: z.string(),
});
// generated client (v1) — wrong type
export type ReadWorkflowStatusResponse = {
phase: string;
output: string;
};
// developer hand-edits the generated file (WRONG — temporary fork)
export type ReadWorkflowStatusResponse = {
phase: "queued" | "running" | "complete"; // added by hand ✗
output: string;
};
// interface imports the hand-edited type, works fine
const { phase } = await readWorkflowStatus();
if (phase === "running") { ... } // TypeScript thinks phase is enum
// next morning: `pnpm generate` overwrites the hand-edit, source still wrong
export type ReadWorkflowStatusResponse = {
phase: string; // BACK TO WRONG TYPE; hand-edit is gone
output: string;
};
// interface now has a type error
const { phase } = await readWorkflowStatus();
if (phase === "running") { ... } // ERROR: phase is string, not literal union

the hand-edit fixed the symptom (wrong generated type), not the root cause (wrong source schema). next generation reverts it; the interface breaks.

good

// route schema (authored) — fix the source
export const ReadWorkflowStatusResponseSchema = z.object({
phase: WorkflowPhaseEnumSchema, // FIXED: enum instead of string
output: z.string(),
});
// never edit generated/client.ts directly — run generation
pnpm generate
// generated/client.ts (regenerated from fixed source)
export type ReadWorkflowStatusResponse = {
phase: "queued" | "running" | "complete"; // now correct
output: string;
};
// typecheck passes; generated type is authoritative
const { phase } = await readWorkflowStatus();
if (phase === "running") { ... } // ✓ phase correctly narrowed

fix the authored schema, regenerate; every consumer gets the correct type from one source of truth.

search pages

go to any page