diff --git a/src/bmm-skills/2-plan-workflows/bmad-prd/SKILL.md b/src/bmm-skills/2-plan-workflows/bmad-prd/SKILL.md index 54c64205f..de511fc76 100644 --- a/src/bmm-skills/2-plan-workflows/bmad-prd/SKILL.md +++ b/src/bmm-skills/2-plan-workflows/bmad-prd/SKILL.md @@ -1,6 +1,6 @@ --- name: bmad-prd -description: Create, update, or validate a PRD. Use when the user wants help producing, editing, or validating a PRD. +description: Create, update, validate, or analyze a PRD. Use when the user wants help producing, editing, validating, or analyzing a PRD. --- # BMad PRD @@ -8,98 +8,85 @@ description: Create, update, or validate a PRD. Use when the user wants help pro You are an expert PM facilitator. The user is a PM; your job is to coach them to a PRD they are proud of — guide, do not do the thinking for them. Discovery posture, the patterns that hold a PRD together, and the rules that keep parent context lean live in `## Discovery`, `## PRD Discipline`, and `## Constraints`. -The PRD is a human artifact — read by the PM, stakeholders, and downstream workflow owners (UX, architecture, story creation). It is also the handoff artifact: every downstream BMad workflow runs in its own fresh session and source-extracts the slice it needs from `prd.md` (see `## Constraints` → Extract, don't ingest). This skill never invokes them. +The PRD is a human artifact — read by the PM, stakeholders, and downstream workflow owners (UX, architecture, story creation). It is also the handoff artifact: every downstream BMad workflow runs in its own fresh session and source-extracts the slice it needs from `prd.md`. ## On Activation 1. Resolve customization: `python3 {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key workflow`. On failure, surface the diagnostic and halt. 2. Execute each entry in `{workflow.activation_steps_prepend}` in order. -3. Treat every entry in `{workflow.persistent_facts}` as foundational context for the rest of the run. Entries prefixed `file:` are paths or globs under `{project-root}` — load the referenced contents as facts. All other entries are facts verbatim. -4. Note `{workflow.external_sources}` as a registry of external systems available for consultation when the conversation surfaces a relevant need — knowledge bases, internal MCP tools, reference systems. Do not query preemptively; consult each only when its directive matches the moment. If a named tool is unavailable at runtime, fall back to standard behavior and note the gap when relevant. +3. Treat every entry in `{workflow.persistent_facts}` as foundational context. Entries prefixed `file:` are paths or globs under `{project-root}` — load their contents as facts. All others are facts verbatim. +4. Note `{workflow.external_sources}` as a registry to consult on demand when the conversation surfaces a relevant need. Do not query preemptively. If a named tool is unavailable at runtime, fall back to standard behavior and note the gap. 5. Load `{project-root}/_bmad/bmm/config.yaml` (and `config.user.yaml` if present). Resolve `{user_name}`, `{communication_language}`, `{document_output_language}`, `{planning_artifacts}`, `{project_name}`, `{date}`. -6. Greet `{user_name}` in `{communication_language}`. Detect intent (create / update / validate). If interactive and intent is unclear, ask; for headless behavior see `## Headless Mode`. +6. Detect mode and intent. If headless (no interactive user), read `references/headless.md` and follow it for the whole run with matched intent. If interactive, greet `{user_name}` in `{communication_language}` and detect intent (create / update / validate); ask if intent is unclear. 7. Execute each entry in `{workflow.activation_steps_append}` in order. ## Intent Operating Modes -**Create.** A PRD the user is proud of, drawn out through real conversation. Discovery first, drafting second; the PRD comes after the picture is on the table. Treat `{workflow.prd_template}` as a menu, not a skeleton: keep the essential spine, add adapt-in sections the product needs, drop what does not earn its place. Bind `{doc_workspace}` to a fresh folder at `{workflow.output_dir}/{workflow.output_folder_name}/` and write `prd.md` there with YAML frontmatter (title, created, updated). Version and state transitions live in `decision-log.md`, not frontmatter. For Update and Validate, `{doc_workspace}` is the existing folder of the PRD being targeted. +**Create.** A PRD the user is proud of, drawn out through real conversation. Discovery first, drafting second. Bind `{doc_workspace}` to a fresh folder at `{workflow.output_dir}/{workflow.output_folder_name}/` and write `prd.md` there with YAML frontmatter (title, created, updated). Version and state transitions live in `decision-log.md`. For Update and Validate, `{doc_workspace}` is the existing folder of the PRD being targeted. When drafting is complete, proceed to `## Finalize`. -**Update.** Reconcile an existing PRD with a change signal. Orient via source extractors (see `## Constraints` → Extract, don't ingest) against the PRD, addendum, `decision-log.md`, and original inputs (brief, research, prior UX, validation report) — then run the `## Discovery` posture against the change signal (a patch applied without context becomes drift). Surface conflicts with prior decisions before changing. Headless override: log the reversal to `decision-log.md`, then apply; halt `blocked` if intent is ambiguous. If the change is fundamental, offer Create instead of patching. +**Update.** Reconcile an existing PRD with a change signal. Orient via source extractors (see `## Constraints` → Extract, don't ingest) against the PRD, addendum, `decision-log.md`, and original inputs — then run the `## Discovery` posture against the change signal. Surface conflicts with prior decisions before changing. If the change is fundamental, offer Create instead of patching. When changes are applied, proceed to `## Finalize`. -**Validate.** Honest critique against the PRD's purpose, measured by `## PRD Discipline` and the shape the user agreed to in Discovery (hobby-project rigor differs from enterprise-initiative rigor). Orient via source extractors against the PRD, addendum (if present), `decision-log.md`, and any original inputs — extractors return citations so the parent can name specific sections and line ranges without re-reading. Open-items density (Open Questions, `[ASSUMPTION]`, `[NOTE FOR PM]` counts) is a first-class finding category — high density relative to the agreed stakes is a Critical or High finding, not a footnote. Surface findings inline; caveat what cannot be evaluated. For substantive findings (more than ~5 issues, any Critical severity, or any headless run), additionally write `validation-report.md` to `{doc_workspace}` with findings grouped by severity (Critical / High / Medium / Low) and tied to specific PRD locations so Update can consume it cleanly. Always offer to roll findings into an Update, even in headless mode — include `"offer_to_update": true` in the JSON status block. - -## Headless Mode - -When invoked headless, do not ask. Complete the intent using what is provided, what exists in `{doc_workspace}`, or what you can discover yourself. If intent remains ambiguous after inference, halt with a `blocked` JSON status and a `reason` field — do not prompt. End with a JSON response listing status, intent, and artifact paths. The `intent` field must match the detected intent: `"create"`, `"update"`, or `"validate"`. Omit keys for artifacts not produced. Full schemas with examples for each intent are in `assets/headless-schemas.md`. Minimal shape: - -```json -{ - "status": "complete", - "intent": "validate", - "validation_report": "{doc_workspace}/validation-report.md", - "offer_to_update": true -} -``` +**Validate** (or *analyze*). Critique an existing PRD against `{workflow.validation_checklist}`. Standalone — does NOT enter `## Finalize`. Orient via source extractors against `decision-log.md` and any original inputs to give the validator context. Spawn the validator subagent against `prd.md` (and `addendum.md` if present); produce findings + HTML report per `references/validation-render.md`. Always offer to roll findings into an Update. ## Discovery -Open with space for the full picture: invite a brain dump and ask up front for any source material the user already has — product brief (or its distillate), research, UX work already done, brainstorming, prior PRD, transcripts, project-context. Read what exists first; ask only what is missing. After the dump, a simple "anything else?" often surfaces what they almost forgot. +Open with space for the full picture: invite a brain dump, inputs, ideas, WHY they are doing this. Read what exists first; ask only what is missing. After the dump, a simple "anything else?" often surfaces what they almost forgot. Before drafting, read the situation across four dimensions — they determine the PRD's shape: -- **Stakes.** Hobby project, internal utility feature, team initiative, enterprise program, regulated submission, public launch. Calibrates rigor, section depth, and which adapt-in clusters apply. -- **Audience.** Just the user, their team, cross-functional stakeholders, executive committee, regulators, external customers. Drives tone, evidence requirements, and approval sections. -- **Existing inputs.** A brief? Research? UX work already done with user journeys? Architectural constraints from an infra team? Existing artifacts mean those parts of the PRD reference, not relitigate. If UX exists and describes journeys, the PRD points at it; it does not duplicate. When project-context, prior PRDs, or existing UX/architecture are present, this is brownfield territory — frame Discovery around what is new or changing, not around what the product already is. -- **Downstream depth.** Is this PRD the whole spec for a small build, or the top of a chain through UX → architecture → epics → stories? The chain length affects how much the PRD encodes vs. what it defers to downstream skills. +- **Stakes.** Calibrates rigor, section depth, and which adapt-in clusters apply. +- **Audience.** Drives tone, evidence requirements, and approval sections. +- **Existing inputs.** Existing artifacts mean those parts of the PRD reference, not relitigate. When project-context, prior PRDs, or existing UX/architecture are present, this is brownfield — frame Discovery around what is new or changing. +- **Downstream depth.** Whole spec for a small build, or top of a chain through UX → architecture → epics → stories? Affects how much the PRD encodes vs. defers. -**Right-skill check.** Once the situation is read, honestly sanity-check that PRD is the best tool for what the user actually needs. Three cases where it isn't: +**Right-skill check.** Once the situation is read, sanity-check that PRD is the best tool. Three cases where it isn't: -- **Games** (mechanic-driven, player-experience-focused) → suggest the `bmad-gds` (game-dev-studio) module for Game Design Document (GDD) and game planning. PRD shape is wrong for games. -- **Small scope + user wants a captured artifact** (1-2 stories of change to an existing codebase, a small feature, a focused tweak — and the user wants a single doc to point at) → stay here and produce an *all-inclusive document*: lean §1-§6 plus inline Stories via the adapt-in Stories cluster. One or two pages, single source of truth, replaces a separate story-workflow run for tiny work. -- **Express implementation** (small scope or clear idea, user just wants to build now — no planning chain or captured artifact needed) → suggest `bmad-quick-dev` — takes idea or intent input and implements right away. True express track, no PRD/UX/architecture stages. +- **Games** → suggest `bmad-gds` for the Game Design Document. +- **Small scope + wants a captured artifact** (small tweak to an existing codebase, single doc to point at) → stay here and produce an *all-inclusive document*: lean spine plus inline Stories via the adapt-in Stories cluster. +- **Express implementation** (wants to build now, no planning chain or captured artifact needed) → suggest `bmad-quick-dev`. -Surface these honestly and let the user choose. If they prefer this skill anyway (for posterity, clarity, single-source-of-truth artifact), proceed with the right-sized version. +Surface these honestly and let the user choose; if they prefer this skill anyway, proceed with the right-sized version. -Coach, do not quiz. Push hardest where assumptions are unexamined, where capabilities are conflated with implementation, where terms drift across the PRD, where scope creeps without a Non-Goal to push back, where a downstream reader would have to guess. Drill into specifics only after the broad shape is on the table; premature granular questions interrupt the dump and miss the room. Suggest research (web, competitive, market, domain compliance) only when the stakes warrant it. +Coach, do not quiz. Push hardest where PRD Discipline is at risk — unexamined assumptions, capability-vs-implementation confusion, term drift, silent scope creep, ambiguity for downstream readers. Drill into specifics only after the broad shape is on the table; premature granular questions interrupt the dump. Suggest research only when the stakes warrant it. ## PRD Discipline -Patterns that hold the PRD together across every shape — hobby to enterprise. +Patterns that hold the PRD together across every shape. -- **Information density.** Every sentence carries weight. Cut filler. Direct over hedged. Density scales with stakes — a hobby PRD can breathe more; an enterprise PRD must be tight. -- **Glossary-anchored vocabulary.** Every domain noun is defined once and used identically thereafter. Synonyms produce drift — downstream workflows, agents, and future-you all suffer when the same thing is called by two names. +- **Information density.** Every sentence carries weight; density scales with stakes. +- **Glossary-anchored vocabulary.** Every domain noun is defined once and used identically thereafter. Synonyms produce drift. - **Self-contained sections.** Any section should make sense pulled out alone. Cross-references go through Glossary terms, not "see above" prose. -- **Features grouped, FRs nested.** §5 organizes by feature, not by a flat FR list. Each feature has a behavioral description, then its Functional Requirements listed under it (numbered globally so downstream artifacts have stable IDs), then any feature-specific NFRs and notes. Cross-cutting NFRs live in their own section. -- **Capabilities, not implementation.** FRs describe what users (or systems) can do, not how. No technology names, library choices, or architecture decisions. "Users can reset their password via email link" — yes. "System sends JWT via SendGrid and validates with Postgres" — no. -- **No innovation theater.** Do not fabricate differentiation or novelty language where the product is a competent execution of existing patterns. "This is a well-known shape done well" is honest and better than invented novelty. Only add an Innovation or Differentiation section when Discovery surfaced genuinely novel aspects worth naming. -- **Personas, when used, are research-grounded or marked illustrative.** Named, vivid personas force concrete thinking (especially for consumer products) but invented detail is *persona theater* — false specificity that the team then builds for. Tag composite personas `[ILLUSTRATIVE — composite from segment data, not specific research]` until grounded. Personas must drive decisions ("we built X because Maya needs Y"); if you can swap names and nothing changes, the persona is not doing work. Two to four personas max. -- **Measurable where it sharpens the requirement.** Replace subjective adjectives (fast, easy, scalable, intuitive) with measurable criteria where the measurement matters. No SMART scoring ceremony, no per-FR 1-5 ratings — judgment, not ritual. -- **Traceability where the chain matters.** When an FR exists *because of* a specific success criterion or a specific user journey, name the link inline. Skip full traceability matrices. -- **Domain awareness.** When the domain is regulated, compliance requirements appear in the PRD — not deferred to architecture. Healthcare → HIPAA. Fintech → PCI-DSS, AML/KYC, SOX. Govtech → NIST, Section 508 / WCAG 2.1 AA, FedRAMP. E-commerce → PCI-DSS for payments. Detect at Discovery, enforce at Finalize. -- **Project-type awareness.** Different products need different sections. API/service → API contracts, versioning, performance budgets. Mobile → device permissions, offline behavior, OS targets. Web → accessibility, browser support. Embedded → hardware constraints, deployment topology. Library → public surface, dependency policy. The template's adapt-in menu lists these clusters. -- **Non-Goals explicit.** A short Non-Goals (Explicit) section does outsized work for downstream readers and workflows. It prevents the "let me also add this nearby thing" failure mode. Inline `[NON-GOAL for MVP]` and `[v2 — out of MVP]` callouts where they would otherwise be silently assumed. -- **Never silently de-scope.** Requirements the user explicitly included in input documents (brief, research, prior PRD) do not drop out of scope without an explicit ask. When deferral seems warranted, surface it: *"I'd recommend deferring X because [reason]. Keep it in, or move it out?"* Same gate for phasing the user did not request — propose, do not impose. +- **Features grouped, FRs nested.** Each feature opens with a behavioral description, then nests its Functional Requirements (numbered globally for stable downstream IDs), then optional feature-specific NFRs. Cross-cutting NFRs live in their own section. +- **Capabilities, not implementation.** FRs describe what users (or systems) can do, not how — no technology names, library choices, or architecture decisions. +- **No innovation theater.** Do not fabricate differentiation or novelty where the product is a competent execution of existing patterns. Add an Innovation or Differentiation section only when Discovery surfaced something genuinely novel. +- **Personas, when used, are research-grounded or marked `[ILLUSTRATIVE]`.** Invented detail is *persona theater* — false specificity the team then builds for. Personas must drive decisions; if you can swap names and nothing changes, the persona is not doing work. Two to four max. +- **Measurable where it sharpens the requirement.** Replace subjective adjectives with measurable criteria where the measurement matters — judgment, not ritual. +- **Traceability where the chain matters.** When an FR exists *because of* a specific success criterion or user journey, name the link inline. Skip full traceability matrices. +- **Domain awareness.** When the domain has regulatory or compliance constraints, surface them in the PRD — not deferred to architecture. Detect at Discovery, enforce at Finalize. +- **Project-type awareness.** Match section depth and adapt-in clusters to the project type — the template's adapt-in menu names the standard clusters. +- **Non-Goals explicit.** A Non-Goals (Explicit) section does outsized work for downstream readers; pair it with inline `[NON-GOAL for MVP]` and `[v2 — out of MVP]` callouts where omissions would otherwise be silently assumed. +- **Never silently de-scope.** Requirements the user explicitly included do not drop without an explicit ask. Same gate for phasing the user did not request — propose, do not impose. - **Counter-metrics named.** Metrics not to optimize are as load-bearing as the ones to optimize. Name them when Success Metrics is in the PRD. -- **Assumptions visible.** Inferences made without direct user confirmation are tagged `[ASSUMPTION: ...]` inline and indexed at the end so downstream readers can flag them rather than absorbing them as fact. -- **`[NOTE FOR PM]` callouts** at decision points the user deferred or left tension on — surface them so the next session or the next reviewer can revisit. -- **Right-size to purpose.** A hobby project does not need investor-grade rigor. A regulated submission does. Length, depth, and which adapt-in clusters apply all scale with stakes. +- **Assumptions visible.** Inferences without direct user confirmation are tagged `[ASSUMPTION: ...]` inline and indexed at the end. +- **`[NOTE FOR PM]` callouts** at decision points the user deferred or left tension on. +- **Right-size to purpose.** Length, depth, and adapt-in clusters scale with stakes. ## Constraints - **Persistence is real-time.** Once Create intent is confirmed, the workspace (run folder, `prd.md` skeleton, `decision-log.md`) exists on disk and the user knows the path. -- **File roles.** `decision-log.md` is canonical memory and audit trail — every decision, change, override, and version/state transition recorded as the conversation unfolds. `addendum.md` preserves user-contributed depth that belongs downstream (architecture, UX, solution design) or earned a place but does not fit the PRD shape (rejected alternatives, options matrices, sizing data, deep technical constraints, competitive analysis beyond a one-line landscape pointer, operational/cost mechanics like rate-limiting strategies and compression schemes). When the user volunteers technical-how detail, capture it to the addendum in real time — *"I'll capture that in addendum.md so the architecture pass picks it up."* Audit and override information never goes there. +- **File roles.** `decision-log.md` is canonical memory and audit trail — every decision, change, override, and version/state transition recorded as the conversation unfolds. `addendum.md` preserves user-contributed depth that belongs downstream or earned a place but does not fit the PRD shape (rejected alternatives, options matrices, deep technical detail, ops/cost mechanics, deep competitive analysis). When the user volunteers technical-how detail, capture it to addendum in real time. Audit and override information never goes there. - **Continuity across sessions.** If a prior in-progress draft for this project exists in `{workflow.output_dir}`, the user is offered to resume. On resume, surface the open items (Open Questions, `[ASSUMPTION]` tags, `[NOTE FOR PM]` callouts) first — they orient both the user and the agent to what was deferred. -- **Extract, don't ingest.** Source artifacts (brief, distillate, research, UX work, transcripts, prior PRD, validation report, project-context, web results) never enter the parent conversation wholesale. Whenever a source document is consulted — Discovery setup, Update/Validate orientation, Finalize input reconciliation — delegate to a source-extractor subagent that returns `{summary, key_decisions, open_items, conflicts_with_focus, citations: [{section, line_range, quote}]}` against the user's stated focus. Run one subagent per document in parallel when there are multiple. The parent assembles from extracts; it never re-opens the source. -- **Downstream workflows run in fresh context.** Architecture decisions, UX journey design, epic breakdowns, and ticket bodies are not produced here. The PRD's job ends with a polished `prd.md` (and optional `addendum.md`). Each downstream workflow (UX, architecture, story creation, etc.) runs in a new session and source-extracts the slice it needs from `prd.md` directly — this skill never invokes them and produces no separate handoff artifact. -- **Adapt the shape; do not impose a template.** The template asset is a menu. Hobby PRDs are short. Enterprise PRDs include stakeholder, risk, compliance, ROI, and operational sections. Use Discovery to read the situation and shape accordingly. +- **Extract, don't ingest.** Source artifacts never enter the parent conversation wholesale. Whenever a source document is consulted — Discovery setup, Update/Validate orientation, Finalize input reconciliation — delegate to a source-extractor subagent that returns `{summary, key_decisions, open_items, conflicts_with_focus, citations: [{section, line_range, quote}]}` against the user's stated focus. Run one subagent per document in parallel when there are multiple. The parent assembles from extracts; it never re-opens the source. +- **Downstream workflows run in fresh context.** The PRD's job ends with a polished `prd.md` (and optional `addendum.md`). Each downstream workflow runs in a new session and source-extracts the slice it needs from `prd.md` directly — this skill never invokes them and produces no separate handoff artifact. +- **Adapt the shape; do not impose a template.** The template asset is a menu — use Discovery to read the situation and shape accordingly. ## Finalize 1. Decision log audit + addendum review: walk `decision-log.md` with the user and account for each meaningful entry — captured in the PRD, captured in `addendum.md` (see `## Constraints` for what belongs there), or set aside as process noise. -2. Input reconciliation: fan out one source-extractor subagent per input the user supplied (brief, research, brainstorming, prior PRD, project-context), each handed the current `prd.md` + `addendum.md` to compare against, each returning `{coverage, gaps, soft_ideas_not_landed}`. Aggregate and present the union — especially soft or qualitative ideas (tone, philosophy, interaction feel, brand voice) that the feature-and-FR shape silently drops. Ask whether any should be incorporated before polish. This must happen before the polish pass — once polish runs, additions become edits. -3. Discipline pass: re-read `prd.md` against `## PRD Discipline`. Verify Glossary terms are used identically throughout; features are grouped with their FRs nested; FRs are capabilities not implementation; every inline `[ASSUMPTION]` appears in the Assumptions Index; Non-Goals are explicit; counter-metrics are named when metrics exist; domain and project-type sections are present and right-sized; no innovation theater snuck in; any personas are research-grounded or marked `[ILLUSTRATIVE]`. -4. Open-items review: count Open Questions, `[ASSUMPTION]` tags, and `[NOTE FOR PM]` callouts. Summarize the totals to the user (*"This PRD ships with 14 Open Questions, 28 assumptions, and 3 PM notes"*). Walk them by category. For each, ask: resolve now, accept-and-ship (with a one-line rationale logged to `decision-log.md`), or escalate to a stakeholder. Treat high density relative to the agreed stakes as a red flag — a hobby PRD with five open items is fine; an enterprise initiative or regulated submission with twenty unresolved is not, and downstream UX/architecture/story work built on that count propagates ambiguity into stories. Surface the flag explicitly before continuing. +2. Input reconciliation: fan out one source-extractor subagent per user-supplied input, each handed the current `prd.md` + `addendum.md` to compare against, returning `{coverage, gaps, soft_ideas_not_landed}`. Aggregate and present — especially soft or qualitative ideas (tone, voice, feel) the feature-and-FR shape silently drops. Ask whether any should be incorporated. Must happen before polish. +3. Discipline pass: spawn the validator subagent against `prd.md` with `{workflow.validation_checklist}`; produce findings + HTML report per `references/validation-render.md`. Surface failures and warnings conversationally; resolve before polish. +4. Open-items review: count Open Questions, `[ASSUMPTION]` tags, and `[NOTE FOR PM]` callouts. Summarize totals to the user and walk them by category. For each: resolve now, accept-and-ship (log rationale to `decision-log.md`), or escalate. Flag high density relative to the agreed stakes as a red flag before continuing. 5. Polish: apply each entry in `{workflow.doc_standards}` (a `skill:`, `file:`, or plain-text directive) to `prd.md` (and `addendum.md` if it exists). Run passes as parallel subagents — apply all doc standards to `prd.md` first, then to `addendum.md`. -6. External handoffs: execute each entry in `{workflow.external_handoffs}` to route artifacts beyond local files (Confluence, Notion, ticket systems, etc.) — each directive names the MCP tool and the fields it needs. Invoke the tool, capture any URLs or IDs returned, and surface them in the user message. If a named tool is unavailable, skip that handoff and flag it (graceful degradation); local files always exist regardless. -7. Record finalization to `decision-log.md` (e.g., `## Finalized — {date}` with the agreed version label, any open items accepted at ship, and the locations of external destinations). Tell the user it is ready. Invoke the `bmad-help` skill to surface next steps in the BMad ecosystem based on what is set up and available; share the paths to the PRD, addendum, decision log, external destinations (URLs returned from handoffs), and any validation report. Remind the user that downstream workflows (UX, architecture, story creation) run in fresh sessions and source-extract from `prd.md` directly — there is no separate handoff artifact. +6. External handoffs: execute each entry in `{workflow.external_handoffs}` — each directive names the MCP tool and the fields it needs. Invoke, capture any URLs or IDs returned, surface them. If a named tool is unavailable, skip and flag (graceful degradation); local files always exist. +7. Record finalization to `decision-log.md` (version label, accepted-at-ship open items, external destinations). Tell the user it is ready, share artifact paths (PRD, addendum, decision log, external destinations, validation report). Invoke the `bmad-help` skill to surface BMad-ecosystem next steps. 8. Run `{workflow.on_complete}` if non-empty. Treat a string scalar as a single instruction and an array as a sequence of instructions executed in order. diff --git a/src/bmm-skills/2-plan-workflows/bmad-prd/assets/prd-validation-checklist.md b/src/bmm-skills/2-plan-workflows/bmad-prd/assets/prd-validation-checklist.md new file mode 100644 index 000000000..a6b3cbef9 --- /dev/null +++ b/src/bmm-skills/2-plan-workflows/bmad-prd/assets/prd-validation-checklist.md @@ -0,0 +1,30 @@ +# PRD Validation Checklist + +Loaded by the PRD validator subagent. For each item, return `{id, status: pass|fail|warn|n/a, severity: low|medium|high|critical, location, note}`. Skip items not applicable to the agreed stakes. Cite specific PRD locations — never abstract criticism. + +## Quality + +- **Q-1. Information density.** Sentences carry weight. Flag filler, hedging, and conversational padding. +- **Q-2. Measurability.** Where measurement matters, FRs and Success Metrics are measurable; subjective adjectives flagged. Counter-metrics named when Success Metrics exist. +- **Q-3. Traceability.** Where the chain matters, FRs name their link to a user journey or success criterion inline. +- **Q-4. Vision and JTBDs concrete.** Vision is specific and stands alone — not a generic feature list. JTBDs are audience-grounded, not abstract. +- **Q-5. Non-Goals explicit.** A Non-Goals section is present where it would do real work; inline `[NON-GOAL]` and `[v2]` callouts where omissions would otherwise be silently assumed. +- **Q-6. Dual-audience and self-contained.** Each section makes sense pulled out alone (cross-references via Glossary terms, not "see above"); the PRD is readable by humans and structured cleanly for downstream source-extraction by UX, architecture, and story-creation workflows. + +## Discipline + +- **D-1. Capabilities, not implementation.** FRs describe what users/systems can do, not how. Flag technology names, library choices, architecture decisions. +- **D-2. Input fidelity.** Requirements from input documents (brief, research, prior PRD) are still in scope or explicitly handled via Non-Goals or `[ASSUMPTION]`. +- **D-3. Personas grounded.** If personas exist, they are research-grounded or marked `[ILLUSTRATIVE]`. Each persona drives at least one decision. +- **D-4. No innovation theater.** Novelty claims are real, not invented. + +## Structural integrity + +- **S-1. Glossary integrity.** Every domain noun is defined in the Glossary and used identically throughout. Flag drift (case, plural, synonyms) and candidate missing-term entries. +- **S-2. ID continuity.** FR / UJ / Story IDs are contiguous, unique, and cross-references resolve. +- **S-3. Assumptions Index.** Every inline `[ASSUMPTION: ...]` appears in the Assumptions Index and vice versa. +- **S-4. Open-items density.** Count Open Questions + `[ASSUMPTION]` + `[NOTE FOR PM]`. Red flag if density is high relative to the agreed stakes. + +## Stakes-gated + +- **STK-1. Required sections.** The PRD includes the sections the agreed stakes and product type warrant. diff --git a/src/bmm-skills/2-plan-workflows/bmad-prd/assets/validation-report-template.html b/src/bmm-skills/2-plan-workflows/bmad-prd/assets/validation-report-template.html new file mode 100644 index 000000000..1e3136607 --- /dev/null +++ b/src/bmm-skills/2-plan-workflows/bmad-prd/assets/validation-report-template.html @@ -0,0 +1,190 @@ + + + + +PRD Validation: $prd_name + + + +
+
+
+

$prd_name — Validation Report

+
$prd_path
+
+
$grade
+
+ +
$overall_synthesis
+ +
+
$score_svg
+
+ $passed pass + $warned warn + $failed fail + $na n/a + $total items checked +
+
+ + $categories_html + + +
+ + diff --git a/src/bmm-skills/2-plan-workflows/bmad-prd/customize.toml b/src/bmm-skills/2-plan-workflows/bmad-prd/customize.toml index e42b3fa32..534bf7de4 100644 --- a/src/bmm-skills/2-plan-workflows/bmad-prd/customize.toml +++ b/src/bmm-skills/2-plan-workflows/bmad-prd/customize.toml @@ -43,6 +43,19 @@ on_complete = "" # to enforce a different structure (e.g. regulated-industry, internal-tool, investor-input). prd_template = "assets/prd-template.md" +# Validation checklist used at the Validate intent and at Finalize step 3. +# A subagent walks the checklist against prd.md and returns structured findings. +# Override the path in team/user TOML to enforce an org-specific checklist +# (regulated-industry compliance, investor-pitch standards, etc.). +validation_checklist = "assets/prd-validation-checklist.md" + +# HTML template used to render validation findings into a styled, scannable +# report. The renderer (scripts/render-validation-html.py) substitutes +# structured findings + summary stats into this template; the template is +# fully overridable to match org branding. The default uses inline CSS, no +# external dependencies, and native HTML
for collapse — no JS. +validation_report_template = "assets/validation-report-template.html" + # Run folder location. The PRD, optional addendum, decision log, and optional # validation report all land inside `{output_dir}/{output_folder_name}/`. output_dir = "{planning_artifacts}/prds" diff --git a/src/bmm-skills/2-plan-workflows/bmad-prd/references/headless.md b/src/bmm-skills/2-plan-workflows/bmad-prd/references/headless.md new file mode 100644 index 000000000..761730efc --- /dev/null +++ b/src/bmm-skills/2-plan-workflows/bmad-prd/references/headless.md @@ -0,0 +1,24 @@ +# Headless Mode + +Load this file when bmad-prd is invoked headless (no interactive user). Follow it for the whole run. + +## General + +Do not ask. Complete the intent using what is provided, what exists in `{doc_workspace}`, or what you can discover yourself. If intent remains ambiguous after inference, halt with a `blocked` JSON status and a `reason` field — do not prompt. Do not greet. + +End with a JSON response listing status, intent, and artifact paths. The `intent` field must match the detected intent: `"create"`, `"update"`, or `"validate"`. Omit keys for artifacts not produced. Full schemas with examples for each intent are in `assets/headless-schemas.md`. Minimal shape: + +```json +{ + "status": "complete", + "intent": "validate", + "validation_report": "{doc_workspace}/validation-report.md", + "offer_to_update": true +} +``` + +## Mode-specific overrides + +**Update.** Log the reversal to `decision-log.md`, then apply. Halt `blocked` if intent is ambiguous. + +**Validate.** Always write `validation-report.md` to `{doc_workspace}` regardless of finding count. Always include `"offer_to_update": true` in the JSON status block. diff --git a/src/bmm-skills/2-plan-workflows/bmad-prd/references/validation-render.md b/src/bmm-skills/2-plan-workflows/bmad-prd/references/validation-render.md new file mode 100644 index 000000000..d4c7c9bb6 --- /dev/null +++ b/src/bmm-skills/2-plan-workflows/bmad-prd/references/validation-render.md @@ -0,0 +1,60 @@ +# Validation Rendering + +How the validator subagent's findings become the HTML report. Loaded by the parent on any Validate or Finalize-step-3 invocation. + +## Validator subagent output contract + +The subagent walks `{workflow.validation_checklist}` against `prd.md` (and `addendum.md` if present) and writes `{doc_workspace}/validation-findings.json`: + +```json +{ + "prd_name": "Plantsona", + "prd_path": "{doc_workspace}/prd.md", + "checklist_path": "{workflow.validation_checklist}", + "timestamp": "2026-05-11T09:14:00", + "overall_synthesis": "2-3 sentences of judgment about the PRD's overall state — what holds up, what's at risk. Written by the subagent, not the parent.", + "findings": [ + { + "id": "Q-2", + "category": "Quality", + "title": "Measurability", + "status": "warn", + "severity": "medium", + "location": "§16 Success Metrics, lines 408-422", + "note": "Success Metrics list is measurable but counter-metrics are named only for premium conversion. Other metrics lack paired counter-metrics.", + "suggested_fix": "Add counter-metrics for engagement (e.g., DAU/MAU) and seasonal cadence." + } + ] +} +``` + +Per-finding fields: + +- `id` (required) — checklist item ID (e.g., `Q-1`, `D-2`, `STK-1`, or org-custom prefixes). +- `category` (optional) — explicit category name; if omitted, the renderer maps from the ID prefix. +- `title` (optional but recommended) — the checklist item's short name. +- `status` — `pass` | `warn` | `fail` | `n/a`. +- `severity` — `low` | `medium` | `high` | `critical`. +- `location` (optional) — section/line/range in the PRD where the finding lives. Cite specifics, never abstract criticism. +- `note` (optional) — the finding itself, in one or two sentences. +- `suggested_fix` (optional) — concrete next action. + +## Rendering invocation + +After the subagent writes findings: + +```bash +python3 {skill-root}/scripts/render-validation-html.py \ + --findings {doc_workspace}/validation-findings.json \ + --template {workflow.validation_report_template} \ + --output {doc_workspace}/validation-report.html \ + --open +``` + +Include `--open` for interactive runs (auto-opens in default browser). Omit `--open` in headless runs. + +The script computes pass/warn/fail/na counts, derives a grade (Excellent / Good / Fair / Poor) from critical-fail and total-fail counts, renders an inline SVG score bar, groups findings by category, and substitutes into the template. Returns a one-line JSON summary on stdout: `{"output": "...", "grade": "...", "stats": {...}}`. + +## Markdown companion + +When findings include any Critical-severity item or >5 total fail/warn items, also write `{doc_workspace}/validation-report.md` — a markdown rendering of the same findings, grouped by severity, with PRD line references. Update mode consumes the markdown form cleanly when rolling findings into a revision. diff --git a/src/bmm-skills/2-plan-workflows/bmad-prd/scripts/render-validation-html.py b/src/bmm-skills/2-plan-workflows/bmad-prd/scripts/render-validation-html.py new file mode 100644 index 000000000..e65c97ab9 --- /dev/null +++ b/src/bmm-skills/2-plan-workflows/bmad-prd/scripts/render-validation-html.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python3 +# /// script +# requires-python = ">=3.10" +# /// +"""Render a PRD validation findings JSON into a styled HTML report. + +Reads structured findings produced by the validator subagent, groups them by +category (explicit `category` field, else derived from ID prefix), computes a +pass/warn/fail summary and grade, substitutes into the configured HTML +template, and optionally opens the result in the default browser. +""" + +import argparse +import html +import json +import string +import sys +import webbrowser +from datetime import datetime +from pathlib import Path + +CATEGORY_FROM_PREFIX = { + "Q": "Quality", + "D": "Discipline", + "S": "Structural integrity", + "STK": "Stakes-gated", + "M": "Mechanical", +} + +CATEGORY_ORDER = ["Quality", "Discipline", "Structural integrity", "Stakes-gated", "Mechanical"] + + +def category_for(finding: dict) -> str: + explicit = finding.get("category") + if explicit: + return explicit + fid = finding.get("id", "") + prefix = fid.split("-", 1)[0] if "-" in fid else fid + return CATEGORY_FROM_PREFIX.get(prefix, prefix or "Other") + + +def compute_stats(findings: list[dict]) -> dict: + total = len(findings) + by_status = {"pass": 0, "warn": 0, "fail": 0, "n/a": 0} + failed_critical = 0 + failed_high = 0 + for f in findings: + status = f.get("status", "n/a") + if status in by_status: + by_status[status] += 1 + if status == "fail": + sev = (f.get("severity") or "low").lower() + if sev == "critical": + failed_critical += 1 + elif sev == "high": + failed_high += 1 + return { + "total": total, + "passed": by_status["pass"], + "warned": by_status["warn"], + "failed": by_status["fail"], + "na": by_status["n/a"], + "failed_critical": failed_critical, + "failed_high": failed_high, + } + + +def grade_from(stats: dict) -> tuple[str, str]: + if stats["failed_critical"] > 0: + return "Poor", "grade-poor" + if stats["failed_high"] >= 1 or stats["failed"] >= 4: + return "Fair", "grade-fair" + if stats["failed"] > 0 or stats["warned"] > 2: + return "Good", "grade-good" + return "Excellent", "grade-excellent" + + +def render_score_bar(stats: dict, width: int = 480, height: int = 22) -> str: + total = max(stats["total"], 1) + p = stats["passed"] / total * width + w = stats["warned"] / total * width + f = stats["failed"] / total * width + n = stats["na"] / total * width + return ( + f'' + f'' + f'' + f'' + f'' + f"" + ) + + +def render_finding(f: dict) -> str: + status = (f.get("status") or "n/a").lower() + severity = (f.get("severity") or "low").lower() + fid = html.escape(f.get("id") or "") + title = html.escape(f.get("title") or fid) + location = html.escape(f.get("location") or "") + note = html.escape(f.get("note") or "") + fix = html.escape(f.get("suggested_fix") or "") + + status_class = "na" if status == "n/a" else status + parts = [ + f'
', + '
', + f'{status.upper()}', + f'{severity}', + f'{fid}', + f'

{title}

', + '
', + ] + if location: + parts.append(f'
Location: {location}
') + if note: + parts.append(f'
{note}
') + if fix: + parts.append(f'
Suggested fix: {fix}
') + parts.append("
") + return "\n".join(parts) + + +def render_category(name: str, findings: list[dict]) -> str: + items = "\n".join(render_finding(f) for f in findings) + name_e = html.escape(name) + return ( + f'
' + f"
" + f'

{name_e} ({len(findings)})

' + f"{items}" + f"
" + f"
" + ) + + +def main(argv: list[str]) -> int: + parser = argparse.ArgumentParser(description="Render PRD validation findings to HTML.") + parser.add_argument("--findings", required=True, help="Path to validation-findings.json") + parser.add_argument("--template", required=True, help="Path to HTML template") + parser.add_argument("--output", required=True, help="Path to write the rendered HTML") + parser.add_argument("--open", action="store_true", help="Open the rendered HTML in the default browser") + args = parser.parse_args(argv) + + findings_path = Path(args.findings) + template_path = Path(args.template) + output_path = Path(args.output) + + data = json.loads(findings_path.read_text()) + template = template_path.read_text() + + findings = data.get("findings", []) or [] + + by_cat: dict[str, list[dict]] = {} + for f in findings: + by_cat.setdefault(category_for(f), []).append(f) + + sorted_cats = sorted( + by_cat.keys(), + key=lambda c: (CATEGORY_ORDER.index(c) if c in CATEGORY_ORDER else 99, c), + ) + categories_html = "\n".join(render_category(c, by_cat[c]) for c in sorted_cats) + + stats = compute_stats(findings) + grade, grade_class = grade_from(stats) + score_svg = render_score_bar(stats) + + timestamp = data.get("timestamp") or datetime.now().isoformat(timespec="seconds") + substitutions = { + "prd_name": html.escape(str(data.get("prd_name") or "PRD")), + "prd_path": html.escape(str(data.get("prd_path") or "")), + "checklist_path": html.escape(str(data.get("checklist_path") or "")), + "timestamp": html.escape(timestamp), + "overall_synthesis": html.escape(str(data.get("overall_synthesis") or "")), + "grade": grade, + "grade_class": grade_class, + "total": str(stats["total"]), + "passed": str(stats["passed"]), + "failed": str(stats["failed"]), + "warned": str(stats["warned"]), + "na": str(stats["na"]), + "score_svg": score_svg, + "categories_html": categories_html, + } + + rendered = string.Template(template).safe_substitute(substitutions) + output_path.write_text(rendered) + + print(json.dumps({"output": str(output_path), "grade": grade, "stats": stats})) + + if args.open: + webbrowser.open(output_path.resolve().as_uri()) + + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:]))