Summary
The mock server null-initializes optional schema properties on create — matching database NULL behavior for unset columns. As a result, GET responses return null for any optional field not provided. Blueprint schemas don't currently mark optional fields as nullable: true, so generated SDK clients using strict type validation (e.g., Zod) reject the null values with type errors.
This mismatch surfaced in the steel thread: income records with unset incomeBasis, unearnedType, startDate, and endDate cause ZodError in listMemberIncome, breaking the income section form.
Expected behavior
null is a valid value for any optional field in a GET response. Consumers do not need to distinguish between null (field stored as NULL in the database) and absent (field not in the response) — both mean "not set."
Industry convention
This is the standard pattern across major REST APIs:
- GitHub REST API — optional fields are returned as
null when unset, not omitted. The schema marks them nullable: true.
- Stripe API — optional fields that haven't been set are returned as
null explicitly.
- JSON:API spec — resource attributes may be
null; absent and null are treated as distinct but both valid.
- OpenAPI best practice — fields that may be stored as NULL and returned as null in responses should be declared
nullable: true (OAS 3.0) or type: [T, 'null'] (OAS 3.1). Omitting nullable implies the field is never null.
Decision
All optional fields in blueprint response schemas should be marked nullable: true. The convention is:
- Required fields — always present, never null.
- Optional fields — may be absent on write; returned as
null in GET responses when not set. Always nullable: true.
This should be documented in packages/contracts/patterns/api-patterns.yaml as a standing convention so states extending schemas follow the same pattern.
Scope
All optional properties across all blueprint schemas. Known immediate impact:
packages/contracts/schemas/common/income.yaml — incomeBasis, unearnedType, startDate, endDate
All other schemas with optional non-required fields are affected but haven't surfaced yet because they haven't been exercised by a generated SDK client with strict response validation.
Short-term workaround
Adopters can mark the specific fields causing issues as nullable: true in their overlays while the blanket fix is applied to the blueprint.
Summary
The mock server null-initializes optional schema properties on create — matching database NULL behavior for unset columns. As a result, GET responses return
nullfor any optional field not provided. Blueprint schemas don't currently mark optional fields asnullable: true, so generated SDK clients using strict type validation (e.g., Zod) reject the null values with type errors.This mismatch surfaced in the steel thread: income records with unset
incomeBasis,unearnedType,startDate, andendDatecauseZodErrorinlistMemberIncome, breaking the income section form.Expected behavior
nullis a valid value for any optional field in a GET response. Consumers do not need to distinguish betweennull(field stored as NULL in the database) and absent (field not in the response) — both mean "not set."Industry convention
This is the standard pattern across major REST APIs:
nullwhen unset, not omitted. The schema marks themnullable: true.nullexplicitly.null; absent and null are treated as distinct but both valid.nullable: true(OAS 3.0) ortype: [T, 'null'](OAS 3.1). Omittingnullableimplies the field is never null.Decision
All optional fields in blueprint response schemas should be marked
nullable: true. The convention is:nullin GET responses when not set. Alwaysnullable: true.This should be documented in
packages/contracts/patterns/api-patterns.yamlas a standing convention so states extending schemas follow the same pattern.Scope
All optional properties across all blueprint schemas. Known immediate impact:
packages/contracts/schemas/common/income.yaml—incomeBasis,unearnedType,startDate,endDateAll other schemas with optional non-required fields are affected but haven't surfaced yet because they haven't been exercised by a generated SDK client with strict response validation.
Short-term workaround
Adopters can mark the specific fields causing issues as
nullable: truein their overlays while the blanket fix is applied to the blueprint.