From b8741d55d8b032410ba4d8752615e34735be4729 Mon Sep 17 00:00:00 2001 From: Jennifer Power Date: Wed, 12 Aug 2026 19:07:39 -0400 Subject: [PATCH 1/3] feat: design prd-review into 5 dispatchable agents with JSON verdict protocol Design of prd-review from a single-context 6-lens inline checklist model into 5 standalone dispatchable review agents (Guard, Adversary, Tester, Operator, Curator) with a shared JSON verdict protocol. Adds Phase 0 schema conformance gate with registry-first/--schema fallback dispatch, parallel/serial execution modes, and schema extensions (KPIs at parent level, dependencies at phase level). Includes CUE module scaffolding, CI publish workflow, and example PRDs demonstrating parent and phase-level schema conformance. Assisted-by: Claude (Anthropic, Claude Sonnet 5) Signed-off-by: Jennifer Power --- .github/dependabot.yml | 6 + .github/workflows/publish-cue.yml | 27 +++ .gitignore | 15 ++ AGENTS.md | 62 +++++ README.md | 95 +++++++- cue.mod/module.cue | 7 + examples/parent-prd.yaml | 44 ++++ examples/phase-prd.yaml | 40 ++++ module/AGENTS.md | 28 +++ module/agents/prd-adversary.md | 122 ++++++++++ module/agents/prd-curator.md | 129 +++++++++++ module/agents/prd-guard.md | 176 ++++++++++++++ module/agents/prd-operator.md | 111 +++++++++ module/agents/prd-tester.md | 153 ++++++++++++ module/commands/prd-review.md | 11 + module/skills/prd-review/SKILL.md | 217 ++++++++++++++++++ .../references/reviewer-protocol.md | 114 +++++++++ schema/prd.cue | 109 +++++++++ 18 files changed, 1464 insertions(+), 2 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/publish-cue.yml create mode 100644 .gitignore create mode 100644 AGENTS.md create mode 100644 cue.mod/module.cue create mode 100644 examples/parent-prd.yaml create mode 100644 examples/phase-prd.yaml create mode 100644 module/AGENTS.md create mode 100644 module/agents/prd-adversary.md create mode 100644 module/agents/prd-curator.md create mode 100644 module/agents/prd-guard.md create mode 100644 module/agents/prd-operator.md create mode 100644 module/agents/prd-tester.md create mode 100644 module/commands/prd-review.md create mode 100644 module/skills/prd-review/SKILL.md create mode 100644 module/skills/prd-review/references/reviewer-protocol.md create mode 100644 schema/prd.cue diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..ca79ca5 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly diff --git a/.github/workflows/publish-cue.yml b/.github/workflows/publish-cue.yml new file mode 100644 index 0000000..00c2611 --- /dev/null +++ b/.github/workflows/publish-cue.yml @@ -0,0 +1,27 @@ +name: "Publish to CUE Registry" +on: + push: + tags: + - "v*" + +jobs: + publish: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + fetch-depth: 0 + persist-credentials: false + - name: Setup CUE + uses: cue-lang/setup-cue@a93fa358375740cd8b0078f76355512b9208acb1 # v1.0.1 + with: + version: "v0.17.0" + - name: Login to CUE Central Registry + run: cue login --token=${{ secrets.CUE_REG_TOKEN }} + - name: Publish module + env: + REF_NAME: ${{ github.ref_name }} + run: cue mod publish "$REF_NAME" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8d29bba --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +.idea/ +.DS_Store +Thumbs.db +*.swp +*.swo +*~ +.task/ +node_modules/ +.lola/ +*.log +.claude/ +.superpowers/ + +# Working artifacts (never committed) +docs/superpowers/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..99de289 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,62 @@ +# Intake Kit — Developer Guide + +This repo is the source for the `intake-kit` Lola module. +The installable module lives entirely under `./module/`. + +## Project layout + +``` +module/ ← installable module (what users get) + AGENTS.md ← injected into user's AGENTS.md by lola + agents/ ← standalone review agent definitions + skills/prd-review/ + SKILL.md ← orchestration skill + references/ ← reviewer protocol and shared refs + commands/prd-review.md ← command entry point +schema/ ← CUE schema (published to CUE registry) +cue.mod/ ← CUE module definition +examples/ ← example PRD YAML files +.github/workflows/ ← CI (CUE registry publish) +``` + +## Golden rule + +**All changes to module content go in `./module/`, never in the installed location.** + +The installed copy (typically `~/.config/opencode/skills/prd-review/` +or `.claude/skills/prd-review/`) is a deployment artifact. If you find +yourself editing files outside `./module/`, stop — you're modifying a +copy that will be overwritten on next install. + +## Working on review agents + +Agent definitions live at `module/agents/prd-*.md`. Each is a standalone +reviewer that runs in parallel during Phase 2 of the review council. +Follow the existing agent files as a template when adding new ones. + +## Working on the schema + +The CUE schema at `schema/prd.cue` is published to the CUE Central +Registry via the `publish-cue.yml` workflow on tag push. The module +definition at `cue.mod/module.cue` controls the module path and +language version. + +## Tool Agnosticism + +This module MUST remain target-tool agnostic. It must work identically +whether the hosting tool is Claude Code, OpenCode, Cursor, Windsurf, +Gemini CLI, or any future AI coding assistant. + +Rules: + +1. **No tool-specific frontmatter.** Skill and agent files use only + keys every host understands. +2. **No tool-specific dispatch syntax.** Orchestrator docs describe + dispatch intent with fallback instructions for hosts that lack + named-agent dispatch. +3. **No tool names in operational text.** References to specific tools + are permitted only in docs — never in instructions or agent + definitions that affect runtime behavior. +4. **Graceful degradation over hard requirements.** Features that + depend on host capabilities must degrade gracefully when the host + lacks them, not fail. diff --git a/README.md b/README.md index a04f969..a376153 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,93 @@ -# intake-kit -A CUE-validated PRD authoring and review system for structured project intake +# Intake Kit + +A structured product requirements intake toolkit. CUE-validated PRD authoring paired with a multi-agent AI review +council that gates PRD advancement from Draft to Review. + +Installs as a [Lola](https://github.com/LobsterTrap/lola) module and works with Claude Code, Cursor, Gemini CLI, and +OpenCode. + +## What's in the box + +- **CUE schema** (`schema/prd.cue`) — validates PRD YAML documents. Enforces structure for stakeholders, functional + requirements, acceptance criteria, workflows, and lifecycle state. +- **prd-review skill** (`module/skills/prd-review/`) — a Lola skill that checks CUE schema conformance, then dispatches + 5 specialist review agents (`module/agents/prd-*.md`) against a PRD family and emits a BLOCKED / NEEDS REVISION / + APPROVED verdict. +- **Example PRDs** (`examples/`) — parent and phase templates ready to copy and fill in. + +## Install + +```bash +lola install github.com/unbound-force/intake-kit +``` + +## PRD Document Model + +PRDs are structured YAML. A **parent PRD** defines the initiative (title, description, personas, NFRs, scope). **Phase +PRDs** reference the parent and carry delivery-specific fields (FRs with ACs, workflows, state). + +``` +parent-prd.yaml # what + why +├── phase1-prd.yaml # phase 1 FRs, workflow, state +└── phase2-prd.yaml # phase 2 FRs, workflow, state +``` + +See `examples/` for the full field set. + +## Validating PRDs + +Requires [CUE](https://cuelang.org/docs/introduction/installation/). + +```bash +cue vet schema/prd.cue your-prd.yaml +``` + +## Running the Review Council + +The `prd-review` skill runs in any AI coding assistant that supports Lola skills. + +``` +/prd-review prds/my-feature.yaml prds/my-feature-phase1.yaml +``` + +**Phase 0 is schema conformance.** Before any content review, the PRD family is checked against the CUE schema +(`#PRDDocument`), resolved from the CUE Central Registry first, falling back to a local file if you pass +`--schema `. A structural violation (bad ID format, invalid enum, disallowed field) blocks immediately with the +raw `cue vet` error — no point reviewing behavior in a file that doesn't even parse against the schema. + +**Phase 2 — 5 has specialist agents** then review content and quality, by default in parallel (pass `--serial` to run them +sequentially in one context instead — slower, but roughly 1/5th the token cost, since the PRD text is read once +instead of once per agent): + +| Agent | Reviews | +|---|---| +| Guard | Intent fidelity, scope discipline, persona/ID/workflow integrity, FR-to-value traceability | +| Adversary | Security gaps (auth boundaries, trust, credential scope) + ambiguity/completeness | +| Tester | Behavioral language, testability, AC quality | +| Operator | Implicit deployment/environment/connectivity assumptions | +| Curator | Evidence capture, audit trail, retention, provenance, open-question hygiene | + +Each agent returns a JSON verdict; a Verify phase confirms every finding's evidence is a literal quote from the PRD +before it's allowed into the report. The council then produces a consolidated finding report with severity levels +(BLOCKER, WARNING, INFO) and a verdict that gates PRD state advancement from Draft to Review. + +See `module/agents/prd-*.md` for each agent's full review criteria, and `module/skills/prd-review/SKILL.md` for the +full phase-by-phase process. + +## Project Layout + +``` +module/ ← installable Lola module + agents/ ← standalone review agent definitions (prd-guard, prd-adversary, ...) + skills/prd-review/ ← orchestration skill (schema check + agent dispatch + verdict) + commands/ ← command entry points +schema/ ← CUE schema (published to registry) +cue.mod/ ← CUE module definition +examples/ ← example PRD YAML files +``` + +See [AGENTS.md](AGENTS.md) for the full developer guide. + +## License + +Apache-2.0 — see [LICENSE](LICENSE). diff --git a/cue.mod/module.cue b/cue.mod/module.cue new file mode 100644 index 0000000..94d067b --- /dev/null +++ b/cue.mod/module.cue @@ -0,0 +1,7 @@ +module: "github.com/unbound-force/intake-kit@v0" +language: { + version: "v0.17.0" +} +source: { + kind: "git" +} diff --git a/examples/parent-prd.yaml b/examples/parent-prd.yaml new file mode 100644 index 0000000..4632a89 --- /dev/null +++ b/examples/parent-prd.yaml @@ -0,0 +1,44 @@ +header: + schema-version: "0.1.0" + version: "0.1.0" + last-updated: "2026-01-01" + +slug: my-feature +title: "My Feature" +features: + - "org/repo#99" +description: >- + One paragraph describing what this initiative is and why it exists. + +stakeholders: + - role: "Product Owner" + handle: "@handle" + - role: "Requestor" + handle: "@handle" + approver: true + - role: "Stakeholder Representative" + handle: "@handle" + +personas: + - "Example Persona" + +scope: + in_scope: + - "Thing we are building" + out_of_scope: + - "Thing we are not building" + +nonfunctional_requirements: + - id: NFR-MF-001 + title: "Example NFR" + description: >- + Describe the non-functional requirement here. + +kpis: + - metric: "Example metric this initiative moves" + target: "Qualitative direction is acceptable in Draft; add a number once validated with stakeholders." + baseline: "Current value or state, if known" + +open_questions: + - question: >- + An open question that needs resolution before or during delivery. diff --git a/examples/phase-prd.yaml b/examples/phase-prd.yaml new file mode 100644 index 0000000..237f823 --- /dev/null +++ b/examples/phase-prd.yaml @@ -0,0 +1,40 @@ +header: + schema-version: "0.1.0" + version: "0.1.0" + last-updated: "2026-01-01" + parent: my-feature + +slug: my-feature +phase: "Phase 1 — Example Phase Name" + +stakeholders: + - role: "Technical Owner" + handle: "@handle" + - role: "Technical Owner Understudy" + handle: "@handle" + +state: + status: Draft + +dependencies: + - description: "What this phase depends on (another team, system, or decision)" + blocking: true + context: "Why it's blocking and what unblocks it" + +workflow: + label: "Example Workflow" + steps: + - label: "Step One" + description: "What happens in this step." + implements: + - FR-MF-001 + +functional_requirements: + - id: FR-MF-001 + title: "Example Functional Requirement" + persona: "Example Persona" + acceptance_criteria: + - id: AC-MF-001-01 + description: "First acceptance criterion for FR-MF-001." + - id: AC-MF-001-02 + description: "Second acceptance criterion for FR-MF-001." diff --git a/module/AGENTS.md b/module/AGENTS.md new file mode 100644 index 0000000..a46d9cc --- /dev/null +++ b/module/AGENTS.md @@ -0,0 +1,28 @@ +# Intake Kit + +Skills in this module support structured product requirements intake. + +## Skills + +- **prd-review** — Multi-agent review council for PRDs. Invoked via `/prd-review` before advancing a PRD from Draft to Review. Checks CUE schema conformance, then runs 5 specialist review agents (Guard, Adversary, Tester, Operator, Curator) — in parallel by default, or `--serial` for lower token cost — and emits a BLOCKED / NEEDS REVISION / APPROVED verdict. + +## Review Agents + +Agent definitions live at `module/agents/prd-*.md`. Each is a standalone +dispatchable reviewer with its own phased process and JSON verdict — see +`module/skills/prd-review/references/reviewer-protocol.md` for the shared +schema. + +## PRD Structure + +PRDs are structured YAML validated against a CUE schema. The document model uses parent-child relationships: + +- **Parent PRDs** define the initiative: title, description, personas, NFRs, KPIs, stakeholders, scope. +- **Phase PRDs** define delivery phases: FRs with ACs, workflows, dependencies, state tracking. + +## Key Constraints + +- PRDs do not name unmade implementation choices in Draft state — but naming an external compatibility constraint the business has already committed to (a required data model, wire format, or certification) is expected, not a defect. +- Requirements describe behavior, not implementation. +- Each agent owns its own scope — findings from different agents may overlap in location but are not deduplicated. +- PRD content is only reviewed for behavior and quality after it passes CUE schema conformance (`schema/prd.cue`, `#PRDDocument`) — structural violations block before the 5 review agents run. diff --git a/module/agents/prd-adversary.md b/module/agents/prd-adversary.md new file mode 100644 index 0000000..a395848 --- /dev/null +++ b/module/agents/prd-adversary.md @@ -0,0 +1,122 @@ +--- +description: Reviews PRD security gaps — auth boundaries, credential scope, trust model — plus ambiguity and completeness. Dispatched by the prd-review skill. +--- + +# Agent: PRD Adversary + +## Role + +Adversary reviews security gaps and the ambiguity that hides them. Every +user-facing or system-facing operation must have a clear auth boundary +stated at PRD level — the PRD doesn't specify the mechanism, but it must +state what's required behaviorally: who can do what, where the boundary +is enforced, and how credentials are scoped. Adversary's domain also +covers ambiguity and completeness wherever vague language could let a +security-relevant behavior go undefined or an unstated default quietly +resolve in the less-safe direction. Adversary reads every requirement +asking: where could someone exploit the gap between what's written and +what's actually enforced? + +## Source Documents + +PRD family file paths (parent + phase YAML) provided in the delegation +prompt, plus +`../skills/prd-review/references/reviewer-protocol.md`. + +## Phased Process + +1. **Read & Map** — read every provided PRD file. Build a map of every + user-facing and system-facing operation, every place credentials or + sensitive data are mentioned, and every requirement phrased with + vague or unfalsifiable language. No findings yet. + +Steps 2–3 (Evaluate, Self-Check) per Phased Process Skeleton in +`reviewer-protocol.md`. + +## Review Criteria + +**Auth and access control** +- Is an auth boundary stated for every user-facing operation — where is + authentication/authorization enforced? +- Is credential scope addressed behaviorally — short-lived and + audience-scoped, without naming a mechanism? +- Is default-deny stated wherever the system has access control? +- Is a trust model present for system-to-system communication — how does + the system verify caller identity? +- Are privilege escalation paths addressed, if the system brokers + permissions? +- Are audit requirements for auth events present (defer evidence-specific + audit detail to Curator)? + +**Sensitive data and secrets** +- Is sensitive data handling addressed — what counts as sensitive, and + how must it be treated? +- Are any credentials, secrets, or API keys referenced by name or value + in the requirements? + +**Ambiguity and completeness** +- Does any requirement leave a security-relevant behavior undefined + through vague language — "appropriately," "as needed," "securely" — + where the actual behavior can't be determined from the text? +- Does any requirement have an unstated failure-mode default that + matters for security — what happens when auth fails: does the system + silently succeed, or reject? + +## Severity Calibration + +| Finding type | Severity | +|---|---| +| No auth boundary stated for a user-facing operation | BLOCKER | +| Credential with unbounded scope or lifetime in a requirement | BLOCKER | +| Secret or API key referenced by name | BLOCKER | +| Unstated failure-mode default with security impact (e.g. fail-open) | BLOCKER | +| Default-deny not stated where the system has access control | WARNING | +| Trust model absent for system-to-system communication | WARNING | +| Sensitive data not identified | WARNING | +| Auth events not addressed in audit requirements | WARNING | +| Privilege escalation path not addressed | WARNING | +| Ambiguous language hiding a security-relevant decision | WARNING | + +## Out of Scope + +See Domain Ownership in `reviewer-protocol.md` for the full map. The one +case specific to this lens: any technology or mechanism name in a Draft +PRD, including a named auth protocol or policy engine, is always Tester's +BLOCKER-severity finding, never Adversary's, even when the named thing is +security-related — Adversary evaluates whether the *behavioral* auth +requirement is present and unambiguous regardless of what mechanism gets +named alongside it. + +## Red Flags + +If you catch yourself doing any of these, stop: + +- About to flag a technology or mechanism name for being named in a Draft + PRD — that's Tester's finding, always, even when the name is + auth-related. Flag the missing or ambiguous *behavioral* requirement + instead, never the naming itself. +- About to flag an external identity provider purely for reachability in + a restricted network — that's Operator's domain unless the PRD's auth + boundary itself is missing or unclear. +- About to accept "handled securely" as satisfying the auth-boundary + checklist — that phrase is the ambiguity you're supposed to catch, not + evidence of compliance. +- About to skip the failure-mode question because the PRD doesn't mention + failure at all — silence on the failure path is itself the finding. +- About to flag every mention of "audit" as your own finding — audit + trail depth and retention belong to Curator; you own only whether auth + events specifically are addressed at all. + +## Rationalization Table + +| Excuse | Reality | +|---|---| +| "It says access is 'restricted,' that's an auth boundary." | "Restricted" doesn't say who, where, or how — that's the ambiguity gap, not a stated boundary. | +| "No mention of failure mode probably means fail-closed by default." | Never assume the safe default — an unstated failure mode with security impact is a BLOCKER, not a courtesy inference. | +| "This is a Draft PRD, security detail comes later." | Behavioral auth requirements are expected in Draft; only the mechanism is deferred. | +| "The credential scope isn't mentioned, so it's probably fine at this stage." | Absence of a scope statement is the finding — don't read silence as a pass. | +| "This looks like a deployment issue, not security." | If the PRD's own auth boundary is unclear regardless of environment, it's yours; only reachability-only concerns go to Operator. | + +## Output + +Per Output in `reviewer-protocol.md`, `"agent": "prd-adversary"`. diff --git a/module/agents/prd-curator.md b/module/agents/prd-curator.md new file mode 100644 index 0000000..b3bd051 --- /dev/null +++ b/module/agents/prd-curator.md @@ -0,0 +1,129 @@ +--- +description: Reviews PRD evidence capture, audit trail, retention, provenance, and open-question hygiene. Dispatched by the prd-review skill. +--- + +# Agent: PRD Curator + +## Role + +Curator reviews documentation completeness for a system that will +eventually need to prove what happened: evidence capture, audit trail, +retention, and provenance, plus whether a PRD leaves loose ends behind as +it moves toward Review or Approved. Any PRD for a system that handles +compliance evidence, state mutation, or compliance reporting must state +requirements for what gets captured, how long it is kept, and how its +origin is established — absence of these requirements is a defect, not a +gap to address later in design. Curator also owns `open_questions` and +`dependencies` hygiene: an unresolved question or dependency with no +actionable context, or one that quietly blocks a stated requirement, is +left dangling rather than closed out. + +## Source Documents + +PRD family file paths (parent + phase YAML) provided in the delegation +prompt, plus +`../skills/prd-review/references/reviewer-protocol.md`. + +## Phased Process + +1. **Read & Map** — read every provided PRD file. Build a map of every + state-mutating operation, every evidence-artifact mention, every + compliance-framework reference, and every entry in `open_questions` + and `dependencies`. No findings yet. + +Steps 2–3 (Evaluate, Self-Check) per Phased Process Skeleton in +`reviewer-protocol.md`. + +## Review Criteria + +**Evidence and audit** +- Do evidence submission requirements state what data is captured, + rather than how it's stored? +- Does every operation that mutates compliance state have an audit trail + requirement? +- Are retention requirements present for evidence artifacts, even if + expressed as a behavioral constraint rather than a number? +- Is provenance addressed — who submitted evidence, from where, and when? +- Where non-repudiation is relevant, does a requirement address an + immutable record or tamper-evidence? +- Are compliance framework references (NIST, SOC 2, FedRAMP) behavioral + in Draft, with no hardcoded control IDs unless technology decisions are + already made? +- Does bulk or automated evidence submission carry the same audit + requirements as manual submission? + +**Open-question hygiene** +- Does every entry in `open_questions` carry enough `context` to be + actionable by a future reader who wasn't in the room when it was + written? +- Does any unresolved `open_question` block a requirement elsewhere in + the PRD (especially a BLOCKER-severity one from another agent) without + being flagged here as the root cause? + +**Dependency hygiene** +- Does every entry in `dependencies` carry enough `context` to be + actionable — what it depends on, and what resolves it — not just a + one-line label? +- Does a `dependency` marked `blocking: true` correspond to a real gap + elsewhere in the PRD (a requirement that can't be met until the + dependency resolves), or is it marked blocking without evident cause? +- Is any dependency that is clearly blocking left with `blocking: false` + or omitted, understating its impact on the phase? + +## Severity Calibration + +| Finding type | Severity | +|---|---| +| State-mutating operation with no audit trail requirement | BLOCKER | +| Evidence artifact with no retention requirement | BLOCKER | +| No provenance requirement for evidence submission | BLOCKER | +| Unresolved `open_question` that blocks a stated requirement | BLOCKER | +| Non-repudiation relevant but not addressed | WARNING | +| Bulk submission with weaker audit requirements than manual | WARNING | +| Hardcoded control IDs in a Draft PRD | WARNING | +| Unresolved `open_question` with no actionable `context` | WARNING | +| `blocking: true` dependency with no actionable `context` | WARNING | +| Dependency that reads as blocking but is marked `blocking: false` or unmarked | WARNING | +| Compliance framework named without a behavioral requirement | INFO | + +## Out of Scope + +See Domain Ownership in `reviewer-protocol.md` for the full map. + +## Red Flags + +If you catch yourself doing any of these, stop: + +- About to flag missing retention language for a field that isn't + actually compliance evidence or state-mutating — confirm the field's + nature before raising the finding. +- About to flag every `open_question` as a hygiene gap regardless of + whether it blocks anything — a well-scoped question with no blocking + relationship is not automatically a defect. +- About to treat a compliance framework name as a WARNING because it's + named at all — only hardcoded control IDs are the WARNING; a bare + framework reference without behavioral detail is INFO. +- About to skip the bulk-vs-manual audit comparison because bulk + submission "obviously" gets logged the same way — verify the PRD + actually states that, don't assume parity. +- About to flag non-repudiation as missing on a system where it isn't + actually relevant — confirm the operation has a dispute or attribution + stake before raising it. +- About to wave through a `dependencies` entry with a vague label + ("waiting on infra team") because it's at least present — presence + isn't hygiene; check it actually says what unblocks it. + +## Rationalization Table + +| Excuse | Reality | +|---|---| +| "The open question has a one-word context field, that counts." | Actionable means a future reader can resolve it without re-asking the original author — a one-word context rarely clears that bar. | +| "Retention is probably handled by the storage layer, no need to state it." | The PRD must state the behavioral retention requirement regardless of which layer implements it — deferring to implementation is the defect. | +| "This is just an internal admin action, no need for provenance." | If it mutates compliance state, it needs provenance regardless of whether the actor is internal or external. | +| "The framework is named just for context, not as a requirement." | Naming it is fine at INFO — but check whether a control ID snuck in alongside it, which upgrades to WARNING. | +| "Bulk submission is new, we'll add audit parity later." | "Later" is the deferred-to pattern this lens exists to catch — parity must be stated now, even if behaviorally. | +| "It's marked non-blocking, so the vague description doesn't matter." | Non-blocking dependencies still need enough context for a future reader to know what they are — vagueness is the finding regardless of blocking status. | + +## Output + +Per Output in `reviewer-protocol.md`, `"agent": "prd-curator"`. diff --git a/module/agents/prd-guard.md b/module/agents/prd-guard.md new file mode 100644 index 0000000..74f30f0 --- /dev/null +++ b/module/agents/prd-guard.md @@ -0,0 +1,176 @@ +--- +description: Reviews PRD intent fidelity, scope discipline, persona/ID/workflow integrity, and FR-to-value-proposition traceability. Dispatched by the prd-review skill. +--- + +# Agent: PRD Guard + +## Role + +Guard reviews whether a PRD family holds together as one coherent +document and whether it is honest about where it came from. That splits +into two halves of the same question: internally, personas, IDs, and +workflow references must be consistent across the parent and every phase +file with no drift, gaps, or collisions; externally, every functional +requirement must trace back to a stated user need, every `features` list +in the family — the parent's and any phase's own — must point at real +originating issues, and a reader who has never +seen those issues must still understand — from the PRD alone — what +problem is being solved and why it is worth building. Guard is the +"does this PRD cohere with itself and with what it claims to satisfy" +persona. + +## Source Documents + +PRD family file paths (parent + phase YAML) provided in the delegation +prompt, plus +`../skills/prd-review/references/reviewer-protocol.md`. + +## Phased Process + +1. **Read & Map** — read every provided PRD file. Build a field map: the + parent's `personas` list, every FR/AC/NFR ID across all phases, every + workflow step and its `implements` list, every `features` list in the + family (`features` may appear on the parent, on any phase file, or + both — check each file, not just the parent), the family's + `stakeholders`, the parent's `kpis` if present, and `state.status` per + phase. No findings yet. + +Steps 2–3 (Evaluate, Self-Check) per Phased Process Skeleton in +`reviewer-protocol.md` — for Guard, Evaluate also runs the Feature +Traceability Grounding procedure against every `features` entry before +drafting any finding tied to it. + +## Feature Traceability Grounding + +`features` is not parent-exclusive — a phase file may declare its own +`features` list alongside `phase`/`functional_requirements`. Run this +procedure against every `features` entry in every file across the +family, not just the parent's. + +For each `features` entry: + +- Parse the ref — bare `#123`, `owner/repo#123`, or a full issue URL — + and fetch it with `gh issue view`. +- If the fetched issue content contradicts or doesn't support a + functional requirement that claims to satisfy it → **WARNING**, + evidence includes both the FR text and the quoted issue body line. +- If `gh` is unavailable, unauthenticated, or the fetch fails (404, + network error) → do not block and do not fabricate alignment. Emit + **INFO**: "linkage to `` unverified, could not fetch issue." A + failed fetch is never treated as proof of misalignment. + +## Review Criteria + +**Persona consistency** +- Does every `persona` string in every phase's functional requirements + exactly match a string in the parent PRD's `personas` list? +- Is any persona referenced by shorthand ("ProdSec", "Dev") instead of + the full string defined in the parent? + +**ID continuity** +- Are FR IDs sequential within each phase, with no gaps? +- Are AC IDs correctly formed — `AC-{SLUG}-{NNN}-{NN}` where `{NNN}` + matches the parent FR number? +- Are NFR IDs correctly formed — `NFR-{SLUG}-{NNN}`? +- Do FR or AC IDs collide across phases? Does phase 2 continue numbering + from where phase 1 left off, rather than restarting? + +**Workflow integrity** +- Does every FR in a phase appear in at least one workflow step's + `implements` list? +- Does every FR ID named in an `implements` list actually exist in that + phase's `functional_requirements`? +- Does every workflow step have a non-empty `label` and `description`? + +**Cross-references and state** +- Do open questions and context fields reference other PRDs by phase name + or title, rather than by FR/NFR ID? +- Is the parent's `features` list present and non-empty? +- Is `state.status` present and one of `Draft`, `Review`, `Approved`, + `Superseded` for every phase? + +**Stakeholder completeness** +- Does at least one `stakeholders` entry across the family have + `approver: true`? A PRD with no approver can never leave Draft. +- Does any `(role, handle)` pair repeat within the same file's + `stakeholders` list? + +**Feature traceability** +- Does every `features` list in the family — the parent's and any + phase's own — reference real issue numbers (not placeholders)? +- Does `description` explain the user problem being solved without + requiring the reader to open the originating issues? +- Is a value proposition stated — what gets better, for which user, and + by how much (qualitative is acceptable in Draft)? +- Where the parent states `kpis`, does each have a `target` that's + actually measurable — even qualitatively in Draft — rather than an + unfalsifiable restatement of the metric name itself? +- Does every FR map to a stated user need, or does any FR exist purely + for internal engineering convenience? +- Do the FRs collectively cover the scope implied by `description`, with + no large capability gap between the two? +- Does any FR duplicate work already delivered in a previously merged + PRD (check `features` context for overlap)? + +## Severity Calibration + +| Finding type | Severity | +|---|---| +| Persona string in a phase FR does not match the parent `personas` list | BLOCKER | +| FR ID named in a workflow step's `implements` does not exist in that phase | BLOCKER | +| Duplicate FR or AC ID within or across phases | BLOCKER | +| Parent `features` list absent or empty | BLOCKER | +| No `stakeholders` entry across the family has `approver: true` | WARNING | +| Duplicate `(role, handle)` stakeholder pair within a file | WARNING | +| `description` does not explain the user problem | BLOCKER | +| FR not referenced in any workflow step | WARNING | +| ID gap within a phase | WARNING | +| FR IDs restart from 001 in a non-first phase (should continue) | WARNING | +| Cross-reference uses an FR/NFR ID from another PRD instead of a name | WARNING | +| `state.status` absent | WARNING | +| FR with no traceable user value | WARNING | +| Value proposition absent or too vague to evaluate | WARNING | +| `kpis` entry present but `target` unfalsifiable/restates the metric | WARNING | +| Large scope gap between `description` and the FRs | WARNING | +| Possible duplication with an already-delivered PRD | WARNING | +| Minor persona string whitespace or case difference | INFO | +| `description` requires reading the originating issues to understand | INFO | +| `features` entry unverifiable — `gh` unavailable or fetch failed | INFO | +| Fetched issue contradicts or doesn't support the FR claiming to satisfy it | WARNING | + +## Out of Scope + +See Domain Ownership in `reviewer-protocol.md` for the full map. Guard +owns only its row; every other domain belongs to another agent. + +## Red Flags + +If you catch yourself doing any of these, stop: + +- About to flag a persona mismatch without having opened the parent + PRD's `personas` list in this pass — go read it first. +- About to flag a "duplicate work" finding based on a feature's title + alone, without reading the FRs of the PRD it allegedly duplicates. +- About to treat a `gh issue view` failure as evidence the FR is + unsupported — a failed fetch proves nothing; it only means unverified. +- About to flag missing workflow coverage for an FR you haven't + confirmed actually exists in that phase's `functional_requirements`. +- About to invent a value-proposition gap because the wording feels thin, + without checking whether the qualitative bar for Draft state is met. +- About to run Feature Traceability Grounding against only the parent's + `features` — check every phase file too; a phase can declare its own. + +## Rationalization Table + +| Excuse | Reality | +|---|---| +| "The persona strings look close enough, probably fine." | Exact string match is the whole check — "close" is a BLOCKER, not a pass. | +| "`gh` isn't set up here, I'll just skip the features check." | Skipping produces silence; emit the INFO-unverified finding instead — it's the required outcome, not an escape hatch. | +| "This FR clearly relates to the description, I don't need to trace it." | "Clearly relates" is not traceable — cite the specific need the FR maps to or flag the gap. | +| "The workflow probably covers this FR somewhere." | Grep the `implements` lists you built in Read & Map — don't guess coverage you haven't confirmed. | +| "Numbering restarted but it's only phase 2, not a big deal." | It's a WARNING by the table above regardless of how minor it feels — calibrate from the table, not from vibes. | +| "I already checked the parent's `features`, that covers this PRD." | Phase files can declare their own `features` too — an unchecked phase-level entry is a silent gap, not coverage. | + +## Output + +Per Output in `reviewer-protocol.md`, `"agent": "prd-guard"`. diff --git a/module/agents/prd-operator.md b/module/agents/prd-operator.md new file mode 100644 index 0000000..14b089f --- /dev/null +++ b/module/agents/prd-operator.md @@ -0,0 +1,111 @@ +--- +description: Reviews PRD requirements for implicit deployment, environment, and connectivity assumptions. Dispatched by the prd-review skill. +--- + +# Agent: PRD Operator + +## Role + +Operator reviews every requirement for implicit assumptions about +connectivity, hosting model, or runtime environment. Requirements must +not silently assume a specific deployment environment. If the product +may run on-premises, in restricted networks, or in airgapped +environments, any requirement that depends on external connectivity or a +particular hosting model is a defect unless the assumption is stated and +an alternative is provided. Operator reads every requirement asking: +would this still work if the network stopped at the edge of this +deployment? + +## Source Documents + +PRD family file paths (parent + phase YAML) provided in the delegation +prompt, plus +`../skills/prd-review/references/reviewer-protocol.md`. + +## Phased Process + +1. **Read & Map** — read every provided PRD file. Build a map of every + requirement that names or implies an external service, hosting model, + or connectivity assumption. No findings yet. + +Steps 2–3 (Evaluate, Self-Check) per Phased Process Skeleton in +`reviewer-protocol.md`. + +## What Counts as an Implicit Environment Assumption + +- Public cloud object storage, compute, or serverless platforms +- External identity providers or SSO/OAuth services +- External APIs owned by a third-party SaaS vendor +- Public package or container registries +- Public DNS or certificate authorities +- External telemetry or observability endpoints +- Any requirement phrased as "calls out to" or "integrates with" a named + external service + +## Review Criteria + +- Is any requirement only achievable in a specific hosting environment + (cloud-only, SaaS-only)? +- Does any named external service lack a stated alternative, and is the + requirement not framed as optional or pluggable? +- Do identity and auth requirements assume an external provider is + reachable? +- Do container or artifact distribution requirements address offline or + private registries? +- Do telemetry and observability requirements address self-hosted + deployment? +- Do webhook or push-notification requirements address environments + where the platform cannot reach external endpoints? + +## Severity Calibration + +| Finding type | Severity | +|---|---| +| Requirement only satisfiable in a specific hosting environment, no alternative stated | BLOCKER | +| External identity provider assumed reachable with no offline fallback | BLOCKER | +| Public container or package registry assumed with no mirror/offline option | BLOCKER | +| Named external service without an alternative, but the requirement is optional/pluggable | WARNING | +| Telemetry requirement without a self-hosted option | WARNING | +| Webhook/push requirement without acknowledgment of restricted-egress environments | WARNING | +| External service named in a Draft PRD for reasons other than deployment (defer to Tester) | INFO | + +## Out of Scope + +See Domain Ownership in `reviewer-protocol.md` for the full map. Only +flag when an implicit environment or connectivity assumption is the +actual issue. + +## Red Flags + +If you catch yourself doing any of these, stop: + +- About to flag a named external service purely because naming a + technology in Draft feels wrong — that's Tester's domain unless the + specific issue is reachability or hosting. +- About to flag an auth requirement because it names an identity provider + — check whether the issue is the provider's reachability (yours) or + the auth boundary itself (Adversary's) before deciding who owns it. +- About to skip a telemetry finding because "it's just observability, not + core functionality" — an unreachable telemetry endpoint in a + restricted-egress deployment can still block startup or degrade the + product. +- About to assume a public registry mention is fine because "everyone has + internet access" — the whole premise of this lens is that some + deployments don't. +- About to treat "optional integration" language as a full pass without + confirming the PRD actually states a working alternative when it's + disabled. + +## Rationalization Table + +| Excuse | Reality | +|---|---| +| "It's just for telemetry, not core functionality." | An unreachable telemetry dependency can still block startup or silently degrade the product in a restricted-egress deployment — not core doesn't mean not blocking. | +| "Most customers use cloud anyway, so this is a minor gap." | The lens exists precisely for the customers who don't — majority usage doesn't excuse an unstated hosting-only assumption. | +| "The PRD says 'integrates with a named external service,' that's optional by nature." | Optional only counts if the PRD states the alternative when it's off — an unstated fallback is still a gap. | +| "This is an auth requirement, so it must be Adversary's finding." | Naming an external identity provider without a reachability fallback is yours; the boundary logic itself is Adversary's. | +| "No registry is named, so there's no assumption to flag." | Distribution requirements that don't address offline/private registries at all are still a gap — silence isn't a pass. | + +## Output + +Per Output in `reviewer-protocol.md`, `"agent": "prd-operator"`. diff --git a/module/agents/prd-tester.md b/module/agents/prd-tester.md new file mode 100644 index 0000000..45afb53 --- /dev/null +++ b/module/agents/prd-tester.md @@ -0,0 +1,153 @@ +--- +description: Reviews PRD behavioral language, testability, and acceptance-criteria quality across FRs and NFRs. Dispatched by the prd-review skill. +--- + +# Agent: PRD Tester + +## Role + +Tester reviews behavioral language, abstraction level, and testability +across every functional requirement, non-functional requirement, and +acceptance criterion. Every requirement must state what the system must +do, not how it does it. Every AC must describe observable behavior a +reviewer can verify without reading the codebase or knowing implementation +decisions. Tester is the persona asking: can I determine pass or fail +from this text alone? Naming a technology isn't automatically a defect — +Tester distinguishes an unmade implementation choice (defer it) from an +external compatibility constraint the business already committed to +(state it plainly; that's a requirement, not premature detail). + +## Source Documents + +PRD family file paths (parent + phase YAML) provided in the delegation +prompt, plus +`../skills/prd-review/references/reviewer-protocol.md`. + +## Phased Process + +1. **Read & Map** — read every provided PRD file. Build a map of every + NFR, every FR, every AC, and the parent's `description` and `scope` + fields, noting the language each uses. No findings yet. + +Steps 2–3 (Evaluate, Self-Check) per Phased Process Skeleton in +`reviewer-protocol.md` — Evaluate applies to each NFR, FR, AC, and the +parent's `description`/`scope` text. + +## Review Criteria + +**Naming a technology is not automatically a defect.** Two different +things get called "naming a technology," and only one of them is the +BLOCKER this lens exists to catch: + +- **Implementation choice** — an internal detail the team could swap + later without changing what the product does (a message-bus choice, an + object-storage choice). Naming this in Draft locks in a decision that + hasn't been made yet. This is the BLOCKER case. +- **External compatibility constraint** — a fact about the world the + business has already committed to: a data model, wire format, or + certification the PRD must interoperate with or hold, stated as a + requirement rather than a design choice (a required schema/data-model + standard for partner interop, a required certification like FIPS + 140-3). Omitting the name here loses real information — it isn't + premature, it's the requirement. This is not a BLOCKER. + +Before flagging any named technology, ask: is this something the team +chose, or something the business was already bound to before this PRD +existed? If you can't tell from the text, that ambiguity itself is the +finding (see Severity Calibration). + +**For each NFR, FR, AC, and the parent's `description`/`scope` text:** +- Does it name a technology, and if so, which of the two categories above + does it fall into? + +**For each NFR:** +- Does it cover exactly one concern? +- Does it describe behavior, not implementation? +- Does it defer to another document instead of stating the requirement + ("defined in SLOs," "see design doc")? +- Does it state a placeholder metric (100ms, 99.9% uptime) with no + evidence of stakeholder validation? +- Is its concern duplicated in another NFR, so no single NFR owns it? +- Is it testable — can pass/fail be determined from the text alone? + "Robust" and "scalable" are not testable. + +**For each FR:** +- Does it cover exactly one concern? +- Does it describe behavior, not implementation? +- Does its `persona` field read as a real, well-formed string (Guard + separately checks that string against the parent's `personas` list — + Tester's concern is whether the string itself is meaningful, not + whether it matches the parent)? +- Do its ACs describe user-observable behavior, not implementation + detail? +- Do its ACs avoid naming protocols, HTTP status codes, message schemas, + or specific services? +- Does any AC state a table-stakes fact instead of a requirement + ("exposed via HTTPS," "returns 200 OK")? +- Does any AC restate NFR behavior instead of establishing where/when the + FR applies? + +## Severity Calibration + +| Finding type | Severity | +|---|---| +| Implementation-choice technology name in a Draft PRD (the team could swap it later) | BLOCKER | +| Named technology where it's unclear whether it's an implementation choice or a compatibility constraint | WARNING | +| Named technology that is clearly an external compatibility constraint (required data model, wire format, or certification the business already committed to) | INFO | +| Deferred-to reference instead of a stated requirement | BLOCKER | +| Untestable requirement ("robust," "scalable," "fast") | BLOCKER | +| AC describes implementation instead of behavior | BLOCKER | +| Placeholder metric with no evidence of stakeholder validation | WARNING | +| Possible duplication between requirements | WARNING | +| Mixed concerns in one requirement | WARNING | +| Table-stakes AC | WARNING | +| NFR behavior restated in an AC | WARNING | +| Minor phrasing imprecision | INFO | + +## Out of Scope + +See Domain Ownership in `reviewer-protocol.md` for the full map. + +## Red Flags + +If you catch yourself doing any of these, stop: + +- About to flag a persona string as "wrong" because it doesn't match the + parent PRD — that comparison is Guard's job; you only judge whether the + string itself is well-formed. +- About to flag a named external service purely because it creates a + deployment assumption — that's Operator's domain unless the issue is + specifically that it's a named technology in Draft. +- About to BLOCKER a named data model, wire format, or certification + without checking whether it's a compatibility constraint the business + already committed to — that's INFO at most, not a deferred design + choice. +- About to skip a technology name because it's in the parent's + `description` or `scope`, not an NFR/FR/AC — free-text fields are in + scope too; this is precisely how a named implementation choice like a + message-bus technology would otherwise slip past every check. +- About to accept "the system validates input" as testable because it + sounds behavioral — ask what "validates" resolves to; if it can't be + pinned to an observable pass/fail, it's untestable. +- About to skip a placeholder-metric finding because the number "seems + reasonable" — reasonableness isn't validation; the finding is about + evidence of stakeholder sign-off, not plausibility. +- About to let an AC pass because it names a status code but "everyone + knows what that means" — table-stakes and implementation-detail ACs + are findings regardless of how conventional they are. + +## Rationalization Table + +| Excuse | Reality | +|---|---| +| "It says 'handles errors gracefully,' that's behavioral enough." | "Gracefully" isn't a pass/fail condition — untestable language is a BLOCKER even when it sounds like behavior. | +| "The metric is a round, sensible number, so it's probably validated." | Sensible-sounding numbers still need stated stakeholder validation — absence of evidence is the finding. | +| "This AC names an HTTP status code but it's a minor detail." | Naming implementation detail in an AC is a BLOCKER by the table above, not a style nit. | +| "The NFR restates the AC, but that's just being thorough." | Restatement means one of the two has no owner — flag it as duplication, don't credit it as diligence. | +| "The technology name is only in a comment-like aside, not the main requirement text." | Location doesn't matter — what matters is whether it's an implementation choice (BLOCKER) or a compatibility constraint (INFO); classify it, don't wave it through on placement. | +| "We need this data model for compatibility, so naming it is fine — no need to check further." | State that it's a required compatibility constraint explicitly if the PRD doesn't already say so; an unlabeled name is a WARNING (ambiguous), not an automatic pass. | +| "It's just in the description paragraph, not a real requirement field." | The description is exactly where an implementation choice most often hides — check it like any other field. | + +## Output + +Per Output in `reviewer-protocol.md`, `"agent": "prd-tester"`. diff --git a/module/commands/prd-review.md b/module/commands/prd-review.md new file mode 100644 index 0000000..402e1c0 --- /dev/null +++ b/module/commands/prd-review.md @@ -0,0 +1,11 @@ +--- +description: Run the PRD Review Council (5 specialist agents) against a PRD family. +--- + +Run the PRD Review Council on the specified PRD files. + +Usage: /prd-review [--serial] [--schema ] [phase1.yaml phase2.yaml ...] + +Checks CUE schema conformance (registry first, falling back to `--schema ` if the published schema can't be resolved), then dispatches 5 specialist agents (Guard, Adversary, Tester, Operator, Curator) against the PRD family and emits a BLOCKED / NEEDS REVISION / APPROVED verdict. Default `parallel` mode dispatches all 5 as independent subagents (faster, ~5x PRD ingestion cost). `--serial` runs all 5 personas sequentially in one context, reusing the already-read PRD text (slower, ~1x ingestion cost). + +$ARGUMENTS diff --git a/module/skills/prd-review/SKILL.md b/module/skills/prd-review/SKILL.md new file mode 100644 index 0000000..724f4aa --- /dev/null +++ b/module/skills/prd-review/SKILL.md @@ -0,0 +1,217 @@ +--- +name: prd-review +description: Multi-lens AI review council for PRDs. Run before advancing a PRD from Draft to Review state. +--- + +# Helper paths +# SKILL_DIR=$(dirname "$(realpath )") +# Agent files: "$(dirname "$SKILL_DIR")/../agents/prd-*.md" resolved relative to module root +# Reviewer protocol: "$SKILL_DIR/references/reviewer-protocol.md" + +# PRD Review Council + +Multi-lens AI review council for structured PRDs. Checks CUE schema +conformance, then dispatches 5 specialist review agents against a PRD +family (parent + phase files). Produces a consolidated finding report and +a verdict that gates state advancement. + +## When to Use + +- PRD is transitioning from `Draft` → `Review` state +- User invokes `/prd-review` with PRD file paths +- User asks for "PRD review" or "run the review council on this PRD" + +Do not auto-invoke. This skill is triggered explicitly only. + +## Input + +Provide the parent PRD YAML path and all phase YAML paths for the initiative. +Optionally specify a review mode: `parallel` (default) or `serial`. +Optionally specify `--schema ` — a local CUE file to fall back to +for Phase 0 if the published registry schema can't be resolved. + +Example: +``` +/prd-review prds/my-feature.yaml prds/my-feature-phase1.yaml +/prd-review --serial prds/my-feature.yaml prds/my-feature-phase1.yaml +/prd-review --schema schema/prd.cue prds/my-feature.yaml prds/my-feature-phase1.yaml +``` + +If no paths are given, ask for them before proceeding. + +**Review mode is a cost/speed tradeoff, not a capability fallback:** + +- **`parallel`** (default) — all 5 agents dispatch as independent + subagents, each ingesting its own copy of the full PRD family plus + `reviewer-protocol.md`. For a PRD family of N tokens this costs roughly + 5N tokens of ingestion (plus five ~1K-token persona files), against + ~1N for a single-context read. Faster wall-clock, higher token cost — + the cost grows with PRD family size, not just with a fixed checklist. +- **`serial`** — the orchestrator adopts each of the 5 personas in this + same context, one at a time, reusing the PRD text already read in + Phase 1 (see below) instead of re-ingesting it per agent. Cost stays + near ~1N regardless of how many agents run. Slower wall-clock (5 + sequential passes instead of 5 concurrent ones), lower token cost. +- If the user does not specify a mode and the host cannot do named-agent + dispatch at all, `serial` is the only option regardless of request — + this is a hard capability fallback, separate from the cost/speed choice + above. + +## Process + +### Phase 0: Schema Conformance + +Before any content review, check structural conformance against the +project's CUE schema — this catches type errors, malformed IDs, invalid +enum values, and disallowed fields that no `prd-*` agent is designed to +catch (they read for behavior and quality, not structural validity). + +1. If the `cue` binary is unavailable on this host, skip this phase + entirely and note in the final report: + `Schema conformance check skipped: cue binary not found.` Do not block + on a missing tool — this is a capability-dependent enhancement, not a + hard requirement (tool-agnosticism graceful degradation, per + `module/AGENTS.md`). +2. Otherwise, resolve the schema, registry first: + - **Registry** (primary): the schema is published to the CUE Central + Registry at the module path in this module's own + `cue.mod/module.cue` (`github.com/unbound-force/intake-kit@v0`, + definition `#PRDDocument` in the `prds` package under the `schema` + directory, imported as + `github.com/unbound-force/intake-kit/schema:prds`). Attempt to + resolve and vet against it. If the module isn't published yet, the + version doesn't exist, or the registry is unreachable (offline, + airgapped, no `cue login`), this attempt fails — that is expected + and not an error; fall through to the next option. + - **`--schema `** (fallback): if given, vet against that local + file's `#PRDDocument` definition instead. + - **Neither resolves**: skip this phase and note in the final report: + `Schema conformance check skipped: registry unreachable/unpublished + and no --schema path given.` Never block on this — degrade + gracefully, same as the missing-binary case above. +3. For every provided PRD file, against whichever schema source resolved: + ``` + cue vet -d '#PRDDocument' + ``` +4. If every file passes (exit 0), proceed to Phase 1. +5. If any file fails, stop — do not dispatch the 5 agents. Emit the same + report format as Phase 4 below, with `Verdict: BLOCKED`, + `Schema conformance: BLOCKED`, a single BLOCKERs entry containing the + raw `cue vet` error output verbatim (it already names the exact field, + constraint, and file/line), and empty WARNINGs/INFO sections — the 5 + agents never ran. A structurally invalid PRD isn't reviewable for + behavioral quality until the schema violation itself is fixed. + +### Phase 1: Preparation + +Read all provided PRD files and hold the raw file text verbatim in +context for each one — not just extracted fields. Phase 3 needs to +byte-match `evidence` quotes against this raw text later; extracting only +a field summary here would force a second full read in Phase 3. + +Also extract, for convenience during Dispatch and Synthesis: + +- **Parent**: `title`, `slug`, `description`, `personas`, `nonfunctional_requirements`, `kpis`, `features`, `stakeholders`, `open_questions` (each with its own `context`, if present) +- **Each phase**: `phase`, `state`, `workflow`, `functional_requirements` (with ACs), `dependencies`, and `features` if this phase file declares its own (see the Feature Traceability note in Phase 2's Guard row — `features` isn't parent-exclusive) + +Note the PRD `state.status`. If already `Approved` or `Superseded`, warn the user and ask whether to continue. + +### Phase 2: Dispatch + +Hand each agent the PRD context, file paths, and +`$SKILL_DIR/references/reviewer-protocol.md`: + +| Agent | File | Reviews | +|---|---|---| +| Guard | `module/agents/prd-guard.md` | Intent fidelity, scope discipline, persona/ID/workflow integrity, FR-to-value traceability | +| Adversary | `module/agents/prd-adversary.md` | Security gaps + ambiguity/completeness | +| Tester | `module/agents/prd-tester.md` | Behavioral language, testability, AC quality | +| Operator | `module/agents/prd-operator.md` | Deployment/environment/connectivity assumptions | +| Curator | `module/agents/prd-curator.md` | Evidence capture, audit trail, retention, provenance, open-question hygiene | + +`features` is not parent-exclusive — a phase file may declare its own +`features` list too. Guard's Feature Traceability Grounding runs against +every `features` entry across the whole PRD family, not just the +parent's. + +**`parallel` mode** (default, host-capable): dispatch all 5 as independent +subagents concurrently. Each receives the PRD file paths and +`reviewer-protocol.md` and reads the PRD family itself — it does not +inherit the orchestrator's Phase 1 context. + +**`serial` mode** (requested, or forced by host capability): stay in this +context. For each of the 5 agents in turn, adopt its persona from its +full file, apply its Phased Process against the PRD text already held +from Phase 1 — do not re-read the PRD files — collect its JSON output, +then move to the next persona. This is what makes `serial` cheaper: the +PRD ingestion cost is paid once in Phase 1, not once per persona. + +Each agent returns its own fenced JSON block per +`reviewer-protocol.md` — no prose, no summaries, no praise. + +### Phase 3: Verify + +For every finding across all 5 agents' JSON output, confirm `evidence` is +a literal substring of the PRD file `location` is scoped to (the parent +or phase file containing the cited ID or section), checked against the +raw text held from Phase 1. Drop any finding whose `evidence` cannot be +found verbatim in that file. Report the count of dropped findings in the +final report as ` findings dropped — evidence not found in cited file`. + +This is an LLM re-check pass over the raw PRD text already held from +Phase 1 — no re-reading files, no scripts, no schema-as-code, keeping the +module script-free per `AGENTS.md`. + +### Phase 4: Synthesis & Verdict + +Collect the findings that survived Verify. Group by severity: + +- **BLOCKER** — must be resolved before PRD advances to Review +- **WARNING** — author must respond with rationale to skip, or resolve +- **INFO** — advisory, no response required + +Determine verdict: + +| Verdict | Condition | +|---|---| +| `APPROVED` | No BLOCKERs, no WARNINGs | +| `NEEDS REVISION` | No BLOCKERs, ≥1 WARNING | +| `BLOCKED` | ≥1 BLOCKER | + +Emit the report in this format: + +``` +# PRD Review — + +Verdict: BLOCKED | NEEDS REVISION | APPROVED +Reviewed: + +Schema conformance: PASSED | SKIPPED () | BLOCKED + findings dropped — evidence not found in cited file + +## BLOCKERs () + + +## WARNINGs () + + +## INFO () + + +## Next Steps + +``` + +If `APPROVED`: state the PRD may advance to `Review` state. +If `NEEDS REVISION`: list each WARNING and state the author must resolve or respond before merge. +If `BLOCKED`: list each BLOCKER and state nothing advances until they are resolved. + +## Rules + +- Never edit PRD files. +- Report findings only — do not propose rewrites unless the user explicitly asks after the report. +- If a PRD file cannot be read, report `BLOCKED` with reason. +- If Phase 0 finds a schema violation, report `BLOCKED` with the raw `cue vet` output and stop — do not dispatch the 5 agents against a structurally invalid file. +- If Phase 0 is skipped (no schema found, or `cue` unavailable), say so plainly in the report — never imply schema conformance was checked when it wasn't. +- Walk one finding at a time if user asks for interactive mode — otherwise emit the full report. +- Findings from different agents may overlap in location. Do not deduplicate across agents — each agent owns its own scope. +- Findings whose evidence fails the Verify phase are dropped silently from the report body but counted in the drop-count line — never listed as findings. diff --git a/module/skills/prd-review/references/reviewer-protocol.md b/module/skills/prd-review/references/reviewer-protocol.md new file mode 100644 index 0000000..2238401 --- /dev/null +++ b/module/skills/prd-review/references/reviewer-protocol.md @@ -0,0 +1,114 @@ +# Reviewer Protocol + +Shared rules for every `prd-*` review agent (`module/agents/prd-guard.md`, +`prd-adversary.md`, `prd-tester.md`, `prd-operator.md`, `prd-curator.md`). +Each agent's Source Documents section references this file instead of +restating these rules. + +## Tool Access + +Read-only PRD files. No edits. No shell or network access — except Guard, +which alone may run `gh issue view ` (read-only, no writes) for +Feature Traceability Grounding. No other exceptions for any agent. + +## Domain Ownership + +Each finding belongs to exactly one agent. If a topic below isn't your +row, don't flag it — the owning agent will. + +| Domain | Owner | +|---|---| +| Persona/ID/workflow consistency, feature traceability, scope/value coherence | Guard | +| Auth boundaries, credential scope, trust model, ambiguity hiding a security decision | Adversary | +| Behavioral language, testability, AC quality, technology-naming-as-implementation-choice | Tester | +| Deployment, environment, and connectivity assumptions | Operator | +| Evidence capture, audit trail, retention, provenance, open-question/dependency hygiene | Curator | + +This table is the single source of truth for cross-agent boundaries. Each +agent's own Out of Scope section states only the boundary cases specific +to that agent's lens — for the full ownership map, see this table. + +## Phased Process Skeleton + +Every agent's own Phased Process states only its Read & Map step (what to +build a map of — specific to that agent's lens). Steps 2 and 3 are the +same for all 5 agents and aren't restated per agent: + +- **Step 2 — Evaluate** — apply the Review Criteria against the map built + in Read & Map. +- **Step 3 — Self-Check** — re-scan draft findings against the Red Flags + list. Drop or fix any finding that matches one. + +## Evidence Discipline + +- Read every provided PRD file in full before drafting any finding. +- Ground every cited identifier — FR ID, NFR ID, AC ID, persona string, + workflow step — in text you have actually read in the cited file. +- Never assert a field is absent without having read the whole section of + the file where it would appear. +- Never fabricate a quote. `evidence` is a byte-for-byte substring of the + file `location` is scoped to — no ellipses, no paraphrase, no stitching + two passages together. + +## Severity Self-Check + +Before finalizing your findings, ask of every BLOCKER and WARNING: "would +this actually block before a senior PM approves this PRD?" If the answer +is no, downgrade or drop it. Never inflate severity to look thorough. +Never invent a finding to justify the review having run. A clean review +that returns no findings is a correct outcome, not a failure to find +something. + +## External Standard Verification + +Some findings depend on a source outside the PRD files themselves — an +external ticket, issue, or standard. Treat that citation as a claim to +verify, not a fact to assume: + +- If the external source is reachable and contradicts or fails to support + the PRD's claim, raise the finding with both the PRD text and the + external text as evidence. +- If the external source is unreachable, unauthenticated, or the fetch + fails for any reason, do not block and do not fabricate alignment. Emit + an INFO finding: "linkage to `` unverified, could not fetch issue." +- A failed fetch is never treated as proof of misalignment. Silence about + an unreachable source is not acceptable either — always emit the INFO. + +## Verdict Schema + +Your entire response is a single fenced ```json block. Nothing else — +no prose before or after it. + +```json +{ + "agent": "prd-guard", + "sections_read": ["parent.personas", "phase1.functional_requirements"], + "verdict": "APPROVE | REQUEST CHANGES", + "findings": [ + { + "severity": "BLOCKER|WARNING|INFO", + "location": "FR-003-02", + "title": "Short headline naming the defect", + "evidence": "", + "issue": "What is wrong, one sentence", + "fix": "What to change, one sentence" + } + ] +} +``` + +- `evidence` MUST be a byte-for-byte quote from the file `location` + implicitly scopes to (the PRD family file containing that ID) — no + ellipses, no paraphrase, no stitching two passages together. +- A clean review returns `"verdict": "APPROVE", "findings": []` — never + manufacture findings to justify review effort. +- `verdict` here is per-agent and informational only. The council-level + verdict (BLOCKED / NEEDS REVISION / APPROVED) is computed separately by + `SKILL.md` from the aggregated severities of all agents' surviving + findings. + +## Output + +Entire response is the fenced json block above, with `"agent"` set to +your agent id (`prd-guard`, `prd-adversary`, `prd-tester`, `prd-operator`, +or `prd-curator`). No prose outside it. diff --git a/schema/prd.cue b/schema/prd.cue new file mode 100644 index 0000000..873b391 --- /dev/null +++ b/schema/prd.cue @@ -0,0 +1,109 @@ +package prds + +#SchemaVersion: =~"^[0-9]+\\.[0-9]+\\.[0-9]+$" +#Date: =~"^[0-9]{4}-[0-9]{2}-[0-9]{2}$" + +#StakeholderRole: "Product Owner" | "Requestor" | "Stakeholder Representative" | "Technical Owner" | "Technical Owner Understudy" + +#Stakeholder: { + role: #StakeholderRole + handle: string & !="" + approver: bool | *false +} + +#PRDHeader: { + "schema-version": #SchemaVersion + version: #SchemaVersion + "last-updated": #Date + parent?: string & =~"^[a-z][a-z0-9-]*$" +} + +#WorkflowStep: { + label: string & !="" + description: string & !="" + implements: [...string] & [_, ...] +} + +#Workflow: { + label: string & !="" + steps: [...#WorkflowStep] & [_, ...] +} + +#AcceptanceCriteria: { + id: string & =~"^AC-[A-Z]+-\\d{3}-\\d{2}$" + description: string & !="" +} + +#FunctionalRequirement: { + id: string & =~"^FR-[A-Z]+-\\d{3}$" + title: string & !="" + persona: string & !="" + acceptance_criteria: [...#AcceptanceCriteria] & [_, ...] +} + +#NonFunctionalRequirement: { + id: string & =~"^NFR-[A-Z]+-\\d{3}$" + title: string & !="" + description: string & !="" +} + +#OpenQuestion: { + question: string & !="" + context?: string +} + +#KPI: { + metric: string & !="" + target: string & !="" // qualitative acceptable in Draft + baseline?: string +} + +#Dependency: { + description: string & !="" + blocking: bool | *false + context?: string +} + +#State: { + status: "Draft" | "Review" | "Approved" | "Superseded" + remarks?: string +} + +#PRDDocument: { + header: #PRDHeader + slug?: string & =~"^[a-z][a-z0-9-]*$" + + // Root-level fields + stakeholders?: [...#Stakeholder] & [_, ...] + title?: string & !="" + description?: string & !="" + features?: [...string] & [_, ...] + personas?: [...string] & [_, ...] + scope?: { + in_scope: [...string] + out_of_scope: [...string] + } + nonfunctional_requirements?: [...#NonFunctionalRequirement] & [_, ...] + kpis?: [...#KPI] & [_, ...] + + // Phase-level fields + phase?: string & !="" + state?: #State + workflow?: #Workflow + functional_requirements?: [...#FunctionalRequirement] & [_, ...] + dependencies?: [...#Dependency] & [_, ...] + + open_questions?: [...#OpenQuestion] + + // A document is either a phase (has `phase`) or a parent (does not). + // Each shape has its own required fields — `header` alone is not a + // valid document either way. + if phase != _|_ { + workflow: #Workflow + functional_requirements: [...#FunctionalRequirement] & [_, ...] + } + if phase == _|_ { + title: string & !="" + personas: [...string] & [_, ...] + } +} From 33b5f7c0623f8e53f2fdfaeac7c34ee5f3679d63 Mon Sep 17 00:00:00 2001 From: Jennifer Power Date: Thu, 13 Aug 2026 15:55:00 -0400 Subject: [PATCH 2/3] ci: updates publish workflow to use CUE Trusted Publishing Signed-off-by: Jennifer Power --- .github/workflows/publish-cue.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish-cue.yml b/.github/workflows/publish-cue.yml index 00c2611..49d4ceb 100644 --- a/.github/workflows/publish-cue.yml +++ b/.github/workflows/publish-cue.yml @@ -9,6 +9,7 @@ jobs: runs-on: ubuntu-latest permissions: contents: read + id-token: write steps: - name: Checkout uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 @@ -20,7 +21,7 @@ jobs: with: version: "v0.17.0" - name: Login to CUE Central Registry - run: cue login --token=${{ secrets.CUE_REG_TOKEN }} + uses: cue-labs/registry-login-action@66d40052b0206031343e17173425fa10508968d0 # v1.0.3 - name: Publish module env: REF_NAME: ${{ github.ref_name }} From 869bfe13e953daffd53e02aba6a115b16d6b99bf Mon Sep 17 00:00:00 2001 From: Jennifer Power Date: Fri, 14 Aug 2026 13:26:12 -0400 Subject: [PATCH 3/3] fix: address PR #1 review comments - schema/prd.cue: comment explaining phase discriminator conditional - examples/phase-prd.yaml: distinct slug/ID prefix from parent to avoid FR/AC namespace collision - publish-cue.yml: validate examples against schema before publish - reviewer-protocol.md: align per-agent verdict naming with council ternary (APPROVE/REQUEST_CHANGES), clarify it's not a 1:1 mapping - module/agents/prd-*.md: stop hardcoding relative path to reviewer-protocol.md, defer to SKILL_DIR-resolved path passed by the dispatching skill - README.md: fix phase range reference (Phases 2-4, not "2 - 5") Assisted-by: Claude Code Signed-off-by: Jennifer Power --- .github/workflows/publish-cue.yml | 5 +++++ README.md | 2 +- examples/phase-prd.yaml | 14 +++++++------- module/agents/prd-adversary.md | 6 +++--- module/agents/prd-curator.md | 6 +++--- module/agents/prd-guard.md | 6 +++--- module/agents/prd-operator.md | 6 +++--- module/agents/prd-tester.md | 6 +++--- .../prd-review/references/reviewer-protocol.md | 9 +++++---- schema/prd.cue | 2 ++ 10 files changed, 35 insertions(+), 27 deletions(-) diff --git a/.github/workflows/publish-cue.yml b/.github/workflows/publish-cue.yml index 49d4ceb..7feb6b9 100644 --- a/.github/workflows/publish-cue.yml +++ b/.github/workflows/publish-cue.yml @@ -20,6 +20,11 @@ jobs: uses: cue-lang/setup-cue@a93fa358375740cd8b0078f76355512b9208acb1 # v1.0.1 with: version: "v0.17.0" + - name: Validate examples + run: | + for f in examples/*.yaml; do + cue vet schema/prd.cue "$f" -d '#PRDDocument' + done - name: Login to CUE Central Registry uses: cue-labs/registry-login-action@66d40052b0206031343e17173425fa10508968d0 # v1.0.3 - name: Publish module diff --git a/README.md b/README.md index a376153..d7c6378 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ The `prd-review` skill runs in any AI coding assistant that supports Lola skills `--schema `. A structural violation (bad ID format, invalid enum, disallowed field) blocks immediately with the raw `cue vet` error — no point reviewing behavior in a file that doesn't even parse against the schema. -**Phase 2 — 5 has specialist agents** then review content and quality, by default in parallel (pass `--serial` to run them +**Phases 2 – 4 dispatch 5 specialist agents** to review content and quality, by default in parallel (pass `--serial` to run them sequentially in one context instead — slower, but roughly 1/5th the token cost, since the PRD text is read once instead of once per agent): diff --git a/examples/phase-prd.yaml b/examples/phase-prd.yaml index 237f823..ef23f36 100644 --- a/examples/phase-prd.yaml +++ b/examples/phase-prd.yaml @@ -4,7 +4,7 @@ header: last-updated: "2026-01-01" parent: my-feature -slug: my-feature +slug: my-feature-phase1 phase: "Phase 1 — Example Phase Name" stakeholders: @@ -27,14 +27,14 @@ workflow: - label: "Step One" description: "What happens in this step." implements: - - FR-MF-001 + - FR-MFP-001 functional_requirements: - - id: FR-MF-001 + - id: FR-MFP-001 title: "Example Functional Requirement" persona: "Example Persona" acceptance_criteria: - - id: AC-MF-001-01 - description: "First acceptance criterion for FR-MF-001." - - id: AC-MF-001-02 - description: "Second acceptance criterion for FR-MF-001." + - id: AC-MFP-001-01 + description: "First acceptance criterion for FR-MFP-001." + - id: AC-MFP-001-02 + description: "Second acceptance criterion for FR-MFP-001." diff --git a/module/agents/prd-adversary.md b/module/agents/prd-adversary.md index a395848..2470ed9 100644 --- a/module/agents/prd-adversary.md +++ b/module/agents/prd-adversary.md @@ -19,9 +19,9 @@ what's actually enforced? ## Source Documents -PRD family file paths (parent + phase YAML) provided in the delegation -prompt, plus -`../skills/prd-review/references/reviewer-protocol.md`. +PRD family file paths (parent + phase YAML) and the resolved +`reviewer-protocol.md` path, both provided in the delegation prompt by +the dispatching `prd-review` skill (see its `$SKILL_DIR` convention). ## Phased Process diff --git a/module/agents/prd-curator.md b/module/agents/prd-curator.md index b3bd051..e9665b7 100644 --- a/module/agents/prd-curator.md +++ b/module/agents/prd-curator.md @@ -20,9 +20,9 @@ left dangling rather than closed out. ## Source Documents -PRD family file paths (parent + phase YAML) provided in the delegation -prompt, plus -`../skills/prd-review/references/reviewer-protocol.md`. +PRD family file paths (parent + phase YAML) and the resolved +`reviewer-protocol.md` path, both provided in the delegation prompt by +the dispatching `prd-review` skill (see its `$SKILL_DIR` convention). ## Phased Process diff --git a/module/agents/prd-guard.md b/module/agents/prd-guard.md index 74f30f0..63abbb2 100644 --- a/module/agents/prd-guard.md +++ b/module/agents/prd-guard.md @@ -21,9 +21,9 @@ persona. ## Source Documents -PRD family file paths (parent + phase YAML) provided in the delegation -prompt, plus -`../skills/prd-review/references/reviewer-protocol.md`. +PRD family file paths (parent + phase YAML) and the resolved +`reviewer-protocol.md` path, both provided in the delegation prompt by +the dispatching `prd-review` skill (see its `$SKILL_DIR` convention). ## Phased Process diff --git a/module/agents/prd-operator.md b/module/agents/prd-operator.md index 14b089f..96b96a7 100644 --- a/module/agents/prd-operator.md +++ b/module/agents/prd-operator.md @@ -18,9 +18,9 @@ deployment? ## Source Documents -PRD family file paths (parent + phase YAML) provided in the delegation -prompt, plus -`../skills/prd-review/references/reviewer-protocol.md`. +PRD family file paths (parent + phase YAML) and the resolved +`reviewer-protocol.md` path, both provided in the delegation prompt by +the dispatching `prd-review` skill (see its `$SKILL_DIR` convention). ## Phased Process diff --git a/module/agents/prd-tester.md b/module/agents/prd-tester.md index 45afb53..67a58ad 100644 --- a/module/agents/prd-tester.md +++ b/module/agents/prd-tester.md @@ -19,9 +19,9 @@ external compatibility constraint the business already committed to ## Source Documents -PRD family file paths (parent + phase YAML) provided in the delegation -prompt, plus -`../skills/prd-review/references/reviewer-protocol.md`. +PRD family file paths (parent + phase YAML) and the resolved +`reviewer-protocol.md` path, both provided in the delegation prompt by +the dispatching `prd-review` skill (see its `$SKILL_DIR` convention). ## Phased Process diff --git a/module/skills/prd-review/references/reviewer-protocol.md b/module/skills/prd-review/references/reviewer-protocol.md index 2238401..a41dce4 100644 --- a/module/skills/prd-review/references/reviewer-protocol.md +++ b/module/skills/prd-review/references/reviewer-protocol.md @@ -83,7 +83,7 @@ no prose before or after it. { "agent": "prd-guard", "sections_read": ["parent.personas", "phase1.functional_requirements"], - "verdict": "APPROVE | REQUEST CHANGES", + "verdict": "APPROVE | REQUEST_CHANGES", "findings": [ { "severity": "BLOCKER|WARNING|INFO", @@ -102,9 +102,10 @@ no prose before or after it. ellipses, no paraphrase, no stitching two passages together. - A clean review returns `"verdict": "APPROVE", "findings": []` — never manufacture findings to justify review effort. -- `verdict` here is per-agent and informational only. The council-level - verdict (BLOCKED / NEEDS REVISION / APPROVED) is computed separately by - `SKILL.md` from the aggregated severities of all agents' surviving +- `verdict` here is per-agent and informational only — it does not map + 1:1 onto the council-level ternary. The council-level verdict (BLOCKED + / NEEDS REVISION / APPROVED) is computed separately by `SKILL.md` from + the aggregated severities of all agents' surviving findings. ## Output diff --git a/schema/prd.cue b/schema/prd.cue index 873b391..94adf90 100644 --- a/schema/prd.cue +++ b/schema/prd.cue @@ -98,6 +98,8 @@ package prds // A document is either a phase (has `phase`) or a parent (does not). // Each shape has its own required fields — `header` alone is not a // valid document either way. + // Discriminate parent vs phase document shape: `!= _|_` means the + // field is set (not bottom/undefined). if phase != _|_ { workflow: #Workflow functional_requirements: [...#FunctionalRequirement] & [_, ...]