skills/prd-workflow/SKILL.md
PRD mode workflow for Builder. Use when building features from PRDs, implementing user stories, or managing PRD state transitions. Triggers on: PRD mode, build PRD, implement stories, ship PRD.
npx skillsauth add mdmagnuson-creator/yo-go prd-workflowInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Load this skill when: building features from PRDs, implementing user stories, managing PRD state transitions.
⛔ CRITICAL: Check
git.autoCommitsetting before ANY commit operationTrigger: Before running
git commitat any step in this workflow.Check:
project.json→git.autoCommit
- If
true(default): Proceed with commits normally- If
false: NEVER rungit commit— failure to comply violates project constraintWhen autoCommit is disabled, replace commit steps with:
- Stage changes:
git add -A(or specific files)- Report what would be committed:
📋 READY TO COMMIT (manual commit required) Staged files: [list of files] Suggested commit message: feat: [summary from PRD] Run: git commit -m "feat: [summary from PRD]"- Do NOT run
git commit— wait for user to commit manually- Continue workflow only after user confirms commit was made
This applies to ALL commit steps in this workflow (Step 3, story commits, etc.)
PRD mode implements features defined in docs/prds/prd-[name].json. It operates in four phases:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ CLAIM PHASE │ ──► │ BUILD PHASE │ ──► │ SHIP PHASE │ ──► │ CLEANUP PHASE │
│ │ │ │ │ │ │ │
│ Check conflicts │ │ Implement each │ │ Run tests, PR, │ │ Archive PRD, │
│ setup branch │ │ story in order │ │ merge queue │ │ generate script │
└─────────────────┘ └─────────────────┘ └─────────────────┘ └─────────────────┘
Use OpenCode right-panel todos as the live checklist, derived from session.json → chunks[].
US-001, US-002, ...).in_progress; keep remaining chunks pending.completed.session.json drives the right-panel todo state.session.json → chunks[] before continuing.⛔ After completing a story, you MUST update its status in the PRD JSON file.
Failure behavior: If you find yourself about to commit code for a completed story without first updating
docs/prd.jsonwithstatus: "completed",completedAt, andpasses: true— STOP and update the story status before committing.
After each story completes:
Update the story in docs/prd.json:
{
"id": "US-001",
"status": "completed",
"completedAt": "2026-02-28T10:30:00Z",
"passes": true,
"notes": "Implemented with React component, added unit tests"
}
Update the PRD-level status in docs/prd-registry.json:
status: "in_progress"status: "pr_open" (or later states)currentStory to the next pending story (or null if done)Include these updates in the story commit (or subsequent housekeeping commit)
Why this matters:
completedAt timestamps provide implementation timelinePRDs go through 5 states:
┌───────┐ ┌───────┐ ┌─────────────┐ ┌─────────┐ ┌───────────┐
│ draft │ ──▶ │ ready │ ──▶ │ in_progress │ ──▶ │ pr_open │ ──▶ │ completed │
└───────┘ └───────┘ └─────────────┘ └─────────┘ └───────────┘
│ │
│ (skip for │
└─────────small PRDs)─────────┘
| State | Meaning |
|-------|---------|
| draft | PRD exists but not ready for implementation |
| ready | PRD approved, waiting to be picked up |
| in_progress | Implementation actively happening |
| pr_open | PR created, awaiting review/merge |
| completed | PR merged, Playwright tests passed (or skipped via testVerifySettings), work done |
If you encounter PRDs with legacy states, migrate them automatically:
committed → in_progress (work not yet pushed)pushed → in_progress (no PR yet)merged → completed (legacy state)awaiting_e2e → completed (legacy state — E2E deferral removed)Resolve git behavior from docs/project.json:
agents.gitWorkflowtrunk, resolve agents.trunkMode (default: branchless)git.defaultBranch (fallback: main)Rules:
trunk + branchless:
branchName as metadata onlytrunk + pr-based or non-trunk workflows:
When user selects a PRD to build:
docs/prd-registry.json for conflictsWith and conflictRiskBefore copying the PRD to the working location, inspect credential metadata in docs/prds/prd-[name].json:
credentialRequirements[] (if present).requestTiming: "upfront", ask for credential readiness before starting story execution.deferred and continue only with stories that do not depend on it.session.json under the session-level credentials field with pending|provided|deferred.If no credential requirements are listed, continue normally.
Copy docs/prds/prd-[name].json to docs/prd.json. This is where @developer reads the current work.
Update docs/prd-registry.json:
status: "in_progress"startedAt: <now>currentStory as work progressesCreate/update entry in docs/session-locks.json:
{
"sessions": [
{
"sessionId": "builder-abc123",
"prdId": "prd-error-logging",
"currentStory": "US-001",
"status": "in_progress",
"startedAt": "2026-02-19T16:30:00Z",
"heartbeat": "2026-02-19T16:30:00Z"
}
]
}
If trunk + branchless:
git checkout <default-branch>
If not branchless trunk mode, create branch from PRD branchName:
git checkout -b <branchName> <default-branch>
Before story execution begins, ensure architecture automation assets exist and are current:
docs/architecture/bounded-contexts.mddocs/architecture/contexts/*.mdsession.json under session-level architecture field.Default policy:
guardrails.strictness: standardboundedContexts.policy: strictOnly prompt users for policy overrides, not routine generation/refresh.
Before starting story execution, show the story list and offer the flow chart option:
═══════════════════════════════════════════════════════════════════════
STARTING PRD EXECUTION
═══════════════════════════════════════════════════════════════════════
PRD: {prd-title}
Stories: {total count}
US-001: {description}
US-002: {description}
US-003: {description}
...
Pipeline per story: implement → test-flow → commit → postChangeActions
[G] Go — start executing stories
[F] Show implementation flow chart
> _
═══════════════════════════════════════════════════════════════════════
When user selects [F], show the implementation flow chart (same format as adhoc-workflow → Step 0.2a) adapted to the PRD's stories using US-XXX prefixes. After viewing, return to this dashboard.
ℹ️ The flow chart format and adaptation rules are defined in
adhoc-workflow→ Step 0.2a. Both modes use the same pipeline (seebuilder.md→ Story Processing Pipeline).
For each story in priority order:
📚 SKILL: test-flow → "Skip Gate → Activity Resolution → Quality Check Pipeline"
Load the
test-flowskill for the complete quality check pipeline that runs after every story completion. It includes skip-gate logic, activity resolution, typecheck/lint/test/rebuild/critic/Playwright, and the completion prompt. No prompts, no skipping — test-flow runs automatically.PRD-specific context to pass:
mode: "prd"— 5-attempt Playwright retry strategy (vs ad-hoc's 3-attempt)storyIdandprdIdfromsession.json→currentChunkand session metadatastoryChangedFilesfor story-scoped Playwright test selectionPRD-specific behaviors (defined in test-flow):
- Story-scoped Playwright: Tests are scoped to changed files + 1-hop consumers (not full suite)
- 5-attempt retry: On Playwright failure, retry up to 5 times with progressive fix delegation
- Skip and log: After 5 failures, skip Playwright and log to the chunk's
playwrightSkipsinchunk.json, then continue to next story- Playwright install check: One-time per session check, cached in
builder-config.jsonFailure behavior: Steps 1-4 (typecheck/lint/test/critic): max 3 attempts, then STOP and report to user. Step 5 (Playwright): max 5 attempts, then skip and log, continue to next story.
📚 SKILL: test-ui-verification → "Verification Flow"
Load the
test-ui-verificationskill for the full UI verification flow including:
- Playwright verification execution and status handling
- Skip patterns (docs, config, test files, CI/CD, non-UI extensions)
- Override mechanism (user can force-verify or force-skip with reason)
- Verification status:
verified,unverified(BLOCKS completion),skipped(logs to test-debt.json)PRD-specific behavior: If verification status is
unverified, the story remainsin_progressuntil verified or explicitly skipped. Skipped verifications are logged totest-debt.json.
After all checks pass, Builder continues to the next story (or shows completion prompt if E2E is offered).
Generate verification contract BEFORE delegation:
Before delegating to @developer, generate a verification contract (see builder.md → "Verification Contracts"):
Contract generation for story US-003:
1. Parse story description for advisory/skip patterns
2. Identify expected file changes from story context
3. Generate criteria based on file patterns
4. Store contract in the current `chunk.json` → `verification.contract`
Include contract in specialist prompt:
When delegating to @developer, include the verification contract in a human-readable format:
## Verification Contract
Your work will be verified by:
1. **Typecheck** — No type errors
2. **Lint** — No lint errors
3. **Unit tests** — Tests for [component/module name] must pass
4. **E2E test** — [If applicable] Page behavior test (runs immediately/deferred)
Write your implementation knowing these criteria will be checked.
This gives the specialist clear targets and helps them write testable code.
0.5. Credential gate before implementation:
requiredCredentials[] (if present).pending/deferred, prompt at the story boundary.requestTiming: "after-initial-build", this is the first required prompt point.Run the workflow steps from workflows.prd:
For each step in workflows.prd:
Execute the step
If step fails:
Attempt to fix (run @developer with error context)
If still fails after 2 attempts:
Report and ask user
Post-change actions run automatically after commit (see builder.md → Story Processing Pipeline → Step 4.5, and test-flow → Section 5.5):
postChangeActions in project.json defines downstream propagation (support articles, AI tools, marketing pages, pending updates)postChangeActions determine what runsUpdate heartbeat periodically in session lock
Update story todo state (BEFORE commit):
in_progress via todowrite and update session.jsoncompleted in both placesHandle developer failures:
docs/prd.json with clarifications if needed⚠️ Quality checks (including postChangeWorkflow pipeline) already ran above. This step handles additional E2E test generation and deferral.
Use test-flow as the canonical source for all test behavior.
session.json (chunks[] for current chunk).test-flow for any remaining E2E handling.test-flow for:
chunk.json updates for queued testssession.json to completed and continue.⛔ CRITICAL: Update state files BEFORE committing so they are included in the commit.
State updates that happen after the commit will be lost if the session ends.
Failure behavior: If you find yourself about to run
git commitwithout first updatingdocs/prd.json(story status +passes: true),session.json,chunk.json, anddocs/prd-registry.json— STOP and update those files before committing.
After a story completes and post-story checks pass:
1. Update all state files FIRST:
docs/prd.json — update the completed story:
status: "completed"completedAt: <ISO timestamp>passes: truenotes with brief completion summary (e.g., "Implemented with 3 components, added unit tests")session.json + chunk.json:
completed in session.jsoncurrentChunk to next chunk (or null if done)chunk.json already records verification resultscompleteddocs/prd-registry.json — update currentStory field and increment completed count2. Then commit (including state files):
git.autoCommit)feat: [prd-summary] (US-00X)# Verify state files are staged before committing
git add -A # includes prd.json, session.json, chunk.json, prd-registry.json
git status # confirm state files are in staged changes
git commit -m "feat: [summary from PRD] (US-00X)"
Why this order matters: If you commit before updating state, and the session ends (crash, rate limit, context compaction), the committed code and PRD state will be out of sync — git will show all stories implemented, but passes: false everywhere.
Continue Steps 1-2 for each story until all are complete.
After each story, detect boundary-impacting changes and refresh docs/guardrails as needed:
session.json (session-level architecture.boundaryChanges[]).When to run @critic depends on the configured criticMode:
| Mode | When Critic Runs |
|------|------------------|
| strict | After every story |
| balanced | After story 2, then every 3 stories (5, 8, 11...) |
| fast | Only at PRD completion |
Configuration cascade (highest priority first):
--critic-mode=strictproject.json → agents.criticModebalancedℹ️ Note: Rigor profiles are deprecated. Critic mode is now configured directly in
project.jsonor via CLI flag. The system automatically determines which critics to run based on file changes (see test-flow skill for activity resolution).
Balanced mode details:
fast (one critic run at end)After all stories are complete, ship the work:
Use commands from docs/project.json:
# Example - actual commands come from project.json
# CI=true prevents watch mode for test runners
npm run typecheck && CI=true npm run test && npm run build
Run guardrail validation in the same pre-ship gate path:
# Example commands; use project-defined equivalents when available
npm run lint:architecture || npm run guardrails:check
If violations exist:
If boundary-impacting changes were detected during the PRD:
docs/architecture/bounded-contexts.mddocs/architecture/contexts/*.md if usedFirst, gather all queued E2E tests from completed chunks' chunk.json files:
pendingTests.e2e.generated[]If no E2E tests are queued (for example, testing.autoGenerate: false and no manually added tests), skip this step and continue to Step 2.5.
Start dev server if needed (see Dev Server Management).
Run queued E2E execution and failure handling using test-flow retry semantics (max attempts, @developer fix loop, and stop conditions).
Check project.json → testing.qualityChecks:
{
"testing": {
"qualityChecks": true // default: false
}
}
If qualityChecks: true:
Run @quality-critic with context:
Run @quality-critic with:
devServerUrl: http://localhost:{devPort} # Get devPort from ~/.config/opencode/projects.json
changedFiles: [files changed in this PRD/session]
mode: comprehensive // for PRD completion
@quality-critic will check:
Handle quality check results:
Commit all remaining changes:
If per-story commits are enabled, ensure there are no uncommitted changes before this step.
git add -A
git commit -m "feat: [summary from PRD]"
Before final completion/archival, generate:
docs/completed/[prd-id]/completion-report.mdSupport report modes:
detailed (default)compact (if configured in project config)Required report sections:
Always reference this report path in final Builder completion output.
⚓ AGENTS.md: Git Completion Workflow
This step follows the canonical Git Completion Workflow defined in AGENTS.md. Both PRD mode and ad-hoc mode use the same workflow for consistency.
4a. Validate Configuration (Fail Fast)
Read project.json → git.agentWorkflow:
{
"git": {
"agentWorkflow": {
"pushTo": "staging",
"createPrTo": "main",
"requiresHumanApproval": ["main", "production"]
}
}
}
If git.agentWorkflow is not defined: STOP with Missing Config Error (see AGENTS.md).
4b. Push to Configured Branch
Push to the pushTo branch:
git push origin {git.agentWorkflow.pushTo}
4c. Prompt for PR Creation
If createPrTo differs from pushTo, prompt the user:
═══════════════════════════════════════════════════════════════════════
PUSH COMPLETE
═══════════════════════════════════════════════════════════════════════
✅ PRD "{prd-name}" pushed to origin/{pushTo}
Your workflow is configured to create PRs to '{createPrTo}'.
[P] Create PR to {createPrTo}
[S] Stay on {pushTo} (no PR yet)
> _
═══════════════════════════════════════════════════════════════════════
If createPrTo equals pushTo, skip PR creation (work is already on target branch).
Create the PR:
gh pr create --base {createPrTo} --title "feat: {description from prd.json}" --body "{PR body with story list}"
Update registry status to pr_open:
status: "pr_open"prNumber and prUrl from gh pr create outputCheck if target branch requires human approval:
| createPrTo in requiresHumanApproval? | Action |
|------------------------------------------|--------|
| Yes | Report "✅ PR #{number} created. Human approval required to merge." |
| No | Proceed to merge handling (Step 6) |
If createPrTo NOT in requiresHumanApproval:
Check docs/project.json → agents.mergeQueue.enabled (default: true).
If merge queue is enabled:
Read ~/.config/opencode/merge-queue.json
Get list of files changed in this branch:
git diff --name-only origin/<defaultBranch>...HEAD
Check for conflict risk with existing queue entries
Create queue entry:
{
"id": "entry-<random>",
"projectId": "<current-project-id>",
"prdId": "<prd-id or null>",
"branch": "<branch-name>",
"prNumber": <pr-number>,
"prUrl": "<pr-url>",
"status": "queued",
"priority": "<from prd priority or 'normal'>",
"queuedAt": "<now>",
"sessionId": "<session-id>",
"filesChanged": ["<list of changed files>"],
"conflictRisk": ["<conflicting entry IDs>"]
}
Add to queue array and save merge-queue.json
Report: "PR #{number} added to merge queue."
If merge queue is disabled:
Merge immediately after CI passes (or call @felix for CI wait).
Report the final state:
| Outcome | Message |
|---------|---------|
| Pushed only (no PR) | "Changes pushed to {pushTo}. Create PR when ready. Status: in_progress" |
| PR created, awaiting human | "PR #{number} created. Human approval required to merge. Status: pr_open" |
| PR created and merged | "PR #{number} merged to {createPrTo}. Status: completed (pending cleanup)" |
| PR created, in queue | "PR #{number} added to merge queue. Status: pr_open" |
Update session lock to appropriate status based on outcome.
When Builder starts, check for PRs that need cleanup:
Read docs/prd-registry.json for PRDs with status pr_open.
For each:
gh pr view <PR-NUMBER> --json stateIf state: "MERGED":
Automatic PRD Completion Playwright Execution:
Check testVerifySettings.prdUIVerify_PRDCompletionTest (default: true if absent):
true → Run holistic Playwright tests automatically covering the full PRD's changes. Fix loop continues until pass (max 20 attempts per issue). On exhaustion, STOP and report:
⛔ PLAYWRIGHT FIX LOOP EXHAUSTED
Attempted 20 fixes for the following issue without success:
[issue description]
Files affected: [list]
Last error: [error summary]
false → Skip silently with: ⏭️ Skipping PRD completion Playwright tests: testVerifySettings.prdUIVerify_PRDCompletionTest is falseAfter Playwright passes (or is skipped), proceed automatically to step 2.
Generate human testing script (see template below)
Archive the PRD (with rolling window):
docs/completed/[prd-id]/human-testing-script.md to archive folderPRD completes
│
▼
Count completed PRDs in registry
│
├─── <5 completed ──► Add to completed[], done
│
└─── 5 completed ──► Archive oldest to archived[], add new to completed[]
(maintains rolling window of 5)
Rolling window steps:
completed array lengthcompleted.length >= 5:
completedAt)archived arrayarchiveStats (totalArchived++, update dates)completed array:
status: "completed", completedAt: <now>e2eSkipped: true and e2eSkipReason: "user-requested"Example jq command for rolling window:
# Move oldest from completed to archived when completing prd-123
jq --arg id "prd-123" --arg now "$(date -u +%Y-%m-%dT%H:%M:%SZ)" '
# Find the entry to add
.prds |= map(if .id == $id then . + {status: "completed", completedAt: $now} else . end)
| .newEntry = (.prds[] | select(.id == $id))
| .prds |= map(select(.id != $id))
# Handle rolling window
| if (.completed | length) >= 5 then
.oldest = (.completed | sort_by(.completedAt) | first)
| .archived = ((.archived // []) + [.oldest])
| .completed = (.completed | map(select(.id != .oldest.id)))
| .archiveStats.totalArchived = ((.archiveStats.totalArchived // 0) + 1)
| .archiveStats.newestArchivedAt = .oldest.completedAt
| if .archiveStats.oldestArchivedAt == null then .archiveStats.oldestArchivedAt = .oldest.completedAt else . end
| del(.oldest)
else . end
# Add new entry
| .completed += [.newEntry]
| del(.newEntry)
' docs/prd-registry.json > docs/prd-registry.json.tmp && mv docs/prd-registry.json.tmp docs/prd-registry.json
Clear E2E queue from chunk files:
pendingTests.e2e from each chunk's chunk.jsondeferredTo flagℹ️ No top-level verification reset needed. Per-chunk verification isolation in
chunk.jsonmeans each chunk owns its ownverification.contractandverification.results. When a new task starts, it gets a fresh chunk with clean verification state — no cross-contamination is possible.
Run @prd-impact-analyzer:
Remove session lock from docs/session-locks.json
Report and offer to open:
✅ Cleaned up merged PRD: [prd-name]
📋 Human testing script ready:
docs/completed/[prd-id]/human-testing-script.md
Would you like me to open it? (y/n)
state: "OPEN": Keep current state, report: "⏳ PR still open"state: "CLOSED" (not merged): Warn and ask user what to doin_progress with no heartbeat for > 1 hour → warn: "PRD [name] may be abandoned (no heartbeat)"pr_open for > 24 hours → suggest checking PR statusIf queued entries exist for this project, show queue status and offer to process.
If user makes an ad-hoc request while a PRD is checked out:
Determine if it's PRD-related:
For unrelated ad-hoc requests:
workflows.adhoc stepsdocs/prd.json during ad-hoc workExample:
[Working on prd-error-logging]
User: "Oh also, can you fix the typo in the footer?"
Builder: "That's outside the current PRD scope. I'll handle it as ad-hoc.
Running ad-hoc workflow..."
[Runs adhoc workflow, generates adhoc-2026-02-20-fix-footer-typo.md]
Builder: "✅ Fixed footer typo.
📋 Ad-hoc report: docs/completed/adhoc/adhoc-2026-02-20-fix-footer-typo.md
Continuing with prd-error-logging..."
When archiving a completed PRD, generate human-testing-script.md:
Audience: Non-technical PMs and QA testers.
# Testing Script: [Feature Name]
**Feature:** [Human-readable feature name]
**Completed:** [Date]
**Tested by:** _________________
**Test date:** _________________
---
## What Was Built
[2-3 sentences describing what the user can now do]
---
## Before You Start
- [ ] Make sure you're logged into the application
- [ ] [Any setup needed]
---
## Test Scenarios
### Scenario 1: [User goal]
**Starting point:** [Where the user begins]
**Steps:**
1. [Action in plain language]
2. [Next action]
3. [Next action]
**What should happen:**
- [Expected outcome]
**Result:** ☐ Pass ☐ Fail
---
## Edge Cases to Try
| Try this | Expected behavior |
|----------|-------------------|
| [Edge case] | [What should happen] |
---
## Things That Should Still Work
- [ ] [Related feature 1]
- [ ] [Related feature 2]
---
## Final Check
- [ ] All scenarios passed
- [ ] Edge cases behave correctly
- [ ] Existing features still work
**Overall result:** ☐ Ready to ship ☐ Needs fixes
Users can request full PRD history with "show PRD history" or similar phrases.
Trigger phrases:
Response format:
═══════════════════════════════════════════════════════════════════════
PRD HISTORY
═══════════════════════════════════════════════════════════════════════
Recent (last 5):
✅ prd-error-logging Completed: 2026-03-02
✅ prd-user-profile Completed: 2026-02-28
✅ prd-auth-flow Completed: 2026-02-25
✅ prd-dashboard Completed: 2026-02-20
✅ prd-settings Completed: 2026-02-15
Archived (15 total):
📦 prd-onboarding Completed: 2026-02-10
📦 prd-notifications Completed: 2026-02-05
📦 prd-search Completed: 2026-01-30
... (12 more)
View full archive: jq '.archived' docs/prd-registry.json
View PRD details: cat docs/completed/[prd-id]/prd-[id].json
═══════════════════════════════════════════════════════════════════════
Implementation:
# Show recent completed (from active registry)
jq -r '.completed[] | " ✅ \(.id | ljust(25)) Completed: \(.completedAt[:10])"' docs/prd-registry.json
# Show archived summary
ARCHIVED_COUNT=$(jq '.archiveStats.totalArchived // (.archived | length) // 0' docs/prd-registry.json)
echo "Archived ($ARCHIVED_COUNT total):"
jq -r '.archived[:3][] | " 📦 \(.id | ljust(25)) Completed: \(.completedAt[:10])"' docs/prd-registry.json
if [ "$ARCHIVED_COUNT" -gt 3 ]; then
echo " ... ($((ARCHIVED_COUNT - 3)) more)"
fi
This keeps startup reads minimal (only completed array) while allowing full history access on demand.
data-ai
Generate verification contracts before delegating tasks to sub-agents, defining how success will be measured. Triggers on: verification contract, delegation contract, task verification, contract-first delegation.
testing
Verify that Vercel environment variables point to the correct Supabase project for each environment to prevent staging/production cross-wiring. Triggers on: vercel supabase check, environment alignment, env var check, supabase environment.
development
Manage codebase and database vectorization for semantic search. Use when initializing, refreshing, or querying the vector index. Triggers on: vectorize init, vectorize refresh, vectorize search, semantic search, vector index, enable vectorization.
testing
Patterns for XCUITest UI tests for native Apple apps (macOS/iOS). Use when writing or reviewing XCUITest tests for Swift apps. Triggers on: XCUITest, xcuitest, native app testing, Apple UI tests, SwiftUI tests, AppKit tests, UIKit tests.