
Ask for an artifact the application can use
A creation method needs usable product information, not just a convincing paragraph. Wildo supplies structured generation with the schema of the artifact it is producing.
The returned value is normalized and checked before landing. When a contract is not met, concrete findings can guide another attempt instead of leaving every downstream consumer to interpret the answer differently.
Example — Produce a roadmap with usable phases
The method asks for the roadmap’s structured contract. Its phases and references can then be checked and consumed by planning tools, rather than extracted later from a free-form description.
For engineers
A playbook declares an output family and schema reference. The runtime resolves every output binding before invoking generation and rejects unknown families or mismatched schema references. The host supplies the generation adapter, including provider configuration and call handling.
A family can declare a stricter generation schema alongside its stored schema. The runtime selects it here:
const generated = await input.model.generate({
prompt,
schema: binding.generation?.schema ?? binding.snapshotSchema,
label: `${input.playbookRef}:${output.family}`,
});
The distinction allows generation-specific requirements, such as a meaningful minimum collection, while retaining the application’s stored representation. It is a request and validation contract, not a claim that a model can never produce an invalid response.
Normalize the answer, then test its meaning
A family normalizer can assign stable reference shapes and versions before semantic validation. The run supplies its recorded time to normalizers that need it, so related outputs share the same observation instant.
This excerpt shows normalization failures becoming correction findings:
let candidate: unknown;
try {
candidate = binding.generation ? binding.generation.normalize(generated.output, { recordedAt, nextReviewOn: input.nextReviewOn }) : generated.output;
} catch (error) {
const findings = error instanceof z.ZodError
? error.issues.slice(0, 20).map((issue) => `${issue.path.map(String).join(".") || "<root>"}: ${issue.message}`)
: [error instanceof Error ? error.message : String(error)];
await trace.event({
type: PlaybookExecutionTraceEvent.GENERATION_REJECTED,
summary: `"${output.family}" failed its strict generation contract on attempt ${attempts}`,
payload: { family: output.family, attempt: attempts, errorDetails: findings },
});
rejectedFindings.push(findings.join("\n"));
accepted = undefined;
continue;
}
Provider-side schema findings are also retained when no output object is returned. Generation uses bounded findings-guided retries, then reports failure rather than treating a malformed answer as a finished artifact.
Keep the boundaries connected
| Stage | Responsibility |
|---|---|
| Generation schema | Defines the requested answer shape |
| Normalizer | Converts generated content into the family representation |
| Family validator | Checks supported semantic relationships |
| Cross-artifact checks | Compare with supplied application context |
| Landing | Revalidates and writes the intended source representation |
Some families use their stored schema directly. Read the registered binding to know which normalization and extra generation constraints apply. A valid object still needs product judgment and implementation evidence; structured generation makes it consumable, not automatically correct.