| name | bloodbank-sdk-generation |
| description | Generate typed SDK bindings (Pydantic v2 models, TypeScript types) from the Bloodbank JSON Schema tree at `bloodbank/schemas/bloodbank/v1/**` (Draft 2020-12). Uses `datamodel-code-generator` and `json-schema-to-typescript` — NOT the deprecated hand-rolled Holyfields generators. Use when generating event-contract bindings for a Bloodbank consumer, importing typed CloudEvents envelopes into another project, or scaffolding a `bloodbank-contracts` SDK package. Trigger keywords — "bloodbank SDK", "bloodbank contracts", "Pydantic from bloodbank schemas", "TypeScript types for bloodbank events", "bloodbank.v1.* types", "event contract bindings", "datamodel-code-generator", "json-schema-to-typescript", "regenerate SDK", "CloudEvents bindings". Do NOT use for schema authoring (edit `bloodbank/schemas/` directly per `docs/event-naming.md` §12), runtime envelope validation (use `BLOODBANK_HOOK_VALIDATE=1`), schema-tree consistency (`mise run smoketest:schemas`), or generic JSON Schema codegen unrelated to Bloodbank. |
Bloodbank SDK Generation
Deterministic recipe for generating typed bindings from bloodbank/schemas/bloodbank/v1/**. Outputs are plain artifacts the consumer commits or vendors — this skill produces code, not an installable package.
Prereqs
- Run from the Bloodbank repo root (the directory containing
schemas/ and docs/event-naming.md), or pass the schema path explicitly.
uvx (from uv) for Python tooling, npx for TypeScript tooling — both are project-less, no install required.
- Validate the tree is well-formed first:
mise run validate:schemas (or bash scripts/validate_schemas.sh). Skip generation if it fails — fix schemas before generating.
Python (Pydantic v2 models)
uvx --from 'datamodel-code-generator>=0.25' datamodel-codegen \
--input schemas/bloodbank/v1 \
--input-file-type jsonschema \
--output-model-type pydantic_v2.BaseModel \
--target-python-version 3.12 \
--use-schema-description \
--use-field-description \
--use-double-quotes \
--enum-field-as-literal one \
--output bloodbank_contracts/models.py
Flag rationale:
--input-file-type jsonschema — required; the default auto mis-detects when the tree has multiple files.
--enum-field-as-literal one — properties.type.const becomes Literal["bloodbank.v1.<...>"], matching the 5-token contract.
--use-schema-description / --use-field-description — preserves the human-readable descriptions from the JSON Schemas as Pydantic field docstrings.
--target-python-version 3.12 — matches the project floor; bump if the project floor moves.
The generator resolves $ref: "../../../_common/cloudevent_base.v1.json" correctly because all schemas live under one input root.
TypeScript (interfaces + type unions)
npx json-schema-to-typescript \
'schemas/bloodbank/v1/**/*.v1.json' \
--cwd schemas \
--bannerComment '' \
--unreachableDefinitions \
--additionalProperties false \
> bloodbank_contracts/types.ts
Flag rationale:
--cwd schemas — sets the resolver root so $ref: "../../../_common/..." resolves from each file's directory through the same root the schemas were authored against.
--bannerComment '' — drops the autogenerated header that fights with linters.
--unreachableDefinitions — emits the _common types even if no top-level schema references them by name, so consumers can import CloudEventBase directly.
--additionalProperties false — matches the strict-shape default of the runtime validator.
Verify the output
After generation, run a one-shot import smoke:
python3 -c "from bloodbank_contracts.models import *; import bloodbank_contracts.models as m; print(f'{sum(1 for n in dir(m) if not n.startswith(\"_\")):>3} symbols')"
npx tsc --noEmit --strict bloodbank_contracts/types.ts
If either fails, regenerate from a clean validate:schemas pass before debugging the generator output.
Common combinations
- Refresh + commit on schema change: re-run both generators whenever you edit anything under
bloodbank/schemas/bloodbank/v1/. Treat the generated files as committed artifacts the consumer holds — git diff is the drift signal.
- One-off consumer scaffold: a downstream project that needs the types runs both commands once, vendors the outputs, and pins via the schema-tree commit hash recorded in their changelog.
- Future packaging: when a second external consumer materializes, promote
bloodbank_contracts/ into a real package (uv build for Python, tsc --declaration + package.json for TS). See RECOMMENDATION.md step 5 in the Bloodbank repo.
Gotchas
Read references/gotchas.md only when generation fails with a non-obvious error, output looks wrong (missing fields, broken refs, unexpected types), or you're integrating the generated code into an existing project with its own Pydantic/Zod setup.
Out of scope
This skill produces SDK artifacts from the canonical schema tree. It does NOT cover:
- Authoring or modifying schemas. Edit JSON files under
bloodbank/schemas/ directly per bloodbank/docs/event-naming.md §12.
- Validating envelopes at runtime. Set
BLOODBANK_HOOK_VALIDATE=1 and rely on services/agent-hooks/core/validate.py, which uses a pre-loaded referencing.Registry.
- Checking schema-tree consistency. Run
mise run validate:schemas (well-formedness + ref resolution) and mise run smoketest:schema-contract-consistency (validator allowlist drift).
- The deprecated Holyfields generators (
tools/generators/generate_pydantic.py, tools/generators/generate_typescript.sh). Those are hand-rolled, locked to Holyfields' packages/python workspace, and superseded by the third-party tools above. Do not port them.
- Apicurio registry uploads. The
apicurio://holyfields/<type>/versions/1 dataschema URI is a registry artifact id, separate from this skill's filesystem generation path.
- Publishing the SDK (PyPI, npm, GitHub Packages). This skill writes files; distribution is a separate decision.
- Zod schemas.
json-schema-to-typescript emits TypeScript types + interfaces, not Zod validators. If runtime TS validation is needed, layer a tool like json-schema-to-zod on top — but evaluate whether the type-only output is sufficient first.