fix(openprd): allow null on optional front-matter keys

Follow-up to #96. The template and tool-generated PRDs (moshcode /prd) ship
optional keys blank; YAML reads them as null. Accept null on owner/repo/created/
updated/discussion/implementation/tags/supersedes/superseded-by/authors so
template-derived files validate against the schema.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Anthony Ettinger 2026-07-12 15:59:03 +00:00
parent 756c3f6f21
commit 1d079db0dd

View file

@ -2,7 +2,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema", "$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://schemas.logicsrc.com/openprd-prd.schema.json", "$id": "https://schemas.logicsrc.com/openprd-prd.schema.json",
"title": "OpenPRD Product Requirements Document", "title": "OpenPRD Product Requirements Document",
"description": "The front-matter manifest of an OpenPRD document. OpenPRD is a lightweight, DIP-style proposal standard: a repo publishes a numbered collection of PRDs under prd/ (prd/NNNN-slug.md), with a 0000-template.md and a README index, committed to the repo like a BIP/EIP/DIP process. This schema governs the YAML front-matter; the body is Markdown with a fixed set of sections.", "description": "The front-matter manifest of an OpenPRD document. OpenPRD is a lightweight, DIP-style proposal standard: a repo publishes a numbered collection of PRDs under prd/ (prd/NNNN-slug.md), with a 0000-template.md and a README index, committed to the repo like a BIP/EIP/DIP process. This schema governs the YAML front-matter; the body is Markdown with a fixed set of sections. Optional keys MAY be present but empty (null) — the template ships them blank for authors to fill.",
"type": "object", "type": "object",
"required": ["openprd", "id", "title", "status"], "required": ["openprd", "id", "title", "status"],
"additionalProperties": false, "additionalProperties": false,
@ -28,51 +28,50 @@
"description": "Lifecycle stage. Draft → Review → Accepted → Final; or Rejected/Withdrawn/Superseded." "description": "Lifecycle stage. Draft → Review → Accepted → Final; or Rejected/Withdrawn/Superseded."
}, },
"authors": { "authors": {
"type": "array", "type": ["array", "null"],
"items": { "type": "string" }, "items": { "type": "string" },
"minItems": 1,
"uniqueItems": true, "uniqueItems": true,
"description": "Author identities — email, handle, or DID." "description": "Author identities — email, handle, or DID."
}, },
"owner": { "owner": {
"type": "string", "type": ["string", "null"],
"description": "Optional DID or handle of the accountable owner (e.g. a LogicSRC account/agent DID)." "description": "Optional DID or handle of the accountable owner (e.g. a LogicSRC account/agent DID)."
}, },
"repo": { "repo": {
"type": "string", "type": ["string", "null"],
"description": "Optional owner/name of the repository this PRD targets." "description": "Optional owner/name of the repository this PRD targets."
}, },
"created": { "created": {
"type": "string", "type": ["string", "null"],
"format": "date", "format": "date",
"description": "ISO date (YYYY-MM-DD) the PRD was created." "description": "ISO date (YYYY-MM-DD) the PRD was created."
}, },
"updated": { "updated": {
"type": "string", "type": ["string", "null"],
"format": "date", "format": "date",
"description": "ISO date (YYYY-MM-DD) of the last substantive edit." "description": "ISO date (YYYY-MM-DD) of the last substantive edit."
}, },
"discussion": { "discussion": {
"type": "string", "type": ["string", "null"],
"description": "Optional URL to the PR/issue/thread where this PRD is discussed." "description": "Optional URL to the PR/issue/thread where this PRD is discussed."
}, },
"implementation": { "implementation": {
"type": "string", "type": ["string", "null"],
"description": "Optional URL/reference to the implementation PR or tracking issue." "description": "Optional URL/reference to the implementation PR or tracking issue."
}, },
"tags": { "tags": {
"type": "array", "type": ["array", "null"],
"items": { "type": "string" }, "items": { "type": "string" },
"uniqueItems": true, "uniqueItems": true,
"description": "Optional freeform labels for grouping PRDs." "description": "Optional freeform labels for grouping PRDs."
}, },
"supersedes": { "supersedes": {
"type": "string", "type": ["string", "null"],
"pattern": "^\\d{4}$", "pattern": "^\\d{4}$",
"description": "Optional id of an earlier PRD this one replaces." "description": "Optional id of an earlier PRD this one replaces."
}, },
"superseded-by": { "superseded-by": {
"type": "string", "type": ["string", "null"],
"pattern": "^\\d{4}$", "pattern": "^\\d{4}$",
"description": "Optional id of a later PRD that replaces this one." "description": "Optional id of a later PRD that replaces this one."
} }