diff --git a/.claude/hooks/PostToolUse.sh b/.claude/hooks/PostToolUse.sh index bdf57066..d032574a 100755 --- a/.claude/hooks/PostToolUse.sh +++ b/.claude/hooks/PostToolUse.sh @@ -1,10 +1,25 @@ #!/bin/bash -# PostToolUse hook - runs after any tool use -echo "πŸ”” PostToolUse hook triggered" >> /tmp/hook_debug.log -date >> /tmp/hook_debug.log - -# Sync Beads after git commit -if [[ "$@" == *"git commit"* ]]; then - echo "πŸ“¦ Syncing Beads after commit..." >> /tmp/hook_debug.log - bd sync || true +# PostToolUse hook - runs after any tool use. +# +# Claude Code passes tool-use metadata via argv; we detect git-commit calls +# and sync the beads transport so the dolt mirror stays fresh. +set -euo pipefail + +# Resolve repo root (two levels up from .claude/hooks/) so the hook works +# regardless of the caller's cwd. +HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${HOOK_DIR}/../.." && pwd)" + +LOG_FILE="${REPO_ROOT}/.claude/hooks/PostToolUse.log" +mkdir -p "$(dirname "${LOG_FILE}")" + +{ + echo "πŸ”” PostToolUse hook triggered" + date +} >> "${LOG_FILE}" + +# Sync beads after git commit (use $* so word-array joins cleanly for glob match). +if [[ "$*" == *"git commit"* ]]; then + echo "πŸ“¦ Syncing Beads after commit..." >> "${LOG_FILE}" + "${REPO_ROOT}/scripts/beads_transport.sh" export >> "${LOG_FILE}" 2>&1 || true fi diff --git a/.claude/patterns/git-safety.md b/.claude/patterns/git-safety.md index 4d456a4d..500e64b2 100644 --- a/.claude/patterns/git-safety.md +++ b/.claude/patterns/git-safety.md @@ -29,10 +29,9 @@ git stash list ## Branch Convention ``` -feature/-description # New feature -bugfix/sdp-xxx-description # Bug fix (P1/P2) -hotfix/sdp-xxx-description # Emergency fix (P0) -docs/description # Documentation only +feature/FXXX-short-name # New feature (e.g. feature/F004-sequential-reconciler) +fix/FXXX-description # Bug fix within a feature +docs/topic # Documentation only ``` ## Commit Convention @@ -47,8 +46,7 @@ refactor: extract common validation logic ## PR Rules -- Target `dev` branch (NOT `main`) for features -- Target `main` only for releases +- Target `main` branch for all PRs - Include test plan in PR body - Wait for CI before merge diff --git a/.claude/patterns/session-complete.md b/.claude/patterns/session-complete.md index e33228f0..3a82f7e5 100644 --- a/.claude/patterns/session-complete.md +++ b/.claude/patterns/session-complete.md @@ -39,7 +39,7 @@ git commit -m "type: description" bd close # Sync with remote -bd sync +scripts/beads_transport.sh export ``` ### 4. Push @@ -71,7 +71,7 @@ git fetch --prune | Mistake | Consequence | Fix | |---------|-------------|-----| | Forgot `bd close` | Issue stays open | Run `bd close` | -| Forgot `bd sync` | Remote out of sync | Run `bd sync` | +| Forgot `scripts/beads_transport.sh export` | Remote out of sync | Run `scripts/beads_transport.sh export` | | Forgot `git push` | Work not saved | Run `git push` | | Forgot `git status` check | Unknown state | Always verify | diff --git a/.claude/patterns/tdd.md b/.claude/patterns/tdd.md index 50181f8e..9741b5b4 100644 --- a/.claude/patterns/tdd.md +++ b/.claude/patterns/tdd.md @@ -61,4 +61,4 @@ go tool cover -func=coverage.out | grep total - `@build` - Executes workstream with TDD enforcement - `@bugfix` - Bug fixes follow TDD -- `.claude/skills/tdd.md` - Full skill definition +- `prompts/skills/tdd/SKILL.md` - Full skill definition diff --git a/.codex/AGENTS.md b/.codex/AGENTS.md new file mode 100644 index 00000000..61f562ef --- /dev/null +++ b/.codex/AGENTS.md @@ -0,0 +1,77 @@ +# SDP Codex Instructions + +You are operating in a repository that uses Spec-Driven Protocol (SDP). +SDP is a structured workflow for AI-assisted software development: +explicit scope, workstreams, quality gates, review, and evidence before ship. + +## Quick Start + +Read these in order: + +1. `AGENTS.md` +2. `docs/reference/project-map.md` +3. `prompts/commands.yml` +4. `docs/reference/FALLBACK_MODE.md` if your Codex runtime cannot spawn subagents + +## Main Commands + +### Planning and analysis + +- `@vision` β€” strategic product shaping +- `@feature` β€” feature planning +- `@idea` β€” requirements gathering +- `@design` β€” workstream design +- `@understand`, `@scout`, `@architect`, `@reality`, `@metrics` β€” repo analysis + +### Execution + +- `@build` β€” execute one scoped workstream +- `@oneshot` β€” end-to-end feature execution +- `@operate` / `@deploy` β€” release and operations work + +### Bugs and review + +- `@fix`, `@bugfix`, `@hotfix`, `@issue`, `@debug` +- `@review`, `@verify-workstream`, `@ci-triage` + +### Coordination + +- `@llm-council` β€” multi-model synthesis for hard decisions +- `@git-worktree` β€” safe parallel work setup +- `@parallel-dispatch` β€” parallel subagent delegation + +## Quality Gates + +Run the relevant gates before claiming a task is complete: + +| Language | Build | Test | Lint | +|---|---|---|---| +| Go | `go build ./...` | `go test ./...` | `go vet ./...` | +| Python | `pip install .` | `pytest` | `ruff check .` | +| Node.js | `npm run build` | `npm test` | `npm run lint` | +| Rust | `cargo build` | `cargo test` | `cargo clippy` | +| Java | `mvn compile` | `mvn test` | `mvn checkstyle:check` | + +## Operating Rules + +- No code change without a clear scope. +- Prefer TDD for behavior changes. +- Do not hide broken assumptions. Call them out and resolve them. +- Use `prompts/commands.yml` as the canonical command mapping. +- Use `prompts/skills/` as the canonical skill source. + +## Landing The Plane + +Before ending a session: + +1. Run the relevant quality gates. +2. Verify acceptance criteria with evidence. +3. Update docs if behavior or UX changed. +4. Commit and push from a harness that has git access if your Codex sandbox does not. + +## Related Files + +- `prompts/commands.yml` +- `prompts/skills/` +- `prompts/agents/` +- `docs/reference/FALLBACK_MODE.md` diff --git a/.codex/INSTALL.md b/.codex/INSTALL.md index 47f93331..af81f9d2 100644 --- a/.codex/INSTALL.md +++ b/.codex/INSTALL.md @@ -1,47 +1,29 @@ -# SDP β€” Codex setup +# SDP β€” Codex Setup -This project uses [Spec-Driven Protocol (SDP)](https://github.com/fall-out-bug/sdp). Codex reads this file for setup. +This repository ships Codex-oriented guidance and compatibility files. -## Project-level skills +## Start Here -Project skills source of truth lives in `prompts/skills/` (this repo). Tool folders (`.codex`, `.claude`, `.cursor`, `.opencode`) use symlinks to this source. +1. Read [`AGENTS.md`](../AGENTS.md) +2. Read [`.codex/AGENTS.md`](AGENTS.md) in this directory for Codex-specific guidance +3. Use [`prompts/commands.yml`](../prompts/commands.yml) as the canonical command map -## Quick start +## Canonical Prompt Sources -1. Install SDP into your project with Codex integration: - ```bash - SDP_IDE=codex curl -sSL https://raw.githubusercontent.com/fall-out-bug/sdp/main/install.sh | sh - ``` -2. Run project init: - ```bash - sdp init --auto - ``` -3. Use `@build 00-XXX-YY` or `sdp plan`, `sdp apply`, `sdp log trace` per [CLAUDE.md](../CLAUDE.md). +- `prompts/commands/` +- `prompts/skills/` +- `prompts/agents/` -If you want the CLI only, use: +Tool folders such as `.codex/`, `.cursor/`, and `.opencode/` are harness entry +points and lightweight adapters. Prompt logic belongs in `prompts/`. -```bash -curl -sSL https://raw.githubusercontent.com/fall-out-bug/sdp/main/install.sh | sh -s -- --binary-only -``` - -## Directory layout +## Typical Flow +```text +@feature "description" +@build 00-XXX-YY +@review FXXX ``` -.codex/ -β”œβ”€β”€ INSTALL.md # This file (read by Codex) -β”œβ”€β”€ agents/ # Project-level agent symlink -└── skills/ - β”œβ”€β”€ README.md - └── sdp/ # Project-level skills sourced from prompts/skills - -~/.codex/ -└── skills/ # User-level skills (persistent) -``` - -## Beads (optional) - -If Beads is installed (`bd --version`), use `bd ready`, `bd update`, `bd close` for task tracking. See [AGENTS.md](../AGENTS.md). - -## Updates -Rerun the same installer command to refresh the vendored `sdp/` checkout and managed Codex links after upstream changes. +If your Codex runtime cannot spawn subagents, use the manual workflows in +[`docs/reference/FALLBACK_MODE.md`](../docs/reference/FALLBACK_MODE.md). diff --git a/.codex/skills/README.md b/.codex/skills/README.md index 05174fa7..87252bb2 100644 --- a/.codex/skills/README.md +++ b/.codex/skills/README.md @@ -1,9 +1,17 @@ -# Project-level skills (Codex) +# Project-Level Skills (Codex) -SDP project skills are defined in `prompts/skills/` (source of truth). This folder contains symlinks for Codex compatibility. +SDP project skills are defined in `prompts/skills/`. -- **@build** β€” Execute workstream (TDD, guard). See `prompts/skills/build/SKILL.md`. -- **@design** β€” Plan workstreams. See `prompts/skills/design/SKILL.md`. -- **@review** β€” Multi-agent quality review. See `prompts/skills/review/SKILL.md`. +This directory exists only for Codex-facing compatibility and discovery. +The source of truth is still: -Full list: `prompts/skills/` (build, design, feature, guard, oneshot, review, tdd, etc.). +- `prompts/skills/` +- `prompts/commands.yml` +- `prompts/agents/` + +Start with: + +- `@feature` +- `@build` +- `@review` +- `@fix` diff --git a/.cursor/README.md b/.cursor/README.md index b499275e..8b9bc955 100644 --- a/.cursor/README.md +++ b/.cursor/README.md @@ -1,27 +1,43 @@ # Cursor Integration -**Canonical skill source:** `../prompts/skills/` +**Primary context source:** [`.cursorrules`](../.cursorrules) -All SDP command prompts are unified in `prompts/skills/` with symlink adapters in tool folders. +Cursor reads `.cursorrules` automatically at the project root. Keep that file +small and operational: role, decision tree, quality gates, and where to find +canonical prompts. + +## Canonical Prompt Source + +- Commands: `../prompts/commands/` +- Skills: `../prompts/skills/` +- Agents: `../prompts/agents/` +- Command mapping: `../prompts/commands.yml` + +Edit canonical prompt sources only. Do not fork Cursor-only copies of prompt +logic unless the harness format itself requires it. ## Usage -Use `@` prefix to invoke skills: - -```bash -@feature "description" # Unified entry point -@idea "description" # Requirements gathering -@design {id} # Plan workstreams -@build {ws-id} # Execute workstream -@review {feature} # Quality review -@deploy {feature} # Production deployment -@debug "issue" # Systematic debugging -@hotfix "critical" # Emergency fix -@bugfix "issue" # Quality fix +Use `@` commands for the main SDP flows: + +```text +@feature "description" +@idea "description" +@design 00-XXX-YY +@build 00-XXX-YY +@review FXXX +@fix "regression description" +@operate "deploy or release task" ``` +## Fallback Mode + +If your Cursor runtime cannot spawn subagents, use the manual checklists in +[`docs/reference/FALLBACK_MODE.md`](../docs/reference/FALLBACK_MODE.md). + ## See Also -- [CLAUDE.md](../CLAUDE.md) - Full protocol -- [prompts/skills/](../prompts/skills/) - Canonical skill definitions -- [.claude/skills/](../.claude/skills/) - Claude compatibility symlink +- [`AGENTS.md`](../AGENTS.md) +- [`docs/reference/project-map.md`](../docs/reference/project-map.md) +- [`docs/reference/FALLBACK_MODE.md`](../docs/reference/FALLBACK_MODE.md) +- [`prompts/commands.yml`](../prompts/commands.yml) diff --git a/.cursor/worktrees.json b/.cursor/worktrees.json index de37937e..40dd3005 100644 --- a/.cursor/worktrees.json +++ b/.cursor/worktrees.json @@ -1,11 +1,9 @@ { "setup_commands_unix": [ - "cd sdp-plugin && go mod download 2>/dev/null || echo 'Go modules not configured'", - "echo 'βœ… Worktree ready (Go project)'" + "echo 'Worktree ready (SDP project)'" ], "setup_commands_windows": [ - "cd sdp-plugin && go mod download 2>nul || echo Go modules not configured", - "echo Worktree ready (Go project)" + "echo Worktree ready (SDP project)" ], - "description": "Template for SDP project worktree setup. Go-first project." + "description": "Template for SDP project worktree setup." } diff --git a/.cursorrules b/.cursorrules index e28a0d59..ce2ce0ad 100644 --- a/.cursorrules +++ b/.cursorrules @@ -1,58 +1,60 @@ -# SDP Project Rules - -This project uses **Spec-Driven Protocol (SDP)** v0.9.0. - -## Beads (SDP Development) - -**This repo develops SDP.** Beads is ENABLED and MANDATORY here: - -- Before `@build`: `bd update {beads_id} --status in_progress` -- After `@build`: `bd sync` before commit -- After `@design`: `sdp beads migrate docs/workstreams/backlog/ --real` - -For other projects using SDP: Beads is optional (skills check `bd` + `.beads/`). - -## Commands - -Use skills for all work: -- `@vision` β€” Strategic product planning -- `@reality` β€” Codebase analysis -- `@feature` β€” Plan feature (idea + design) -- `@idea` β€” Gather requirements -- `@design` β€” Plan workstreams -- `@build` β€” Execute workstream (guard enforced) -- `@oneshot` β€” Autonomous feature execution -- `@review` β€” Quality review -- `@deploy` β€” Production deployment - -## Guard Enforcement - -All edits require active workstream: - -```bash -sdp guard activate {PP-FFF-SS} # Before editing -sdp guard check {file} # Verify allowed -``` - -## Critical Rules - -**Forbidden:** -- Files > 200 LOC -- TODO without WS -- Edit without active WS - -**Required:** -- TDD (Red -> Green -> Refactor) -- Coverage >= 80% -- Type hints / strict typing -- Conventional commits - -## Documentation - -- [PROTOCOL.md](docs/PROTOCOL.md) β€” Full specification -- [Skills](.claude/skills/) β€” Command details -- [Quality Gates](docs/reference/quality-gates.md) - ---- - -**Version:** 0.9.0 +# SDP Cursor Rules + +You are working in a repository that uses Spec-Driven Protocol (SDP). +SDP is a structured AI-assisted development workflow: explicit scope, +workstreams, quality gates, evidence, and review before ship. + +## Decision Tree + +- New product or unclear scope: + use `@vision`, `@feature`, `@idea`, `@design` +- Existing codebase analysis: + use `@understand`, `@scout`, `@architect`, `@reality`, `@metrics` +- Execute scoped work: + use `@build` for one workstream, `@oneshot` for end-to-end execution +- Bugs and regressions: + use `@fix`, `@bugfix`, `@hotfix`, `@issue`, `@debug` +- Review and verification: + use `@review`, `@verify-workstream`, `@ci-triage` +- Release and operations: + use `@operate`, `@deploy` +- Complex decisions: + use `@llm-council` +- Parallel work: + use `@git-worktree`, `@parallel-dispatch` + +## Working Rules + +- Do not change code without an explicit scope: feature, workstream, or bounded bug. +- Prefer TDD for behavior changes: Red -> Green -> Refactor. +- Before claiming success, run the relevant build, test, and lint gates. +- If your harness cannot spawn subagents, follow `docs/reference/FALLBACK_MODE.md`. +- Canonical command mapping lives in `prompts/commands.yml`. +- Canonical prompt sources live in: + - `prompts/commands/` + - `prompts/skills/` + - `prompts/agents/` + +## Quality Gates + +| Language | Build | Test | Lint | +|---|---|---|---| +| Go | `go build ./...` | `go test ./...` | `go vet ./...` | +| Python | `pip install .` | `pytest` | `ruff check .` | +| Node.js | `npm run build` | `npm test` | `npm run lint` | +| Rust | `cargo build` | `cargo test` | `cargo clippy` | +| Java | `mvn compile` | `mvn test` | `mvn checkstyle:check` | + +## Start Here + +1. Read `AGENTS.md` +2. Read `docs/reference/project-map.md` +3. If you are working without subagents, read `docs/reference/FALLBACK_MODE.md` +4. Use `prompts/commands.yml` when you need the command-to-skill map + +## Critical Constraints + +- No hidden technical debt +- No silent workaround when root cause is fixable +- No β€œdone” without verification evidence +- No stale prompt drift: edit canonical files in `prompts/` diff --git a/.opencode/README.md b/.opencode/README.md index bb4d83c0..93d1dc5d 100644 --- a/.opencode/README.md +++ b/.opencode/README.md @@ -2,33 +2,34 @@ This directory contains SDP integration for OpenCode. -## Setup +## Prompt Surface -SDP skills and agents are available via symlinks: -- `skills/` β†’ All SDP skills (@vision, @build, @review, etc.) -- `agents/` β†’ Agent definitions (orchestrator, reviewer, etc.) +- Skills: `prompts/skills/` +- Commands: `prompts/commands/` +- Agents: `prompts/agents/` +- Canonical command map: `prompts/commands.yml` -Primary agent cards shown in OpenCode are configured in `.opencode/opencode.json`. -Agent metadata (name/description/prompt body) comes from files in `prompts/agents/*.md` via the `agents/` symlink. -Each agent file must have valid closed YAML frontmatter so descriptions render in UI. +## Hook Surface -## Usage - -Skills work the same as in Claude Code: +OpenCode scope enforcement lives in: -``` -@vision "your product" # Strategic planning -@feature "add feature" # Plan feature -@build 00-001-01 # Execute workstream -@review F01 # Quality check -``` +- `.opencode/hooks/pre-tool-use.json` +- `.opencode/hooks/README.md` -## Commands +The current hook implementation uses `sdp-omc-guard`, which is a stronger +wrapper around SDP guard semantics for edit and write operations. -If your tool supports slash commands, create command files pointing to skills: +## Usage +```text +@vision "product" +@feature "add feature" +@build 00-XXX-YY +@review FXXX +@operate "deploy task" ``` -/oneshot β†’ skills/oneshot/SKILL.md -/build β†’ skills/build/SKILL.md -/review β†’ skills/review/SKILL.md -``` + +## Fallback Mode + +If your OpenCode runtime cannot spawn subagents, follow the manual checklists in +[`docs/reference/FALLBACK_MODE.md`](../docs/reference/FALLBACK_MODE.md). diff --git a/.opencode/hooks/README.md b/.opencode/hooks/README.md new file mode 100644 index 00000000..9fd72dae --- /dev/null +++ b/.opencode/hooks/README.md @@ -0,0 +1,73 @@ +# SDP OpenCode Integration + +This directory contains configuration for integrating SDP with OpenCode (OhMyOpenCode). + +## OpenCode-First Approach + +SDP is now **OpenCode-first**. Configuration lives in `.opencode/` directory: +- `.opencode/hooks/` - Hook configurations (this directory) +- `.opencode/agents` - Symlink to `prompts/agents` +- `.opencode/commands` - Symlink to `prompts/commands` + +Legacy Claude config (`.claude/`) is maintained for compatibility but OpenCode is the primary environment. + +## sdp-omc-guard + +Pre-tool-call guard that enforces scope before edit/write operations. + +### Usage + +```bash +# Build +go build ./cmd/sdp-omc-guard + +# Run manually (for testing) +echo '{"tool_name":"edit","tool_input":{"file_path":"test.go"},"cwd":"."}' | \ + sdp-omc-guard --ws 00-059-01 --session-id test-001 + +# Exit codes: +# 0 = allow (files in scope) +# 1 = ask (not used) +# 2 = deny (files out of scope) +``` + +### OpenCode Hook Configuration + +Add to `~/.config/opencode/opencode.json` or use the pre-configured `.opencode/hooks/pre-tool-use.json`: + +```json +{ + "hooks": { + "PreToolUse": [ + { + "matcher": "edit*", + "hooks": [ + { + "type": "command", + "command": "sdp-omc-guard --ws ${SDP_WORKSTREAM} --emit-evidence" + } + ] + } + ] + } +} +``` + +### Environment Variables + +- `SDP_WORKSTREAM`: Workstream ID (e.g., `00-059-01`). Required for scope checking. +- `SDP_SESSION_ID`: Session ID for evidence logging. + +## sdp-ready + +Ready queue bridge for Beads issue tracking. + +```bash +# Build +go build ./cmd/sdp-ready + +# Run +sdp ready # Text output +sdp ready --format json # JSON output +sdp ready --no-cache # Skip cache +``` diff --git a/.opencode/hooks/pre-tool-use.json b/.opencode/hooks/pre-tool-use.json new file mode 100644 index 00000000..5f67bb9b --- /dev/null +++ b/.opencode/hooks/pre-tool-use.json @@ -0,0 +1,33 @@ +# OhMyOpenCode PreToolUse Hook Configuration +# +# This hook runs sdp-omc-guard before edit/write operations to enforce scope. +# The guard reads PreToolUseInput from stdin and returns: +# exit 0 = allow +# exit 1 = ask (not currently used) +# exit 2 = deny (block with error message) +# +# Installation: +# 1. Build sdp-omc-guard: go build ./cmd/sdp-omc-guard +# 2. Install to PATH or update the command path below +# 3. Add this configuration to your OhMyOpenCode settings + +[ + { + "matcher": "edit*", + "hooks": [ + { + "type": "command", + "command": "sdp-omc-guard --ws ${SDP_WORKSTREAM:-00-000-00} --emit-evidence" + } + ] + }, + { + "matcher": "write", + "hooks": [ + { + "type": "command", + "command": "sdp-omc-guard --ws ${SDP_WORKSTREAM:-00-000-00} --emit-evidence" + } + ] + } +] diff --git a/docs/reference/FALLBACK_MODE.md b/docs/reference/FALLBACK_MODE.md new file mode 100644 index 00000000..0cb27022 --- /dev/null +++ b/docs/reference/FALLBACK_MODE.md @@ -0,0 +1,144 @@ +# Fallback Mode + +Fallback mode is the manual path for harnesses that cannot spawn subagents. + +The goal is not β€œapproximate the workflow.” The goal is to preserve the same +outputs and review discipline, just sequentially instead of in parallel. + +## When To Use It + +Use fallback mode when the harness cannot reliably dispatch subagents for: + +- `@review` +- `@vision` +- `@reality` +- `@build` +- `@feature` + +If native subagent spawning works, do not use fallback mode. + +## General Workflow + +1. Confirm that subagent spawning is unavailable or unreliable. +2. Pick the skill you need. +3. Execute the matching manual checklist below. +4. Produce the same artifacts you would expect from the normal path. +5. Verify outputs against the relevant schemas and acceptance criteria. + +## `@review` Fallback + +**Normal mode:** parallel specialist review. +**Fallback expectation:** 7 sections, in this order: + +1. QA +2. Security +3. DevOps +4. SRE +5. Tech Lead +6. Docs +7. Verdict + +### Checklist + +1. Read the workstream or PR scope. +2. For each role, identify the top risks, collect evidence, and assign severity. +3. Record findings with concrete file and line references where possible. +4. Produce a verdict section that summarizes blocking vs non-blocking findings. +5. If your repo uses review verdict artifacts, update `.sdp/review_verdict.json`. + +### Minimum output + +- 6 specialist sections plus 1 verdict section +- explicit blocking vs non-blocking call +- evidence for each serious finding + +## `@vision` Fallback + +**Normal mode:** analyst + architect + product-manager. +**Fallback expectation:** one structured strategy pass. + +### Checklist + +1. Ask the minimum clarifying questions: problem, user, success metric, MVP scope. +2. Write short sections for: + - product + - market + - technical feasibility + - UX constraints + - business value + - delivery risk +3. Produce the intended planning artifacts for the repo: + - product vision + - PRD or feature brief + - roadmap delta if needed + +## `@reality` Fallback + +**Normal mode:** analyst + architect. +**Fallback expectation:** one evidence-backed repo audit. + +### Checklist + +1. Detect project type and main tech stack. +2. Check architecture shape, tests, docs, TODO/HACK debt, and obvious risk areas. +3. Report: + - current state + - top issues + - quick wins + - mismatch between docs and code +4. If a vision or roadmap exists, compare intended state vs actual state. + +## `@build` Fallback + +**Normal mode:** implementer + spec-reviewer + quality-reviewer. +**Fallback expectation:** sequential TDD plus self-review. + +### Checklist + +1. Confirm the scope is executable. If the task is not scoped, stop and clarify. +2. Perform TDD: + - RED: add or update a failing test + - GREEN: implement the smallest fix + - REFACTOR: clean up without changing behavior +3. Review acceptance criteria one by one and attach evidence. +4. Run the relevant quality gates. +5. If the repo uses workstream verdict artifacts, update `.sdp/ws-verdicts/.json`. + +### Required outputs + +- code change +- test evidence +- AC evidence +- gate results + +## `@feature` Fallback + +**Normal mode:** analyst + architect + planner. +**Fallback expectation:** sequential discovery and decomposition. + +### Checklist + +1. Confirm the problem, outcome, and non-goals. +2. Decide whether the work needs: + - full discovery + - direct workstream design + - roadmap extraction +3. Produce scoped workstreams with: + - goal + - scope files + - acceptance criteria + - dependencies +4. If the repo uses issue tracking, create or link the executable units. + +## Command Mapping + +The canonical command mapping for fallback-capable harnesses lives in: + +- [`prompts/commands.yml`](../../prompts/commands.yml) + +## Rule Of Thumb + +Fallback mode is slower, not lower quality. + +If the manual checklist cannot produce the same artifact quality as the normal +path, stop and say so instead of pretending the harness supports the workflow. diff --git a/hooks/build-precheck.sh b/hooks/build-precheck.sh new file mode 100755 index 00000000..58f620ac --- /dev/null +++ b/hooks/build-precheck.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# F142-07: pre-build precheck β€” refuse to /build a workstream without a ws file. +# +# Usage: scripts/hooks/build-precheck.sh +# WS-ID example: 00-141-02 or 00-082-01 +# +# Exit codes: +# 0 ws file exists and is not status=design-pending β†’ proceed +# 1 ws file missing OR design-pending β†’ block /build with a clear message +# 2 bad usage +# +# Rule: no workstream β†’ no execution. Aligns with: +# - scripts/deliver-pick.sh (refuses to pick leafless features) +# - sdp-guard --ws (errors when ws scope cannot be parsed) +# - sdp doctor backlog (CI gate) + +set -uo pipefail + +if [[ $# -lt 1 ]]; then + echo "usage: build-precheck.sh " >&2 + exit 2 +fi + +WS_ID="$1" +REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +WS_FILE="${REPO_ROOT}/docs/workstreams/backlog/${WS_ID}.md" + +if [[ ! -f "$WS_FILE" ]]; then + cat >&2 < to find the right scope, then back-fill the scaffold + under docs/workstreams/backlog/${WS_ID}.md before re-running /build. + +Without a ws file, the guard cannot enforce scope and the verdict cannot be validated. +EOF + exit 1 +fi + +# Read frontmatter status. Treat missing/empty as "open" (proceed). +status="$(awk ' + BEGIN { in_fm=0 } + /^---$/ { in_fm=!in_fm; next } + in_fm && /^status:/ { + sub(/^status:[[:space:]]*/, "") + print + exit + } +' "$WS_FILE")" + +if [[ "$status" == "design-pending" ]]; then + cat >&2 < to produce a real workstream first. +EOF + exit 1 +fi + +exit 0 diff --git a/hooks/commit-msg b/hooks/commit-msg new file mode 100755 index 00000000..64f76650 --- /dev/null +++ b/hooks/commit-msg @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +# Validate commit message follows conventional commit format with optional beads reference. +# Format: (): +# Types: feat, fix, refactor, test, docs, chore, ci, perf, style +# Beads reference (recommended): sdplab-xxxx in message body + +set -euo pipefail + +MSG_FILE="$1" +MSG=$(cat "$MSG_FILE") + +# Skip merge commits +if echo "$MSG" | head -1 | grep -qE '^(Merge|merge:)'; then + exit 0 +fi + +# Skip Co-Authored-By only messages (amend artifacts) +FIRST_LINE=$(echo "$MSG" | head -1) + +# Validate first line format: type(scope): description or type: description +if ! echo "$FIRST_LINE" | grep -qE '^(feat|fix|refactor|test|docs|chore|ci|perf|style)(\([a-zA-Z0-9_/-]+\))?: .{3,}'; then + echo "ERROR: commit message must follow conventional format:" + echo " (): " + echo "" + echo " Types: feat, fix, refactor, test, docs, chore, ci, perf, style" + echo " Example: feat(dispatch): add cold start routing strategy" + echo "" + echo " Got: $FIRST_LINE" + exit 1 +fi + +# Warn (not block) if no beads reference for feat/fix commits +TYPE=$(echo "$FIRST_LINE" | grep -oE '^(feat|fix)' || true) +if [ -n "$TYPE" ] && ! echo "$MSG" | grep -qiE 'sdplab-[a-z0-9]+'; then + echo "WARNING: feat/fix commit without beads issue reference (sdplab-xxxx)" + echo " Consider adding issue ID to commit body" + # Don't exit 1 β€” this is advisory +fi + +exit 0 diff --git a/hooks/commit-msg.sh b/hooks/commit-msg.sh index bcb1ec93..7c627621 100755 --- a/hooks/commit-msg.sh +++ b/hooks/commit-msg.sh @@ -1,8 +1,8 @@ #!/bin/bash -# sdp/hooks/commit-msg.sh +# scripts/hooks/commit-msg.sh # Git commit-msg hook for conventional commits validation # and agent metadata trailers for provenance. -# Install: ln -sf ../../sdp/hooks/commit-msg.sh .git/hooks/commit-msg +# Install via scripts/hooks/install-git-hooks.sh. COMMIT_MSG_FILE=$1 COMMIT_MSG=$(cat "$COMMIT_MSG_FILE") diff --git a/hooks/install-git-hooks.sh b/hooks/install-git-hooks.sh index 98f9484d..91cfe5f2 100755 --- a/hooks/install-git-hooks.sh +++ b/hooks/install-git-hooks.sh @@ -1,39 +1,25 @@ #!/bin/sh -# Install Git hooks: symlink .git/hooks/* to SDP hooks. -# Run from repo root (or from sdp/). Idempotent. -# When sdp is submodule: .git/hooks/pre-commit -> sdp/hooks/pre-commit.sh +# Install Git hooks: symlink .git/hooks/* to scripts/hooks/*.sh. +# Run from repo root. Idempotent. +# F128: hooks are native in scripts/hooks/. The sdp/ local clone is a downstream +# mirror (gitignored) and must not provide hook sources to avoid split-brain. set -e ROOT="$(git rev-parse --show-toplevel)" cd "$ROOT" -# Detect hooks source: sdp/hooks/ (when sdp submodule) or scripts/hooks/ (sdp_dev) -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -if [ -d "$ROOT/sdp" ] && [ -f "$ROOT/sdp/hooks/pre-commit.sh" ]; then - HOOKS_SRC="$ROOT/sdp/hooks" - REL_HOOKS="../../sdp/hooks" -elif [ -f "$ROOT/scripts/hooks/pre-commit.sh" ]; then - HOOKS_SRC="$ROOT/scripts/hooks" - REL_HOOKS="../../scripts/hooks" -elif [ -f "$SCRIPT_DIR/pre-commit.sh" ]; then - HOOKS_SRC="$SCRIPT_DIR" - REL_HOOKS="../../$(echo "$HOOKS_SRC" | sed "s|^$ROOT/||")" -else - echo "install-git-hooks: hooks not found (expected sdp/hooks/ or scripts/hooks/)" >&2 - exit 1 -fi - -HOOKS_DIR="$ROOT/.git/hooks" +HOOKS_DIR=".git/hooks" +SCRIPTS_DIR="scripts/hooks" mkdir -p "$HOOKS_DIR" -if [ -f "$HOOKS_SRC/pre-commit.sh" ]; then - ln -sf "$REL_HOOKS/pre-commit.sh" "$HOOKS_DIR/pre-commit" - chmod +x "$HOOKS_SRC/pre-commit.sh" +if [ -f "$SCRIPTS_DIR/pre-commit.sh" ]; then + ln -sf ../../scripts/hooks/pre-commit.sh "$HOOKS_DIR/pre-commit" + chmod +x "$SCRIPTS_DIR/pre-commit.sh" echo "Installed pre-commit" fi -if [ -f "$HOOKS_SRC/pre-push.sh" ]; then - ln -sf "$REL_HOOKS/pre-push.sh" "$HOOKS_DIR/pre-push" - chmod +x "$HOOKS_SRC/pre-push.sh" +if [ -f "$SCRIPTS_DIR/pre-push.sh" ]; then + ln -sf ../../scripts/hooks/pre-push.sh "$HOOKS_DIR/pre-push" + chmod +x "$SCRIPTS_DIR/pre-push.sh" echo "Installed pre-push" fi diff --git a/hooks/install-hooks.sh b/hooks/install-hooks.sh index 31ca0a8f..968f6bd8 100755 --- a/hooks/install-hooks.sh +++ b/hooks/install-hooks.sh @@ -11,22 +11,41 @@ echo "WARNING: This install method is deprecated." echo "Please use: sdp hooks install" echo "" -# Try to use the Go implementation if available -if command -v sdp >/dev/null 2>&1; then - echo "Using sdp CLI to install hooks..." - exec sdp hooks install +# Try to use the Go implementation if available. +# Resolve the CLI relative to the script/project, never from PATH +# (on macOS /usr/bin/sdp is an Xcode tool, not this project's CLI). +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" + +sdp_cli="" +# 1. Local build inside optional sdp/ checkout +if [ -x "${PROJECT_ROOT}/sdp/sdp-plugin/sdp" ]; then + sdp_cli="${PROJECT_ROOT}/sdp/sdp-plugin/sdp" +fi +# 2. Project's own binary (if built at project root) +if [ -z "${sdp_cli}" ] && [ -x "${PROJECT_ROOT}/sdp" ]; then + case "$(file "${PROJECT_ROOT}/sdp" 2>/dev/null)" in + *"ELF"*|*"Mach-O"*|*"PE32"*) + sdp_cli="${PROJECT_ROOT}/sdp" + ;; + esac +fi + +if [ -n "${sdp_cli}" ]; then + echo "Using sdp CLI (${sdp_cli}) to install hooks..." + exec "${sdp_cli}" hooks install fi # Fallback to manual installation if sdp is not available echo "SDP CLI not found. Using manual installation..." -HOOKS_DIR=".git/hooks" - -# Check if in git repo -if [ ! -d ".git" ]; then - echo "Error: Not in git repository root" >&2 +# Resolve hooks dir via git (works in both regular repos and worktrees +# where .git is a file, not a directory). +if ! GIT_DIR="$(git rev-parse --git-dir 2>/dev/null)"; then + echo "Error: Not in a git repository" >&2 exit 1 fi +HOOKS_DIR="${GIT_DIR}/hooks" # Create hooks directory if needed mkdir -p "${HOOKS_DIR}" diff --git a/hooks/post-bd-close-sync.sh b/hooks/post-bd-close-sync.sh new file mode 100755 index 00000000..62740e78 --- /dev/null +++ b/hooks/post-bd-close-sync.sh @@ -0,0 +1,225 @@ +#!/usr/bin/env bash +# post-bd-close-sync.sh β€” Auto-sync workstream status after bd close. +# +# Moves workstream files from backlog/ to done/ and updates INDEX.md status +# entries when a beads issue is closed. +# +# Usage: +# scripts/hooks/post-bd-close-sync.sh [ ...] +# scripts/hooks/post-bd-close-sync.sh sdplab-nai +# scripts/hooks/post-bd-close-sync.sh sdplab-nai sdplab-abc +# +# Environment: +# BD_POST_CLOSE_DRY_RUN=1 List intended changes without applying +# BD_POST_CLOSE_QUIET=1 Suppress non-error output +# +# Brownfield-safe: exits 0 silently if workstream files or directories don't exist. +set -euo pipefail + +# --- Paths --- +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" +WS_DIR="${PROJECT_ROOT}/docs/workstreams" +BACKLOG_DIR="${WS_DIR}/backlog" +DONE_DIR="${WS_DIR}/done" +INDEX_FILE="${WS_DIR}/INDEX.md" + +# --- Helpers --- +log() { + if [[ -z "${BD_POST_CLOSE_QUIET:-}" ]]; then + printf '[post-bd-close-sync] %s\n' "$*" + fi +} + +log_dry() { + printf '[post-bd-close-sync] DRY RUN: %s\n' "$*" +} + +# Extract workstream ID from a bd issue. +# Strategy: +# 1. Try bd show --json to get the title (e.g. "F124-05: ...") +# 2. Parse title for pattern like F124-05 or F124-5 (feature-step) +# 3. Convert to ws_id format 00-124-05 +# 4. Fallback: scan backlog/*.md frontmatter for a "## Beads" section mentioning the issue id +resolve_ws_id() { + local issue_id="$1" + local ws_id="" + + # Strategy 1: Extract from bd issue title via --json + if command -v bd >/dev/null 2>&1; then + local title="" + title="$(bd show "$issue_id" --json 2>/dev/null | grep -o '"title":"[^"]*"' | head -1 | sed 's/"title":"//;s/"//')" || true + if [[ -n "$title" ]]; then + # Match patterns like F124-05 or F124-5 (with or without zero-padding) + local feature_step="" + feature_step="$(printf '%s' "$title" | grep -oE 'F[0-9]+-[0-9]+' | head -1)" || true + if [[ -n "$feature_step" ]]; then + # Parse F- into 00-FFF-SS + local feature_num step_num + feature_num="$(printf '%s' "$feature_step" | sed 's/F\([0-9]*\)-.*/\1/')" + step_num="$(printf '%s' "$feature_step" | sed 's/F[0-9]*-\([0-9]*\)/\1/')" + # Remove leading zeros for printf, then re-pad + feature_num="$((10#$feature_num))" + step_num="$((10#$step_num))" + ws_id="$(printf '00-%03d-%02d' "$feature_num" "$step_num")" + fi + fi + fi + + # Strategy 2: Scan backlog frontmatter for the issue id in ## Beads section + if [[ -z "$ws_id" && -d "$BACKLOG_DIR" ]]; then + local candidate="" + for candidate in "$BACKLOG_DIR"/*.md; do + [[ -f "$candidate" ]] || continue + # Look for the beads issue ID in the Beads section of the workstream + # Match "- " or "- : ..." + if grep -q "^- ${issue_id}\(:\|$\)" "$candidate" 2>/dev/null; then + ws_id="$(basename "$candidate" .md)" + break + fi + done + fi + + printf '%s' "$ws_id" +} + +# Move a workstream file from backlog/ to done/. +# Returns 0 on success or if already done, 1 on unexpected error. +move_workstream() { + local ws_id="$1" + local src="${BACKLOG_DIR}/${ws_id}.md" + local dst="${DONE_DIR}/${ws_id}.md" + + # Already in done/ β€” idempotent success + if [[ -f "$dst" ]]; then + log "already in done/: ${ws_id}.md" + return 0 + fi + + # Not in backlog β€” nothing to do (brownfield-safe skip) + if [[ ! -f "$src" ]]; then + log "no backlog file for ${ws_id} (skipping)" + return 0 + fi + + if [[ -n "${BD_POST_CLOSE_DRY_RUN:-}" ]]; then + log_dry "would move ${src} -> ${dst}" + return 0 + fi + + # Ensure done/ directory exists + mkdir -p "$DONE_DIR" + + # Update the status field in frontmatter before moving + # Match: open, in_progress, in-progress, backlog β†’ done + if command -v sed >/dev/null 2>&1; then + sed -i.bak -E 's/^status: (open|in_progress|in-progress|backlog)$/status: done/' "$src" 2>/dev/null && rm -f "${src}.bak" || true + fi + + mv "$src" "$dst" + log "moved ${ws_id}.md -> done/" +} + +# Update the Workstream Status section in INDEX.md for a given ws_id. +# Changes the status column from Backlog/In Progress to Done. +update_index_status() { + local ws_id="$1" + + if [[ ! -f "$INDEX_FILE" ]]; then + log "INDEX.md not found (skipping status update)" + return 0 + fi + + # Find the row with this ws_id in the status tables and change status to Done + # The ws_id appears as the first column like | 00-124-05 | + local pattern="^| ${ws_id} |" + if grep -q "$pattern" "$INDEX_FILE"; then + if [[ -n "${BD_POST_CLOSE_DRY_RUN:-}" ]]; then + log_dry "would update INDEX.md status for ${ws_id} -> Done" + return 0 + fi + + # Replace Backlog or In Progress with Done on the matching row + # Using | as delimiter since rows contain pipes; escape carefully + sed -i.bak "/${pattern}/s/| Backlog |/| Done |/;/${pattern}/s/| In Progress |/| Done |/" "$INDEX_FILE" 2>/dev/null && rm -f "${INDEX_FILE}.bak" || true + log "updated INDEX.md: ${ws_id} -> Done" + else + log "${ws_id} not found in INDEX.md status table (skipping)" + fi +} + +# Update the Features table (top of INDEX.md) status column for the feature. +# When all workstreams for a feature are done, flip the feature status. +update_feature_status() { + local ws_id="$1" + + if [[ ! -f "$INDEX_FILE" ]]; then + return 0 + fi + + # Extract feature number from ws_id (00-124-05 -> F124) + local feature_num + feature_num="$(printf '%s' "$ws_id" | sed 's/00-\([0-9]*\)-.*/\1/')" + local feature_id="F${feature_num}" + + # Check if all backlog workstreams for this feature are done + local backlog_count=0 + if [[ -d "$BACKLOG_DIR" ]]; then + backlog_count="$(find "$BACKLOG_DIR" -name "00-${feature_num}-*.md" -type f 2>/dev/null | wc -l | tr -d ' ')" + fi + + if [[ "$backlog_count" -eq 0 ]]; then + # All workstreams done β€” check if feature row exists in INDEX.md + local feature_pattern="| \\*\\*${feature_id}\\*\\* |" + if grep -q "$feature_pattern" "$INDEX_FILE"; then + if [[ -n "${BD_POST_CLOSE_DRY_RUN:-}" ]]; then + log_dry "would update INDEX.md feature ${feature_id} -> Done" + return 0 + fi + + sed -i.bak "/${feature_pattern}/s/| Backlog |/| Done |/;/${feature_pattern}/s/| In Progress |/| Done |/" "$INDEX_FILE" 2>/dev/null && rm -f "${INDEX_FILE}.bak" || true + log "updated INDEX.md feature ${feature_id} -> Done (all workstreams complete)" + fi + fi +} + +# --- Main --- +main() { + if [[ $# -eq 0 ]]; then + echo 'Usage: post-bd-close-sync.sh [ ...]' >&2 + exit 2 + fi + + # Brownfield guard: if workstream directory doesn't exist, exit cleanly + if [[ ! -d "$WS_DIR" ]]; then + log "docs/workstreams/ not found (skipping)" + exit 0 + fi + + local moved=0 + local skipped=0 + + for issue_id in "$@"; do + log "processing issue: ${issue_id}" + + local ws_id + ws_id="$(resolve_ws_id "$issue_id")" + + if [[ -z "$ws_id" ]]; then + log "no workstream found for issue ${issue_id} (skipping)" + ((skipped++)) || true + continue + fi + + log "resolved ${issue_id} -> ${ws_id}" + + move_workstream "$ws_id" + update_index_status "$ws_id" + update_feature_status "$ws_id" + ((moved++)) || true + done + + log "done: ${moved} moved, ${skipped} skipped" +} + +main "$@" diff --git a/hooks/post-build.sh b/hooks/post-build.sh index a3d7f331..4cbfd167 100755 --- a/hooks/post-build.sh +++ b/hooks/post-build.sh @@ -1,15 +1,16 @@ -#!/bin/bash -# Post-build hook: quality checks after workstream execution -# Usage: ./post-build.sh WS-ID [module_path] -# Go-only: build and test sdp-plugin. - -set -e - -REPO_ROOT=$(git rev-parse --show-toplevel) -cd "$REPO_ROOT" - -if [ -d "sdp-plugin" ]; then - cd sdp-plugin - go build ./... - go test ./... -count=1 -short +#!/bin/sh +# Post-build hook for /build workflow. +# Usage: ./post-build.sh {WS-ID} [beads-status] +# On success: bd update {beads_id} --status completed +WS_ID="${1:-}" +STATUS="${2:-completed}" +if [ -n "$WS_ID" ] && command -v bd >/dev/null 2>&1; then + # Resolve beads_id from .beads-sdp-mapping.jsonl if present + if [ -f .beads-sdp-mapping.jsonl ]; then + beads_id=$(grep "\"sdp_id\": \"$WS_ID\"" .beads-sdp-mapping.jsonl 2>/dev/null | head -1 | sed 's/.*"beads_id": "\([^"]*\)".*/\1/') + if [ -n "$beads_id" ]; then + bd update "$beads_id" --status "$STATUS" 2>/dev/null || true + fi + fi fi +exit 0 diff --git a/hooks/post-codereview.sh b/hooks/post-codereview.sh index 26816fb0..2ca9d945 100755 --- a/hooks/post-codereview.sh +++ b/hooks/post-codereview.sh @@ -1,5 +1,5 @@ #!/bin/bash -# sdp/hooks/post-codereview.sh +# scripts/hooks/post-codereview.sh # Post-codereview checks for /codereview command # Usage: ./post-codereview.sh F{XX} @@ -102,7 +102,7 @@ else echo "❌ UAT Guide NOT found" echo " Expected: $UAT_FILE or $ALT_UAT_FILE" echo "" - echo " Create UAT Guide using template: sdp/templates/uat-guide.md" + echo " Create UAT Guide using template: templates/uat-guide.md" echo "" echo " To skip: SKIP_UAT_CHECK=1 ./post-codereview.sh $FEATURE" exit 1 diff --git a/hooks/post-merge.sh b/hooks/post-merge.sh index 49cbda08..c2d430f3 100755 --- a/hooks/post-merge.sh +++ b/hooks/post-merge.sh @@ -1,17 +1,20 @@ #!/bin/sh -# Post-merge hook: clear Go caches after merge operations +# Post-merge hook: clear Go caches and run doc-sync for architectural changes # Part of F063 follow-up - keep local build state aligned +# Part of sdplab-665 - auto-fix documentation on merge set -e REPO_ROOT=$(git rev-parse --show-toplevel) cd "$REPO_ROOT" -if [ "${SDP_SKIP_GO_CACHE_CLEAN:-0}" = "1" ]; then +# Skip if requested +if [ "${SDP_SKIP_POST_MERGE:-0}" = "1" ]; then exit 0 fi -if command -v go >/dev/null 2>&1; then +# Clear Go caches if requested +if [ "${SDP_SKIP_GO_CACHE_CLEAN:-0}" != "1" ] && command -v go >/dev/null 2>&1; then if go clean -cache -testcache >/dev/null 2>&1; then echo "Go build/test caches cleared" else @@ -27,4 +30,17 @@ if command -v go >/dev/null 2>&1; then ) >/dev/null 2>&1 & fi +# Run doc-sync fix for architectural changes +# This automatically fixes documentation inconsistencies when merging changes +if command -v sdp-doc-sync >/dev/null 2>&1; then + if [ "${SDP_SKIP_DOC_SYNC:-0}" != "1" ]; then + echo "Running sdp-doc-sync fix for architectural changes..." + if sdp-doc-sync --mode fix 2>&1 | grep -q "nothing to fix"; then + echo "Documentation is consistent" + else + echo "Documentation inconsistencies fixed automatically" + fi + fi +fi + exit 0 diff --git a/hooks/post-ws-complete.sh b/hooks/post-ws-complete.sh index bee64d31..d5a3b697 100755 --- a/hooks/post-ws-complete.sh +++ b/hooks/post-ws-complete.sh @@ -10,5 +10,5 @@ if [ -z "$WS_ID" ]; then exit 1 fi -echo "WS $WS_ID complete. Run: bd close --reason 'WS completed'; bd sync" +echo "WS $WS_ID complete. Run: bd close --reason 'WS completed'; scripts/beads_transport.sh export" exit 0 diff --git a/hooks/pre-build.sh b/hooks/pre-build.sh index 53d2302f..85c525fa 100755 --- a/hooks/pre-build.sh +++ b/hooks/pre-build.sh @@ -1,118 +1,10 @@ -#!/bin/bash -# sdp/hooks/pre-build.sh -# Pre-build checks for /build command -# Usage: ./pre-build.sh WS-060-01 - -set -e - -WS_ID=$1 - -if [ -z "$WS_ID" ]; then - echo "❌ Usage: ./pre-build.sh WS-ID" - exit 1 -fi - -echo "πŸ” Pre-build checks for $WS_ID" -echo "================================" - -# Find WS file (project-agnostic: auto-detect workstream dir) -REPO_ROOT=$(git rev-parse --show-toplevel) -WS_DIR="" -if [ -n "$SDP_WORKSTREAM_DIR" ] && [ -d "$REPO_ROOT/$SDP_WORKSTREAM_DIR" ]; then - WS_DIR="$SDP_WORKSTREAM_DIR" -elif [ -d "$REPO_ROOT/docs/workstreams" ]; then - WS_DIR="docs/workstreams" -elif [ -d "$REPO_ROOT/workstreams" ]; then - WS_DIR="workstreams" -elif [ -d "$REPO_ROOT/tools/hw_checker/docs/workstreams" ]; then - WS_DIR="tools/hw_checker/docs/workstreams" -fi - -WS_FILE="" -if [ -n "$WS_DIR" ]; then - WS_FILE=$(find "$REPO_ROOT/$WS_DIR" -name "${WS_ID}-*.md" 2>/dev/null | head -1) -fi - -if [ -z "$WS_FILE" ]; then - echo "❌ WS file not found: ${WS_ID}-*.md" - echo " Searched in: docs/workstreams, workstreams, SDP_WORKSTREAM_DIR" - exit 1 -fi - -echo "βœ“ Found: $WS_FILE" - -# Check 1: Goal defined -echo "" -echo "Check 1: Goal defined" -if grep -q "### 🎯 ЦСль\|### 🎯 Goal" "$WS_FILE"; then - echo "βœ“ Goal section found" -else - echo "❌ Goal section not found" - echo " Add '### 🎯 ЦСль (Goal)' section to WS file" - exit 1 -fi - -# Check 2: Acceptance Criteria -echo "" -echo "Check 2: Acceptance Criteria" -if grep -q "Acceptance Criteria" "$WS_FILE"; then - AC_COUNT=$(grep -c "\- \[ \]" "$WS_FILE" || echo "0") - echo "βœ“ Acceptance Criteria found ($AC_COUNT items)" -else - echo "❌ Acceptance Criteria not found" - echo " Add Acceptance Criteria checklist to WS file" - exit 1 -fi - -# Check 3: Scope not LARGE -echo "" -echo "Check 3: Scope check" -if grep -q "πŸ”΄.*LARGE\|LARGE.*Π ΠΠ—Π‘Π˜Π’Π¬" "$WS_FILE"; then - echo "❌ Scope is LARGE β€” split WS first" - echo " Run /design to break down into smaller WS" - exit 1 -else - echo "βœ“ Scope is acceptable" -fi - -# Check 4: Dependencies completed (if any) -echo "" -echo "Check 4: Dependencies" -DEP=$(grep -A1 "### Π—Π°Π²ΠΈΡΠΈΠΌΠΎΡΡ‚ΡŒ\|### Dependency" "$WS_FILE" | tail -1 | tr -d '[]' | xargs) - -if [ -z "$DEP" ] || [ "$DEP" = "НСзависимый" ] || [ "$DEP" = "Independent" ] || [ "$DEP" = "-" ]; then - echo "βœ“ No dependencies (independent WS)" -else - echo " Dependencies: $DEP" - # Check if dependency is completed - INDEX_FILE="$REPO_ROOT/$WS_DIR/INDEX.md" - if grep -q "$DEP.*completed\|$DEP.*βœ…" "$INDEX_FILE" 2>/dev/null; then - echo "βœ“ Dependency $DEP is completed" - else - echo "⚠️ Warning: Dependency $DEP may not be completed" - echo " Check INDEX.md to verify status" - fi -fi - -# Check 5: Input files exist -echo "" -echo "Check 5: Input files" -INPUT_FILES=$(grep -A10 "### Π’Ρ…ΠΎΠ΄Π½Ρ‹Π΅ Ρ„Π°ΠΉΠ»Ρ‹\|### Input" "$WS_FILE" | grep "^\- \`" | sed 's/.*`\([^`]*\)`.*/\1/' | head -5) - -if [ -n "$INPUT_FILES" ]; then - for FILE in $INPUT_FILES; do - if [ -f "$FILE" ]; then - echo "βœ“ $FILE exists" - else - echo "⚠️ $FILE not found (may be created during build)" - fi - done -else - echo " No input files specified" -fi - -echo "" -echo "================================" -echo "βœ… Pre-build checks PASSED" -echo "" -echo "Ready to execute: /build $WS_ID" +#!/bin/sh +# Pre-build hook for /build workflow. +# Usage: ./pre-build.sh {WS-ID} +WS_ID="${1:-}" +if [ -n "$WS_ID" ]; then + if command -v sdp >/dev/null 2>&1; then + sdp guard activate "$WS_ID" 2>/dev/null || true + fi +fi +exit 0 diff --git a/hooks/pre-commit.sh b/hooks/pre-commit.sh index 2b1b9723..278ef165 100755 --- a/hooks/pre-commit.sh +++ b/hooks/pre-commit.sh @@ -1,17 +1,14 @@ #!/bin/sh -# Pre-commit hook: go build, optional ws-verdict validation (if docs/ws-verdicts/*.json changed). +# Pre-commit hook: go build, ws-verdict schema validation (if docs/ws-verdicts/*.json changed). # CWD = repo root. Exit 1 on any failure. -# When scripts/hooks/validate-ws-verdicts.sh exists (sdp_dev), runs ws-verdict validation. set -e # 1. go build ./... -go build ./... || { echo "pre-commit: go build failed" >&2; exit 1; } +go build -tags "sqlite_fts5" ./... || { echo "pre-commit: go build failed" >&2; exit 1; } -# 2. If staged files touch docs/ws-verdicts/*.json and validate script exists β€” validate +# 2. If staged files touch docs/ws-verdicts/*.json β€” validate if git diff --cached --name-only | grep -q '^docs/ws-verdicts/.*\.json$'; then - if [ -f ./scripts/hooks/validate-ws-verdicts.sh ]; then - sh ./scripts/hooks/validate-ws-verdicts.sh || { echo "pre-commit: ws-verdict validation failed" >&2; exit 1; } - fi + sh ./scripts/hooks/validate-ws-verdicts.sh || { echo "pre-commit: ws-verdict validation failed" >&2; exit 1; } fi exit 0 diff --git a/hooks/pre-push b/hooks/pre-push new file mode 100755 index 00000000..592824db --- /dev/null +++ b/hooks/pre-push @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# Pre-push hook: validate tests pass before pushing to main. +# Only runs full validation when pushing to main/master. + +set -euo pipefail + +while read local_ref local_sha remote_ref remote_sha; do + if echo "$remote_ref" | grep -qE 'refs/heads/(main|master)$'; then + echo "Pushing to main β€” running quality gates..." + + # Build check + if ! go build ./... 2>/dev/null; then + echo "ERROR: build failed β€” fix before pushing to main" + exit 1 + fi + + # Test check + if ! go test ./... -count=1 -short 2>/dev/null; then + echo "ERROR: tests failed β€” fix before pushing to main" + exit 1 + fi + + echo "Quality gates passed." + fi +done + +exit 0 diff --git a/hooks/pre-push.sh b/hooks/pre-push.sh index 775c0ba7..018efb9a 100755 --- a/hooks/pre-push.sh +++ b/hooks/pre-push.sh @@ -4,7 +4,7 @@ set -e # 1. go test -short ./... -go test -short ./... || { echo "pre-push: go test -short failed" >&2; exit 1; } +go test -tags "sqlite_fts5" -short ./... || { echo "pre-push: go test -short failed" >&2; exit 1; } # 2. If feature branch + diff touches internal/ or cmd/: require .sdp/evidence/*.json, validate BRANCH=$(git branch --show-current) diff --git a/hooks/sdp-doctor-precommit.sh b/hooks/sdp-doctor-precommit.sh new file mode 100755 index 00000000..dcf563e8 --- /dev/null +++ b/hooks/sdp-doctor-precommit.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# F141-04: pre-commit drift gate. +# Optional install via `sdp init` (F141-03) or manually: +# ln -sf ../../scripts/hooks/sdp-doctor-precommit.sh .git/hooks/pre-commit +# +# Checks that .sdp/generated/ is in sync with sdp.manifest.yaml before every +# commit. Exits 1 if drift is detected, so the commit is aborted. +set -euo pipefail +cd "$(git rev-parse --show-toplevel)" + +if ! go run ./cmd/sdp doctor adapters; then + echo "" + echo "❌ sdp doctor: adapter drift detected." + echo " Fix: run \`sdp generate-adapters --write --out .sdp/generated\`" + echo " then \`git add .sdp/generated\` and retry the commit." + exit 1 +fi diff --git a/hooks/validate-ws-verdicts.sh b/hooks/validate-ws-verdicts.sh new file mode 100755 index 00000000..6cbc7bf1 --- /dev/null +++ b/hooks/validate-ws-verdicts.sh @@ -0,0 +1,14 @@ +#!/bin/sh +# Validate docs/ws-verdicts/*.json against schema/ws-verdict.schema.json. +# Used by post-build pipeline hook. Exits 1 if any verdict fails schema validation. +# CWD = project root (set by RunHooks). +set -e +if [ ! -f schema/ws-verdict.schema.json ]; then + echo "ws-verdict-validate: schema not found" >&2 + exit 1 +fi +if ! command -v go >/dev/null 2>&1; then + echo "ws-verdict-validate: go not found, skipping" >&2 + exit 0 +fi +go run ./cmd/sdp-ws-verdict-validate . 2>&1 || exit 1 diff --git a/hooks/validators/post-edit-check.sh b/hooks/validators/post-edit-check.sh index b616f361..45497e82 100755 --- a/hooks/validators/post-edit-check.sh +++ b/hooks/validators/post-edit-check.sh @@ -1,5 +1,5 @@ #!/bin/bash -# sdp/hooks/validators/post-edit-check.sh +# scripts/hooks/validators/post-edit-check.sh # Validates file after editing - checks TODO/FIXME and file size FILE_PATH="${1:-}" diff --git a/hooks/validators/pre-edit-check.sh b/hooks/validators/pre-edit-check.sh index 15f64bdc..762f2389 100755 --- a/hooks/validators/pre-edit-check.sh +++ b/hooks/validators/pre-edit-check.sh @@ -1,5 +1,5 @@ #!/bin/bash -# sdp/hooks/validators/pre-edit-check.sh +# scripts/hooks/validators/pre-edit-check.sh # Validates file before editing - checks Clean Architecture set -e diff --git a/hooks/validators/session-quality-check.sh b/hooks/validators/session-quality-check.sh index 3e00a29a..cab985d7 100755 --- a/hooks/validators/session-quality-check.sh +++ b/hooks/validators/session-quality-check.sh @@ -1,5 +1,5 @@ #!/bin/bash -# sdp/hooks/validators/session-quality-check.sh +# scripts/hooks/validators/session-quality-check.sh # Run at end of agent turn to check overall quality # CD to project directory diff --git a/hooks/validators/ws-sync-hook.sh b/hooks/validators/ws-sync-hook.sh index 96d06eae..bfb45893 100755 --- a/hooks/validators/ws-sync-hook.sh +++ b/hooks/validators/ws-sync-hook.sh @@ -28,7 +28,7 @@ fi echo "Syncing to GitHub: $FILE_PATH" -# Find sdp directory (hook is in sdp/hooks/validators/) +# Find sdp_lab root (hook is in scripts/hooks/validators/) SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" SDP_DIR="$(cd "$SCRIPT_DIR/../../.." && pwd)" diff --git a/hooks/verification-completion.sh b/hooks/verification-completion.sh index 657b2420..686e695b 100755 --- a/hooks/verification-completion.sh +++ b/hooks/verification-completion.sh @@ -1,5 +1,5 @@ #!/bin/bash -# sdp/hooks/verification-completion.sh +# scripts/hooks/verification-completion.sh # Verification hook for /build completion # Enforces evidence-based claims and detects red flag phrases # Usage: ./verification-completion.sh diff --git a/prompts/README.md b/prompts/README.md index 10ba2fdc..227f335e 100644 --- a/prompts/README.md +++ b/prompts/README.md @@ -14,7 +14,7 @@ Compatibility adapters are provided as symlinks: - `.cursor/agents` -> `../prompts/agents` - `.opencode/skills` -> `../prompts/skills` - `.opencode/agents` -> `../prompts/agents` -- `.codex/skills/sdp` -> `../../prompts/skills` +- `.codex/skills` -> per-skill individual symlinks (e.g. `.codex/skills/build` -> `../../prompts/skills/build`) - `.codex/agents` -> `../prompts/agents` Edit only `prompts/*` to avoid prompt drift across tools. diff --git a/prompts/agents/README.md b/prompts/agents/README.md index ad055ea7..b8ee105a 100644 --- a/prompts/agents/README.md +++ b/prompts/agents/README.md @@ -1,8 +1,7 @@ --- name: readme description: Agent index for SDP multi-agent coordination. -tools: - read: true +tools: Read --- # SDP Agent Index diff --git a/prompts/agents/architect.md b/prompts/agents/architect.md index 4e46d22c..23a43013 100644 --- a/prompts/agents/architect.md +++ b/prompts/agents/architect.md @@ -1,12 +1,7 @@ --- name: architect description: Software architect for system boundaries, design patterns, and integration tradeoffs. -tools: - read: true - bash: true - glob: true - grep: true - write: true +tools: Read, Bash, Glob, Grep, Write --- You are a Software Architect designing scalable, maintainable systems. diff --git a/prompts/agents/deployer.md b/prompts/agents/deployer.md index 8d0318d0..6a90a31b 100644 --- a/prompts/agents/deployer.md +++ b/prompts/agents/deployer.md @@ -1,13 +1,7 @@ --- name: deployer description: Deployment specialist for release readiness, merge strategy, and rollout safety checks. -tools: - read: true - bash: true - glob: true - grep: true - edit: true - write: true +tools: Read, Bash, Glob, Grep, Edit, Write --- You are a deployment automation specialist. diff --git a/prompts/agents/devops.md b/prompts/agents/devops.md index 8792f13a..ed663c3f 100644 --- a/prompts/agents/devops.md +++ b/prompts/agents/devops.md @@ -1,13 +1,7 @@ --- name: devops description: DevOps specialist for CI/CD pipelines, infrastructure automation, and release operations. -tools: - read: true - bash: true - glob: true - grep: true - edit: true - write: true +tools: Read, Bash, Glob, Grep, Edit, Write --- # DevOps Agent diff --git a/prompts/agents/implementer.md b/prompts/agents/implementer.md index 31a9f64a..25216076 100644 --- a/prompts/agents/implementer.md +++ b/prompts/agents/implementer.md @@ -1,13 +1,7 @@ --- name: implementer description: Implementation agent for executing workstreams with TDD and self-reporting. -tools: - read: true - bash: true - glob: true - grep: true - edit: true - write: true +tools: Read, Bash, Glob, Grep, Edit, Write --- # Implementer Agent diff --git a/prompts/agents/orchestrator.md b/prompts/agents/orchestrator.md index 88a129b3..518c1aa8 100644 --- a/prompts/agents/orchestrator.md +++ b/prompts/agents/orchestrator.md @@ -7,15 +7,9 @@ changes: - Added @deploy step after @review (automated deployment) - Clarified continuous execution requirement - Added explicit "When to Stop" section -tools: - read: true - bash: true - glob: true - grep: true - edit: true - write: true - Emphasized checkpoint updates are transparent - Removed ambiguity about progress reports +tools: Read, Bash, Glob, Grep, Edit, Write --- # Orchestrator Subagent @@ -153,7 +147,7 @@ When Beads is **enabled** (`bd --version` works, `.beads/` exists): bd update {beads_id} --status in_progress # Execute TDD cycle bd close {beads_id} --reason "WS completed" -bd sync +scripts/beads_transport.sh export git commit ``` diff --git a/prompts/agents/planner.md b/prompts/agents/planner.md index fcd4bc3b..b75a6b09 100644 --- a/prompts/agents/planner.md +++ b/prompts/agents/planner.md @@ -1,11 +1,7 @@ --- name: planner description: Planning specialist for workstream decomposition, dependency mapping, and scope sizing. -tools: - read: true - bash: true - glob: true - grep: true +tools: Read, Bash, Glob, Grep --- You are a planning specialist for the consensus workstream methodology. diff --git a/prompts/agents/qa.md b/prompts/agents/qa.md index be6d35bf..9e2c3c60 100644 --- a/prompts/agents/qa.md +++ b/prompts/agents/qa.md @@ -1,11 +1,7 @@ --- name: qa description: QA specialist for test strategy, quality metrics, and release quality gates. -tools: - read: true - bash: true - glob: true - grep: true +tools: Read, Bash, Glob, Grep --- # QA Agent diff --git a/prompts/agents/reviewer.md b/prompts/agents/reviewer.md index cf0bd08d..b8ce8f8d 100644 --- a/prompts/agents/reviewer.md +++ b/prompts/agents/reviewer.md @@ -1,11 +1,7 @@ --- name: reviewer description: Code reviewer for 17-point quality checks with clear approval verdicts. -tools: - read: true - bash: true - glob: true - grep: true +tools: Read, Bash, Glob, Grep --- You are a strict code review specialist for workstream quality assurance. diff --git a/prompts/agents/security.md b/prompts/agents/security.md index d36a642c..3690aa5f 100644 --- a/prompts/agents/security.md +++ b/prompts/agents/security.md @@ -1,11 +1,7 @@ --- name: security description: Security specialist for threat modeling, auth risks, and compliance controls. -tools: - read: true - bash: true - glob: true - grep: true +tools: Read, Bash, Glob, Grep --- # Security Agent diff --git a/prompts/agents/spec-reviewer.md b/prompts/agents/spec-reviewer.md index 75d5764b..f63f0a8f 100644 --- a/prompts/agents/spec-reviewer.md +++ b/prompts/agents/spec-reviewer.md @@ -1,11 +1,7 @@ --- name: spec-reviewer description: Spec reviewer for evidence-based implementation compliance against requirements. -tools: - read: true - bash: true - glob: true - grep: true +tools: Read, Bash, Glob, Grep --- # Spec Compliance Reviewer Agent diff --git a/prompts/agents/sre.md b/prompts/agents/sre.md index ee2f39da..16fa7d7e 100644 --- a/prompts/agents/sre.md +++ b/prompts/agents/sre.md @@ -1,11 +1,7 @@ --- name: sre description: SRE specialist for reliability, observability, and incident response readiness. -tools: - read: true - bash: true - glob: true - grep: true +tools: Read, Bash, Glob, Grep --- # SRE Agent diff --git a/prompts/agents/tech-lead.md b/prompts/agents/tech-lead.md index d7bab706..a14a34c9 100644 --- a/prompts/agents/tech-lead.md +++ b/prompts/agents/tech-lead.md @@ -1,11 +1,7 @@ --- name: tech-lead description: Tech lead for technical direction, code quality governance, and team coordination. -tools: - read: true - bash: true - glob: true - grep: true +tools: Read, Bash, Glob, Grep --- # Tech Lead Agent diff --git a/prompts/commands.yml b/prompts/commands.yml new file mode 100644 index 00000000..7c20dfb2 --- /dev/null +++ b/prompts/commands.yml @@ -0,0 +1,293 @@ +# Canonical LLM-agnostic command mapping for SDP. +# When the command surface changes, update this file and .claude/commands.json +# in the same PR. +--- +version: 1.1.0 +description: LLM-agnostic command to skill mapping for SDP +execution_modes: + cli_only: CLI command handles everything + llm_only: LLM spawning required (no CLI equivalent) + hybrid: CLI for setup/validation, LLM spawning for AI tasks +commands: + build: + file: ".agents/skills/build.md" + cli: sdp apply --ws + mode: hybrid + llm_subagents: + - implementer + - spec-reviewer + - quality-reviewer + description: Execute workstream with TDD + phase: execution + review: + file: ".agents/skills/review.md" + cli: + mode: llm_only + llm_subagents: + - qa + - security + - devops + - sre + - tech-lead + - documentation + description: Multi-agent quality review + phase: execution + fix: + file: ".agents/skills/fix.md" + cli: sdp apply --ws + mode: hybrid + llm_subagents: + - implementer + - quality-reviewer + description: Quality bug fix (P1/P2) - systematic mode + phase: debug + understand: + file: ".agents/skills/understand.md" + cli: sdp landscape + mode: hybrid + llm_subagents: + - analyst + - architect + description: Codebase analysis - standard mode + phase: analysis + operate: + file: ".agents/skills/operate.md" + cli: sdp deploy + mode: hybrid + llm_subagents: + - devops + - sre + - planner + description: Deployment and operations + phase: execution + ship: + file: ".agents/skills/ship.md" + cli: sdp deploy + mode: hybrid + llm_subagents: + - devops + - sre + - planner + description: Deployment orchestration - creates PR to main or merges for release + phase: execution + deploy: + file: ".agents/skills/deploy.md" + cli: sdp deploy + mode: cli_only + deprecated: true + deprecated_in_favor_of: ship + deprecation_version: "5.0.0" + removal_version: "8.0.0" + description: DEPRECATED: Deployment orchestration (legacy alias for @ship) + phase: execution + bugfix: + file: ".agents/skills/bugfix.md" + cli: sdp apply --ws + mode: hybrid + llm_subagents: + - implementer + - quality-reviewer + description: Quality bug fix (P1/P2) - legacy alias for @fix + phase: debug + hotfix: + file: ".agents/skills/hotfix.md" + cli: sdp apply --ws + mode: hybrid + llm_subagents: + - implementer + description: Emergency P0 fix - legacy alias for @fix + phase: debug + issue: + file: ".agents/skills/issue.md" + cli: bd show + mode: llm_only + llm_subagents: + - analyst + description: Bug analysis and routing - legacy alias for @fix + phase: debug + debug: + file: ".agents/skills/debug.md" + cli: + mode: llm_only + description: Systematic debugging - legacy alias for @fix + phase: debug + vision: + file: ".agents/skills/vision.md" + cli: sdp prd generate + mode: hybrid + llm_subagents: + - analyst + - architect + - product-manager + description: Strategic product planning - legacy alias for @build + phase: strategic + reality: + file: ".agents/skills/reality-check.md" + cli: sdp doctor adapters + mode: hybrid + llm_subagents: + - analyst + - architect + description: Codebase analysis - legacy alias for @review + phase: analysis + feature: + file: ".agents/skills/feature.md" + cli: sdp plan + mode: hybrid + llm_subagents: + - analyst + - architect + - planner + description: Feature planning - legacy alias for @build + phase: planning + idea: + file: ".agents/skills/idea.md" + cli: sdp idea + mode: llm_only + description: Requirements gathering - legacy alias for @build + phase: planning + design: + file: ".agents/skills/design.md" + cli: sdp design + mode: llm_only + description: System design - legacy alias for @build + phase: planning + ux: + file: ".agents/skills/ux.md" + cli: + mode: llm_only + description: UX design - legacy alias for @build + phase: planning + prototype: + file: ".agents/skills/prototype.md" + cli: sdp prototype + mode: hybrid + llm_subagents: + - implementer + description: Prototyping - legacy alias for @build + phase: execution + oneshot: + file: ".agents/skills/oneshot.md" + cli: sdp apply --ws + mode: hybrid + llm_subagents: + - implementer + - spec-reviewer + - quality-reviewer + description: Autonomous feature execution (legacy alias for @build) + phase: execution + landscape: + file: ".agents/skills/landscape.md" + cli: sdp landscape + mode: hybrid + llm_subagents: + - analyst + - architect + description: Codebase landscape analysis - legacy alias for @understand + phase: analysis + scout: + file: ".agents/skills/scout.md" + cli: sdp scout + mode: hybrid + llm_subagents: + - analyst + description: Codebase reconnaissance - legacy alias for @understand + phase: analysis + architect: + file: ".agents/skills/architect.md" + cli: sdp architect + mode: hybrid + llm_subagents: + - architect + - analyst + description: Architecture analysis - legacy alias for @understand + phase: analysis + metrics: + file: ".agents/skills/metrics.md" + cli: sdp metrics + mode: hybrid + llm_subagents: + - analyst + description: Metrics analysis - legacy alias for @understand + phase: analysis + ci-triage: + file: ".agents/skills/ci-triage.md" + cli: sdp ci-triage + mode: hybrid + llm_subagents: + - devops + - sre + description: CI/CD triage - legacy alias for @operate + phase: execution + plan: + file: ".agents/skills/plan.md" + cli: sdp plan + mode: hybrid + llm_subagents: + - planner + - analyst + description: Planning - legacy alias for @operate + phase: planning + reality-check: + file: ".agents/skills/reality-check.md" + cli: sdp reality-check + mode: hybrid + llm_subagents: + - analyst + - architect + description: Reality checking - legacy alias for @review + phase: analysis + verify-workstream: + file: ".agents/skills/verify-workstream.md" + cli: sdp verify + mode: hybrid + llm_subagents: + - qa + - quality-reviewer + description: Workstream verification - legacy alias for @review + phase: execution + git-worktree: + file: ".agents/skills/git-worktree.md" + cli: + mode: llm_only + description: Safety-first git worktree setup for parallel feature work + parallel-dispatch: + file: ".agents/skills/parallel-dispatch.md" + cli: + mode: llm_only + description: Delegate independent tasks to parallel subagent sessions + llm-council: + file: ".agents/skills/llm-council.md" + cli: + mode: llm_only + description: Multi-model synthesis for complex decisions + strataudit: + file: ".agents/skills/strataudit.md" + cli: + mode: llm_only + description: Document-backed strategy traceability audit +patterns: + tdd: patterns/tdd.md + git-safety: patterns/git-safety.md + quality-gates: patterns/quality-gates.md + session-complete: patterns/session-complete.md +agents: + orchestrator: agents/orchestrator.md + reviewer: agents/reviewer.md + builder: agents/builder.md + deployer: agents/deployer.md + tester: agents/qa.md + architect: agents/architect.md + analyst: agents/analyst.md + developer: agents/developer.md + implementer: agents/implementer.md + spec-reviewer: agents/spec-reviewer.md + security: agents/security.md + devops: agents/devops.md + sre: agents/sre.md + tech-lead: agents/tech-lead.md +notes: + llm_agnostic: Skills work with any LLM (Opus, GLM, Codex) in any tool (Claude Code, Cursor, Windsurf) + cli_first: CLI is the foundation - LLM spawning is for AI-powered tasks + hybrid_mode: CLI handles setup/validation, LLM spawning handles AI tasks + subagent_spawning: Use tool's native subagent capability (Task tool, agent panel, etc.) diff --git a/prompts/commands/bugfix.md b/prompts/commands/bugfix.md index d9e7e5b5..60065dfe 100644 --- a/prompts/commands/bugfix.md +++ b/prompts/commands/bugfix.md @@ -8,7 +8,7 @@ agent: builder When calling `/bugfix issue NNN`: 1. **Read issue** β€” Load `docs/issues/{NNN}-*.md` -2. **Create branch** β€” `git checkout -b bugfix/{NNN}-{slug}` from master +2. **Create branch** β€” `git checkout -b bugfix/{NNN}-{slug}` from main 3. **TDD cycle** β€” Write failing test β†’ implement fix β†’ refactor 4. **Quality gates** β€” run quality gates (see AGENTS.md) 5. **Commit** β€” `fix(scope): description (issue NNN)` @@ -18,7 +18,7 @@ When calling `/bugfix issue NNN`: ## CRITICAL: You MUST Complete ```bash -git checkout master +git checkout main git merge bugfix/{branch} --no-edit git push git status # MUST show "up to date with origin" @@ -34,5 +34,5 @@ git status # MUST show "up to date with origin" | Aspect | Hotfix | Bugfix | |--------|--------|--------| | Severity | P0 | P1/P2 | -| Branch from | master | master | +| Branch from | main | main | | Testing | Fast | Full | diff --git a/prompts/commands/deliver.md b/prompts/commands/deliver.md new file mode 100644 index 00000000..be103cb0 --- /dev/null +++ b/prompts/commands/deliver.md @@ -0,0 +1,29 @@ +# /deliver β€” Autonomous Feature Delivery + +Invoke `@delivery-loop` with no arguments. + +The skill handles end-to-end: + +1. Feature selection (`bd ready -n 50`, pick highest-priority epic/feature). +2. Workstream identification (cross-reference `docs/workstreams/backlog/` with beads children). +3. Claim + worktree + checkpoint bootstrap. +4. Build β†’ review β†’ fix loop (bounded). +5. PR creation (after local quality gates pass). +6. Codex review loop (bounded, stable-N exit). +7. Closeout (bead close, worktree teardown, beads transport push). + +## Recovery + +- **Resume after compaction:** `@delivery-loop --resume` +- **Abort mid-loop:** `@delivery-loop --abort` + (cleans claim, worktree, checkpoint, and lock; stashes uncommitted work) + +## Escalation policy + +Do **not** stop for routine fix/rebuild decisions. **Do** stop to escalate: +- Tests fail unrelated to feature code +- Merge conflicts +- Ambiguous findings with no clear fix strategy +- Phase-1 cap hit at cycle 5 (operator must paste deferred-P3 list into spin-out bead) + +See `.agents/skills/delivery-loop.md` for the full state machine and `docs/plans/2026-04-22-deliver-skill-review-design.md` for the design rationale. diff --git a/prompts/commands/deploy.md b/prompts/commands/deploy.md index 3d805672..6dc4c947 100644 --- a/prompts/commands/deploy.md +++ b/prompts/commands/deploy.md @@ -13,7 +13,7 @@ When calling `/deploy {feature} [version_bump]`: 4. Generate: CHANGELOG, release notes 5. **EXECUTE** (do NOT propose): - `git commit` artifacts - - `git merge feature/F{XX} β†’ master` (via PR) + - `git merge feature/F{XX} β†’ main` (via PR) - `git tag v{X.Y.Z}` - `git push origin main v{X.Y.Z}` 6. Report summary diff --git a/prompts/commands/design.md b/prompts/commands/design.md index 174510bd..6073eb42 100644 --- a/prompts/commands/design.md +++ b/prompts/commands/design.md @@ -7,7 +7,7 @@ agent: planner When calling `/design {slug}`: -1. Load full prompt: `@.claude/skills/design.md` +1. Load full prompt: `@.claude/skills/design/SKILL.md` 2. Read PROJECT_MAP.md and INDEX.md 3. Read draft: `docs/drafts/idea-{slug}.md` 4. Create all WS files in `workstreams/backlog/` diff --git a/prompts/commands/hotfix.md b/prompts/commands/hotfix.md index 4abd2612..cf1c4431 100644 --- a/prompts/commands/hotfix.md +++ b/prompts/commands/hotfix.md @@ -7,22 +7,22 @@ agent: fixer When calling `/hotfix "description" --issue-id=001`: -1. **Create branch** β€” `git checkout -b hotfix/{id}-{slug}` from master +1. **Create branch** β€” `git checkout -b hotfix/{id}-{slug}` from main 2. **Minimal fix** β€” No refactoring, fix bug only 3. **Fast testing** β€” Smoke + critical path (no full suite) 4. **Commit** β€” `fix(scope): description (issue NNN)` 5. **MERGE, TAG, PUSH** β€” Execute yourself! -6. **Backport** β€” Merge to dev and feature branches +6. **Backport** β€” Merge to feature branches 7. **Close issue** β€” Update status in issue file ## CRITICAL: You MUST Complete ```bash -# Merge to master and tag -git checkout master +# Merge to main and tag +git checkout main git merge hotfix/{branch} --no-edit git tag -a v{VERSION} -m "Hotfix: {description}" -git push origin master --tags +git push origin main --tags ``` **Work is NOT complete until all `git push` commands succeed.** diff --git a/prompts/commands/idea.md b/prompts/commands/idea.md index 7e952e44..b9391823 100644 --- a/prompts/commands/idea.md +++ b/prompts/commands/idea.md @@ -7,7 +7,7 @@ agent: analyst When calling `/idea {description}`: -1. Load full prompt: `@.claude/skills/idea.md` +1. Load full prompt: `@.claude/skills/idea/SKILL.md` 2. Execute Mandatory Initial Dialogue 3. Create draft in `docs/drafts/idea-{slug}.md` 4. Output summary for user diff --git a/prompts/commands/issue.md b/prompts/commands/issue.md index 9d5a3e6c..0d1f514b 100644 --- a/prompts/commands/issue.md +++ b/prompts/commands/issue.md @@ -7,7 +7,7 @@ agent: debugger When calling `/issue "description"`: -1. Load full prompt: `@.claude/skills/issue.md` +1. Load full prompt: `@.claude/skills/issue/SKILL.md` 2. Systematic debugging (5 phases): - Symptom analysis - Hypothesis formation diff --git a/prompts/commands/review.md b/prompts/commands/review.md index 18f35c73..d40cfcef 100644 --- a/prompts/commands/review.md +++ b/prompts/commands/review.md @@ -7,7 +7,7 @@ agent: reviewer When calling `/review {feature}`: -1. Load full prompt: `@.claude/skills/review.md` +1. Load full prompt: `@.claude/skills/review/SKILL.md` 2. Find all feature WS in INDEX.md 3. Check each WS against checklist (Check 0-11) 4. Perform cross-WS checks diff --git a/prompts/commands/ship.md b/prompts/commands/ship.md new file mode 100644 index 00000000..00c4eeb9 --- /dev/null +++ b/prompts/commands/ship.md @@ -0,0 +1,31 @@ +--- +description: Deployment orchestration from approved feature to release handoff. +agent: builder +--- + +# /ship β€” Ship Feature + +When calling `/ship {feature} [version_bump]`: + +1. Load skill: `.claude/skills/ship/SKILL.md` +2. Pre-flight: run quality gates (see AGENTS.md), verify APPROVED +3. Version: bump semver (patch/minor/major) +4. Generate: CHANGELOG, release notes +5. **EXECUTE** (do NOT propose): + - `git commit` artifacts + - `git merge feature/F{XX} β†’ main` (via PR) + - `git tag v{X.Y.Z}` + - `git push origin main v{X.Y.Z}` +6. Report summary + +## Quick Reference + +**Input:** APPROVED feature + version bump (default: patch) +**Output:** Production deployment + v{X.Y.Z} tag +**Rule:** Do NOT stop after artifacts β€” EXECUTE all git operations + +## Version Bump + +- `@ship ` β€” patch (0.5.0 β†’ 0.5.1) +- `@ship minor` β€” minor (0.5.0 β†’ 0.6.0) +- `@ship major` β€” major (0.5.0 β†’ 1.0.0) diff --git a/prompts/commands/submit-to-swarm.md b/prompts/commands/submit-to-swarm.md index bfa16342..dcd2880e 100644 --- a/prompts/commands/submit-to-swarm.md +++ b/prompts/commands/submit-to-swarm.md @@ -19,4 +19,4 @@ Calls `POST /api/v1/intake` on the Intake Gateway with: Set INTAKE_GATEWAY_URL (default http://localhost:8081) for the gateway base URL. -Example: `/swarm sdp_dev "Add user authentication"` +Example: `/swarm sdp_lab "Add user authentication"` diff --git a/prompts/skills/beads/SKILL.md b/prompts/skills/beads/SKILL.md index 1cd80078..c64524f8 100644 --- a/prompts/skills/beads/SKILL.md +++ b/prompts/skills/beads/SKILL.md @@ -16,7 +16,7 @@ Beads integration for SDP. Mapping: `.beads-sdp-mapping.jsonl` (sdp_id β†’ beads | Update status | `bd update --status completed` | | Create | `bd create --title="..." --type=task` | | Dependencies | `bd dep add ` | -| Sync | `bd sync` | +| Sync | `scripts/beads_transport.sh export` | ## Integration Points @@ -24,8 +24,18 @@ Beads integration for SDP. Mapping: `.beads-sdp-mapping.jsonl` (sdp_id β†’ beads - **@design** β€” `bd create` for new WS, `bd dep add` for dependencies - **Mapping** β€” `.beads-sdp-mapping.jsonl` links WS ID to beads ID +## Recovery + +| Symptom | Fix | +|---------|-----| +| Skill produces no output | Check working directory is project root with `docs/workstreams/backlog/` | +| "checkpoint not found" | Run `sdp-orchestrate --feature ` to create initial checkpoint | +| "workstream files missing" | Run `sdp-orchestrate --index` to verify, then `@feature` to regenerate | +| Skill hangs / no progress | Check `.sdp/log/events.jsonl` for last event; use `sdp reset --feature ` if stuck | +| Review loop exceeds 3 rounds | Use `@review --override "reason"`, `@review --partial`, or `@review --escalate` | + ## See Also - @build β€” Uses beads for dependency check - @oneshot β€” Wave execution -- AGENTS.md β€” `bd ready`, `bd show`, `bd update`, `bd close`, `bd sync` +- AGENTS.md β€” `bd ready`, `bd show`, `bd update`, `bd close`, `scripts/beads_transport.sh export` diff --git a/prompts/skills/bugfix/SKILL.md b/prompts/skills/bugfix/SKILL.md index 7de812d7..ecde1947 100644 --- a/prompts/skills/bugfix/SKILL.md +++ b/prompts/skills/bugfix/SKILL.md @@ -1,11 +1,11 @@ --- name: bugfix -description: Quality bug fixes (P1/P2). Full TDD cycle, branch from master via feature/, no production deploy. +description: Quality bug fixes (P1/P2). Full TDD cycle, branch from main via feature/, no production deploy. --- # @bugfix -Quality bug fixes with full TDD cycle. Branch from master via feature/. +Quality bug fixes with full TDD cycle. Branch from main via feature/. ## When to Use @@ -16,11 +16,11 @@ Quality bug fixes with full TDD cycle. Branch from master via feature/. ## Workflow 1. **Read issue** β€” `bd show ` or load from `docs/issues/` -2. **Branch** β€” `git checkout master && git pull && git checkout -b fix/{id}-{slug}` +2. **Branch** β€” `git checkout main && git pull && git checkout -b fix/{id}-{slug}` 3. **TDD** β€” Red: failing test β†’ Green: minimal fix β†’ Refactor 4. **Quality gates** β€” Run quality gates (see Quality Gates in AGENTS.md) 5. **Commit** β€” `git commit -m "fix(scope): description"` -6. **Push** β€” `git push -u origin fix/{branch}` then `gh pr create --base master` +6. **Push** β€” `git push -u origin fix/{branch}` then `gh pr create --base main` ## Write Plan (F101) @@ -36,7 +36,7 @@ Before modifying any file, emit a write plan: {"spec_version":"v1.0","event_id":"","timestamp":"","source":{"system":"sdp-lab","component":"bugfix"},"event_type":"decision.made","payload":{"decision_type":"write_plan","plan":[{"path":"...","action":"CREATE|MODIFY|DELETE","reason":"..."}]},"context":{"feature_id":"","workstream_id":""}} ``` Include context fields only when the ID is known at plan time. Omit unavailable fields rather than inventing placeholders. - > **Note:** Phase 1 uses prompt-level write boundaries (CLI out of scope). Aligns with `sdp/schema/contracts/orchestration-event.schema.json` via `event_type: "decision.made"`. Phase 2 CLI will emit natively. + > **Note:** Phase 1 uses prompt-level write boundaries (CLI out of scope). Aligns with `schema/contracts/orchestration-event.schema.json` via `event_type: "decision.made"`. Phase 2 CLI will emit natively. **Output format:** ``` @@ -57,6 +57,16 @@ Proceed? [y/n] Bug fixed, tests added, issue closed, changes pushed. +## Recovery + +| Symptom | Fix | +|---------|-----| +| Skill produces no output | Check working directory is project root with `docs/workstreams/backlog/` | +| "checkpoint not found" | Run `sdp-orchestrate --feature ` to create initial checkpoint | +| "workstream files missing" | Run `sdp-orchestrate --index` to verify, then `@feature` to regenerate | +| Skill hangs / no progress | Check `.sdp/log/events.jsonl` for last event; use `sdp reset --feature ` if stuck | +| Review loop exceeds 3 rounds | Use `@review --override "reason"`, `@review --partial`, or `@review --escalate` | + ## See Also - @hotfix β€” P0 emergency diff --git a/prompts/skills/build/SKILL.md b/prompts/skills/build/SKILL.md index 5847b285..953b459f 100644 --- a/prompts/skills/build/SKILL.md +++ b/prompts/skills/build/SKILL.md @@ -27,6 +27,7 @@ Continuation is the orchestrator's job (@oneshot / sdp orchestrate). ## CRITICAL RULES +0. **NO WORKSTREAM, NO BUILD** β€” `/build {WS-ID}` MUST refuse to start when `docs/workstreams/backlog/{WS-ID}.md` is missing OR declares `status: design-pending`. Run `scripts/hooks/build-precheck.sh {WS-ID}` as the very first step; exit non-zero blocks execution. This is rule F142-07; matches the picker (`scripts/deliver-pick.sh`) and `sdp doctor backlog` gates. 1. **CHECK EXISTING CODE FIRST** β€” Run `@reality --quick` or grep before starting new features. Output `existing_work_summary` in ws-verdict β€” **required**. Short summary: files/functions/risks found before implementation. 2. **ONE EXECUTABLE LEAF** β€” Execute this workstream only if it is a leaf. If the target is an aggregate/container workstream, STOP and hand control back to `@oneshot` or target a child leaf explicitly. After commit, STOP. Do not start the next WS. 3. **USE SPAWN OR DO IT YOURSELF** β€” If spawn available, use it. If not, implement manually. @@ -69,7 +70,7 @@ Before the TDD cycle (step 2 of EXECUTE THIS NOW), emit a write plan: ```json {"spec_version":"v1.0","event_id":"","timestamp":"","source":{"system":"sdp-lab","component":"build"},"event_type":"decision.made","payload":{"decision_type":"write_plan","plan":[{"path":"...","action":"CREATE|MODIFY|DELETE","reason":"..."}]},"context":{"feature_id":"","workstream_id":""}} ``` - > **Note:** Phase 1 uses prompt-level write boundaries (CLI out of scope). Aligns with `sdp/schema/contracts/orchestration-event.schema.json` via `event_type: "decision.made"`. Phase 2 CLI will emit natively. + > **Note:** Phase 1 uses prompt-level write boundaries (CLI out of scope). Aligns with `schema/contracts/orchestration-event.schema.json` via `event_type: "decision.made"`. Phase 2 CLI will emit natively. **Output format:** ``` @@ -189,7 +190,7 @@ Reason: `existing_work_summary` is required. Add one-line summary of pre-existin ``` Reason: Each ac_evidence entry must include `evidence` (file:line or test name). -Schema: `schema/ws-verdict.schema.json` (from sdp root; project: `sdp/schema/`) +Schema: `schema/ws-verdict.schema.json` --- @@ -204,6 +205,17 @@ When user invokes `/build 00-053-16..25` (or multiple WS IDs): --- +## Recovery + +| Symptom | Fix | +|---------|-----| +| Skill produces no output | Check working directory is project root with `docs/workstreams/backlog/` | +| "checkpoint not found" | Run `sdp-orchestrate --feature ` to create initial checkpoint | +| "workstream files missing" | Run `sdp-orchestrate --index` to verify, then `@feature` to regenerate | +| Skill hangs / no progress | Check `.sdp/log/events.jsonl` for last event; use `sdp reset --feature ` if stuck | +| Review loop exceeds 3 rounds | Use `@review --override "reason"`, `@review --partial`, or `@review --escalate` | +| Build fails quality gates | Run `./scripts/run_go_quality_gates.sh` locally first; fix errors before retry | + ## See Also - `@oneshot` β€” Orchestrator that invokes @build per WS diff --git a/prompts/skills/ci-triage/SKILL.md b/prompts/skills/ci-triage/SKILL.md index 58c5e34b..c8b6458a 100644 --- a/prompts/skills/ci-triage/SKILL.md +++ b/prompts/skills/ci-triage/SKILL.md @@ -59,7 +59,7 @@ Create a follow-up item when CI is not green: ```bash bd create --title="CI: " --type=bug --priority=1 bd dep add -bd sync +scripts/beads_transport.sh export ``` ## Output Template @@ -74,3 +74,13 @@ bd sync - Proposed fix: ... - Beads follow-up: ``` + +## Recovery + +| Symptom | Fix | +|---------|-----| +| Skill produces no output | Check working directory is project root with `docs/workstreams/backlog/` | +| "checkpoint not found" | Run `sdp-orchestrate --feature ` to create initial checkpoint | +| "workstream files missing" | Run `sdp-orchestrate --index` to verify, then `@feature` to regenerate | +| Skill hangs / no progress | Check `.sdp/log/events.jsonl` for last event; use `sdp reset --feature ` if stuck | +| Review loop exceeds 3 rounds | Use `@review --override "reason"`, `@review --partial`, or `@review --escalate` | diff --git a/prompts/skills/debug/SKILL.md b/prompts/skills/debug/SKILL.md index 0c8e8025..f60dc70d 100644 --- a/prompts/skills/debug/SKILL.md +++ b/prompts/skills/debug/SKILL.md @@ -98,6 +98,16 @@ When user invokes `@debug ""`: --- +## Recovery + +| Symptom | Fix | +|---------|-----| +| Skill produces no output | Check working directory is project root with `docs/workstreams/backlog/` | +| "checkpoint not found" | Run `sdp-orchestrate --feature ` to create initial checkpoint | +| "workstream files missing" | Run `sdp-orchestrate --index` to verify, then `@feature` to regenerate | +| Skill hangs / no progress | Check `.sdp/log/events.jsonl` for last event; use `sdp reset --feature ` if stuck | +| Review loop exceeds 3 rounds | Use `@review --override "reason"`, `@review --partial`, or `@review --escalate` | + ## See Also - `@bugfix` - Quality bug fixes (P1/P2) diff --git a/prompts/skills/delivery-loop/SKILL.md b/prompts/skills/delivery-loop/SKILL.md new file mode 100644 index 00000000..53ca37aa --- /dev/null +++ b/prompts/skills/delivery-loop/SKILL.md @@ -0,0 +1,373 @@ +--- +name: delivery-loop +description: Autonomous delivery cycle β€” bootstrap β†’ preflight β†’ build/review/fix loop (bounded) β†’ PR β†’ codex review (bounded, stable-N) β†’ closeout. Replaces manual "build ws β†’ review β†’ fix β†’ PR β†’ codex β†’ fix". +version: 2.0.0 +tags: + - delivery + - orchestration + - loop +requires_cli: + - bd + - git + - go + - gh + - codex +compatibility: + - claude-code + - opencode + - cursor + - codex +--- + +# Delivery Loop + +## Purpose + +Run the full feature delivery cycle autonomously without user intervention. + +**Entry condition:** operator invoked `@delivery-loop` (no arguments) or `@delivery-loop --resume` after compaction. +**Exit condition:** PR merged + children closed + worktree removed, OR explicit `--abort`. + +## Invocation + +``` +@delivery-loop # full run, picks feature from bd ready +@delivery-loop --resume # read .sdp/checkpoints/${FEATURE}.json, continue +@delivery-loop --abort # cleanup: kill subagents, stash work, archive checkpoint +``` + +Per-harness dispatch is delegated to `scripts/sdp-dispatch.sh`. Adding a harness = one `case` branch in that script. + +## Phases (declarative) + +Phases below are **the source of truth**; the prose "Loop structure" section renders each of them for humans. Per-feature overrides live at `.sdp/delivery.yaml` (same schema; any key present there wins; phases can be disabled with `enabled: false`). + +```yaml +phases: + - name: bootstrap + required: true + - name: preflight + required: true + - name: build + required: true + max_cycles: 5 + wallclock_budget_hours: 4 + backoff_seconds: [30, 120, 300, 600] + - name: design_gap # triggered only from build review + required: false + max_scope_delta: 2 + - name: impact_review + required: true + subagent_timeout_minutes: 10 + - name: traceability # advisory β€” see "Traceability gate" + required: false + mode: advisory # warn-only; promote by setting mode: gate + - name: pr + required: true + - name: codex + required: true + max_cycles: 4 + wallclock_budget_hours: 2 + stable_n: 2 + enabled: true # set false for offline mode (.sdp/delivery.yaml override) + - name: closeout + required: true +whole_loop_budget_hours: 72 # runaway detector only, not a delivery SLO +``` + +**Override example** β€” offline dogfood that skips codex: + +```yaml +# .sdp/delivery.yaml +phases: + - name: codex + enabled: false +``` + +**Override example** β€” feature-specific contract-gen stage: + +```yaml +# .sdp/delivery.yaml +phases: + - name: contract_gen # new phase, must be defined in skill logic + required: true + before: build +``` + +Phases not listed in an override inherit the skill defaults. Unknown phase names in an override are errors (operator gets a message; loop refuses to start). + +## Loop structure + +``` +PHASE 0: BOOTSTRAP + 1. **Pick feature (deterministic):** `pick="$(scripts/deliver-pick.sh)"; EPIC_ID="${pick%%$'\t'*}"; TITLE="${pick#*$'\t'}"` + - exit 0 β†’ EPIC_ID + title printed; derive `FEATURE` from EPIC_ID metadata or title (e.g. F129 from "F129: ...") + - exit 4 β†’ no deliverable feature in ready queue β†’ exit Phase 0 cleanly (no work) + - exit 1 β†’ bd error β†’ escalate + - **Do NOT improvise a picker. Do NOT pick a workstream-leaf task. Do NOT pick a coordination/meta epic.** Tag any program/coordination epic with `bd label add coordination` so the picker skips it. + 2. Identify workstreams: cross-reference `docs/workstreams/backlog/${FEATURE}-*.md` with `bd list -n 200 | grep "^${FEATURE}-"` + 3. **Acquire delivery slot (HARD GATE):** `scripts/deliver-acquire.sh ${FEATURE} ${EPIC_ID}` + - exit 0 β†’ claim + lock acquired, proceed + - exit 2 β†’ foreign claim (another operator owns this epic) β†’ exit Phase 0 cleanly + - exit 3 β†’ lock held by live PID on this host (parallel shell of same user) β†’ exit Phase 0 cleanly + - exit 1 β†’ bd error β†’ escalate + - **No state-mutating step (worktree create, checkpoint write, @build dispatch) may run before this returns 0.** + 4. Create worktree: `git worktree add .worktrees/${FEATURE}` + 5. Write initial `.sdp/checkpoints/${FEATURE}.json` (schema v2) + +PHASE 0.5: PREFLIGHT + # Fail-fast before any build work + - command -v bd gh go codex # all required CLIs present + - gh auth status # gh authenticated + - ws_count == bd_count # workstream files match bead children + - ws_count > 0 # feature has at least one workstream + - bd show ${EPIC_ID} assignee == ${USER} # epic claimed to this operator + - lock file pid == $$ # we still hold the delivery slot (no hijack) + - disk free > 2GB # room for worktree + build artifacts + On any failure: emit actionable error, release lock, unclaim epic, exit 2. + +PHASE 1: BUILD LOOP (max 5 cycles, max 4h wallclock) + repeat: + 1. Dispatch @build subagents (one per WS, parallel, haiku; per-subagent timeout 20m) + 2. Dispatch @review subagent (fresh context, sonnet; timeout 10m) + 3. If APPROVED (zero findings) β†’ break + 4. Else: + - P1/P2/P3 findings β†’ dispatch @fix subagents per finding (haiku; timeout 15m) + - Design gaps β†’ dispatch @design subagent; cap: max 2 new WS per feature (scope_delta ≀ 2) + 5. Exponential backoff between cycles: 30s / 2m / 5m / 10m + until: APPROVED, OR cycle=5 hit, OR 4h wallclock hit + + On cap hit with residual P3: + Operator MUST paste the deferred-P3 list into a new bead description + (file:line: description for each P3). NOT a plain y/N prompt. + Create: `bd create --parent ${EPIC_ID} --type=task --priority=3 \ + --title="Deferred P3 from ${FEATURE}" --description=` + Continue to Phase 2. + +PHASE 2: PR CREATION + 1. @review --dimension impact (check blast radius; timeout 10m) + 2. Traceability gate β€” `scripts/traceability-gate.sh ${FEATURE}` + - For each WS: grep AC[0-9]+ tokens in `docs/workstreams/backlog/${FEATURE}-*.md` + confirm they appear in the touched `*_test.go` files (or marked NONE). + - For schema changes (schema/*.schema.json): require an adjacent `_test.go` + invoking `jsonschema.Validate` OR a `testdata/` directory. + - Mode `advisory` (default): emit warnings, do NOT block. + - Mode `gate` (promoted via `.sdp/delivery.yaml`): missing coverage β†’ fail phase 2. + 3. If impact + traceability OK: run `./scripts/run_go_quality_gates.sh` LOCALLY β€” must be green + 4. `gh pr create` β€” MUST NOT run before gates green + 5. Record `pr_number` in checkpoint + +PHASE 3: CODEX REVIEW LOOP (max 4 cycles, max 2h wallclock, stable-N=2) + + **HARD RULE β€” DO NOT IMPROVISE A SKIP.** Phase 3 is required by default. + The ONLY supported way to skip codex review is the operator override + `.sdp/delivery.yaml { phases: [{ name: codex, enabled: false }] }`. If + the override is not present, you MUST run the loop. You MUST NOT mark + the phase SKIPPED for any of these reasons: + - "codex model availability" + - "spark not supported" / "gpt-5.x too slow" / "no model works" + - "codex command failed" (that means retry, not skip) + - "running it would take too long" (the budget is 2h wallclock β€” + the loop self-terminates without inventing a reason) + If the codex command exits non-zero or returns invalid JSON: log the + exact stderr, sleep with the backoff in step 5, retry. After 4 cycles + or 2h, exit the loop honestly with phase_status=exhausted and emit + the last error. Do not pretend the phase succeeded; do not pretend + the phase was skippable. + + Default invocation passes no `--model` flag; the rescue command picks + the best available model. Override with `--model spark` explicitly + only when the operator asks. Do not pre-select a model and then + declare it unavailable. + + repeat: + 1. scripts/sdp-dispatch.sh codex_review "Review PR #${PR}. Steps: (1) read all changed files, (2) run ./scripts/run_go_quality_gates.sh, (3) emit JSON {tests_passed: bool, findings: [{file, line, rule, symbol, severity, msg}]}. Do not skip tests." + 2. Parse codex JSON output. If parse fails or stderr non-empty, log both verbatim and proceed to step 5 retry. + 3. Dedupe findings against prior cycle by hash(rule + symbol_path + normalized_snippet). NOT file:line:rule β€” line shifts invalidate naive hashes. + 4. Mark findings absent for β‰₯2 consecutive cycles as "non-reproducible candidate" β€” these are NEVER auto-closed at v1. They enter **manual Phase-4 triage**: the operator sees the list in Phase 4 and decides close vs re-raise. (Technician minority: rename this to "auto-close" only if/when an AST-unchanged check lands β€” see Β§7.3 of the design doc.) + 5. If zero NEW findings + tests pass β†’ consecutive_clean_cycles++. Break when consecutive_clean_cycles == 2. + 6. Else: dispatch @fix per finding (haiku/sonnet; timeout 15m), run gates locally, `git push`. + Backoff between cycles: 30s / 2m / 5m / 10m. + until: 2 consecutive clean cycles, OR cycle=4 hit, OR 2h wallclock hit + + **On exit, write to checkpoint:** + phase_status: "done" β†’ loop converged (2 clean cycles) + phase_status: "exhausted" β†’ cycle/wallclock budget hit + phase_status: "skipped" β†’ ONLY if .sdp/delivery.yaml override disables phase + phase_status: "error" β†’ something else; capture exact error, do not invent + +PHASE 4: CLOSEOUT + 1. Confirm merge: `gh pr view ${PR} --json state -q .state == "MERGED"` + 2. Manual Phase-4 triage: operator reviews "non-reproducible candidates" from Phase 3 (list shown with file:line). Either close-as-resolved or re-raise as follow-up bead. No silent auto-close. + 3. Batch close children: `bd close ${WS1} ${WS2} ... --reason "merged via PR#${PR}"` + 4. Close epic: `bd close ${EPIC_ID}` + 5. `scripts/beads_transport.sh export && git push` + 6. `git worktree remove .worktrees/${FEATURE}` + 7. `git push origin --delete ${BRANCH}` (remote branch cleanup) + 8. Archive: `mv .sdp/checkpoints/${FEATURE}.json .sdp/archive/delivered/${FEATURE}-$(date -u +%Y%m%dT%H%M%SZ).json` + 9. Release lock + +WHOLE-LOOP BUDGET + 72h hard ceiling (runaway detector only β€” not a delivery SLO). + On ceiling: write `phase_status: "exhausted"`, post PR comment summarizing state, exit non-zero. +``` + +## Subagent model policy + +| Task | Model | Timeout | +|------|-------|---------| +| @build per WS | haiku | 20m | +| @fix per finding | haiku | 15m | +| @review | sonnet | 10m | +| @review --dimension impact | sonnet | 10m | +| @design (new WS) | sonnet | 15m | +| codex:rescue | default (Codex CLI) | 30m | + +Whole-loop wallclock ceiling: 72h. Phase budgets: build=4h, codex=2h. + +## Rules + +**Resolve or explicitly defer P3 with operator signoff.** (Renamed from "Never skip P3".) All findings from @review block the loop inside cycles 1–4. At cycle 5 the operator may defer remaining P3 β€” but only by manually pasting the `file:line: description` list into a new bead. Plain confirmation prompts are disallowed. + +**Never create PR with red tests.** `./scripts/run_go_quality_gates.sh` must be green **locally** before `gh pr create`. Not after. + +**Codex must run tests.** The codex prompt MUST include "run ./scripts/run_go_quality_gates.sh and report failures". Never send codex a code-only review prompt. + +**Codex output must be structured.** Codex is invoked with a JSON output contract so findings can be deduped across non-deterministic runs. + +**Independent WS β†’ parallel subagents.** Check for file overlap before dispatching parallel @build agents. Overlapping WS β†’ sequential. (Enforcement mechanism: WS frontmatter `touches:` β€” tracked as deferred work.) + +**Max parallel subagents: 5.** Orchestrator enforces via a semaphore; queued agents start after first batch completes. + +## Checkpoint schema v2 + +Location: `.sdp/checkpoints/${FEATURE}.json` (per-feature, not single-slot). +Atomic writes: `scripts/sdp-checkpoint-write.sh` (tmp+rename; only orchestrator writes, subagents return structured output which orchestrator merges). + +```json +{ + "schema_version": 2, + "skill": "delivery-loop", + "feature_id": "F134", + "epic_bead_id": "sdplab-xxx", + "worktree_path": ".worktrees/F134", + "branch": "feature/F134-...", + "pr_number": null, + "phase": 1, + "step": "build", + "phase_status": "running", + "cycle_number": 2, + "max_cycles": 5, + "consecutive_clean_cycles": 0, + "ws_done": ["00-134-01"], + "ws_in_progress": ["00-134-02", "00-134-03"], + "findings": [ + { + "id": "F-001", + "hash": "sha1(rule+symbol_path+normalized_snippet)", + "file": "x.go", + "line": 42, + "rule": "gofmt", + "severity": "P2", + "status": "fixing" + } + ], + "scope_delta_count": 0, + "started_at": "2026-04-22T09:00:00Z", + "deadline": "2026-04-25T09:00:00Z", + "last_heartbeat": "2026-04-22T11:00:00Z", + "subagent_pids": [12345, 12346] +} +``` + +Heartbeat: every 2 min orchestrator updates `last_heartbeat`. External tooling (e.g., `bd status` extension) can distinguish stuck from working. + +## Abort & rollback + +`@delivery-loop --abort` performs: + +1. Kill tracked subagent PIDs from `checkpoint.subagent_pids` +2. `bd update ${EPIC_ID} --status blocked --notes "aborted at phase=${P} cycle=${C}"` + (NOT `--release` / unclaim β€” preserve claim history) +3. `git stash push -u -m "delivery-loop abort ${FEATURE}"` in worktree +4. `mv .sdp/checkpoints/${FEATURE}.json .sdp/archive/aborted/${FEATURE}-$(date -u +%Y%m%dT%H%M%SZ).json` +5. Release the lock: `rm .sdp/locks/deliver-${FEATURE}.lock` +6. Print recovery steps: stash ref, checkpoint archive path, how to resume later + +## Compaction recovery + +If loop is interrupted (compaction, crash): + +1. `cat .sdp/checkpoints/${FEATURE}.json` β†’ read phase + step + cycle_number + last_heartbeat +2. `bd show ${EPIC_ID}` β†’ verify claim still held by ${USER} +3. `git diff main --name-only` β†’ confirm which WS are implemented +4. Resume from the current phase's step β€” do NOT restart from WS 1 +5. Stale heartbeat (>10 min old + no live subagent PIDs) β†’ treat as interrupted, resume from the last completed step + +## Output (per phase) + +``` +## Phase 0: Bootstrap +Feature picked: F134 (sdplab-xxx) β€” "Feature title" +Workstreams: 00-134-01, 00-134-02, 00-134-03 (3 files, 3 beads, drift=0) +Lock: .sdp/locks/deliver-F134.lock acquired (pid=12340) +Worktree: .worktrees/F134 created +Checkpoint: .sdp/checkpoints/F134.json initialized + +## Phase 0.5: Preflight +bdβœ“ ghβœ“ goβœ“ codexβœ“ gh-authβœ“ ws-count=3 bd-count=3 claim=${USER}βœ“ disk=45GBβœ“ + +## Phase 1: Build Loop β€” Cycle 1/5 [00:02 elapsed] +WS dispatched: 00-134-01, 00-134-02, 00-134-03 (parallel, haiku) +WS completed: 3/3 (4m avg) +@review verdict: FINDINGS (3: 1Γ—P2, 2Γ—P3) + P2: pkg/x/foo.go:42 missing error wrap β†’ dispatching @fix + P3: pkg/x/bar.go:91 inconsistent naming β†’ dispatching @fix + P3: pkg/y/baz.go:12 missing doc comment β†’ dispatching @fix + +## Phase 1: Build Loop β€” Cycle 2/5 [00:08 elapsed] +@review verdict: APPROVED β€” zero findings + +## Phase 2: PR +Impact review: OK (blast radius: 2 packages, 0 exported symbols changed) +Quality gates (local): 47/47 passed, 0 failed +PR: #123 created + +## Phase 3: Codex Review β€” Cycle 1/4 [00:15 elapsed] +Codex findings: 2 (1 test failure, 1 code issue) + test: TestFoo panics β†’ dispatching @fix + code: pkg/y/baz.go:91 unused import β†’ dispatching @fix +Tests after fix: 47/47 passed +Push: done +consecutive_clean_cycles: 0 + +## Phase 3: Codex Review β€” Cycle 2/4 [00:22 elapsed] +Codex: zero findings, tests pass +consecutive_clean_cycles: 1 + +## Phase 3: Codex Review β€” Cycle 3/4 [00:28 elapsed] +Codex: zero findings, tests pass +consecutive_clean_cycles: 2 β†’ EXIT + +## Phase 4: Closeout +Non-reproducible candidates: (none) +Beads closed: 00-134-01, 00-134-02, 00-134-03, sdplab-xxx (4 issues) +Beads export: pushed +Worktree: removed +Branch (remote): deleted +Checkpoint archived: .sdp/archive/delivered/F134-20260422T113000Z.json +Lock released. + +Done. PR #123 merged, F134 closed. +``` + +## References + +- `docs/reference/go-patterns.md` β€” Go conventions for @build subagents +- `AGENTS.md` β€” beads workflow, quality gates +- `.agents/skills/build.md` β€” build skill (worktree bootstrap anchor) +- `.agents/skills/review.md` β€” review dimensions +- `scripts/run_go_quality_gates.sh` β€” quality gate script +- `scripts/sdp-dispatch.sh` β€” per-harness subagent + codex invocation +- `scripts/sdp-checkpoint-write.sh` β€” atomic checkpoint writer +- `docs/plans/2026-04-22-deliver-skill-review-design.md` β€” design rationale and consensus record diff --git a/prompts/skills/deploy/SKILL.md b/prompts/skills/deploy/SKILL.md index 54dceb29..ae8be73f 100644 --- a/prompts/skills/deploy/SKILL.md +++ b/prompts/skills/deploy/SKILL.md @@ -1,14 +1,27 @@ --- name: deploy -description: Deployment orchestration. Creates PR to master (after @oneshot) or merges for release. -version: 4.0.0 +description: DEPRECATED: Use @ship instead. Deployment orchestration. Creates PR to main (after @oneshot) or merges for release. +version: 5.0.0 +deprecated: true +deprecated_in_favor_of: ship +deprecation_version: "5.0.0" +removal_version: "8.0.0" changes: + - "5.0.0: DEPRECATED - Renamed to @ship" - "4.0.0: Compress to ~150 lines (P2 remediation)" --- -# @deploy - Deployment Orchestration +# @deploy - DEPRECATED -Create PR to master (after @oneshot) or merge for release. +⚠️ **DEPRECATED** β€” Use `@ship` instead. + +This skill will be removed in version 8.0.0. Both `@deploy` and `@ship` will work for 3 minor versions. + +--- + +## Deployment Orchestration (Legacy) + +Create PR to main (after @oneshot) or merge for release. --- @@ -48,7 +61,7 @@ Before modifying any file, emit a write plan: {"spec_version":"v1.0","event_id":"","timestamp":"","source":{"system":"sdp-lab","component":"deploy"},"event_type":"decision.made","payload":{"decision_type":"write_plan","plan":[{"path":"...","action":"CREATE|MODIFY|DELETE","reason":"..."}]},"context":{"feature_id":"","workstream_id":""}} ``` Include context fields only when the ID is known at plan time. Omit unavailable fields rather than inventing placeholders. - > **Note:** Phase 1 uses prompt-level write boundaries (CLI out of scope). Aligns with `sdp/schema/contracts/orchestration-event.schema.json` via `event_type: "decision.made"`. Phase 2 CLI will emit natively. + > **Note:** Phase 1 uses prompt-level write boundaries (CLI out of scope). Aligns with `schema/contracts/orchestration-event.schema.json` via `event_type: "decision.made"`. Phase 2 CLI will emit natively. **Output format:** ``` @@ -69,8 +82,8 @@ Proceed? [y/n] | Mode | Action | |------|--------| -| PR | feature -> master via gh pr create | -| Release | Version bump + tag on master | +| PR | feature -> main via gh pr create | +| Release | Version bump + tag on main | --- @@ -97,6 +110,16 @@ Before ANY git: verify `pwd`, `git branch --show-current`. --- +## Recovery + +| Symptom | Fix | +|---------|-----| +| Skill produces no output | Check working directory is project root with `docs/workstreams/backlog/` | +| "checkpoint not found" | Run `sdp-orchestrate --feature ` to create initial checkpoint | +| "workstream files missing" | Run `sdp-orchestrate --index` to verify, then `@feature` to regenerate | +| Skill hangs / no progress | Check `.sdp/log/events.jsonl` for last event; use `sdp reset --feature ` if stuck | +| Review loop exceeds 3 rounds | Use `@review --override "reason"`, `@review --partial`, or `@review --escalate` | + ## See Also - `@review` β€” Must be APPROVED before deploy diff --git a/prompts/skills/design/SKILL.md b/prompts/skills/design/SKILL.md index 932be416..029ab73b 100644 --- a/prompts/skills/design/SKILL.md +++ b/prompts/skills/design/SKILL.md @@ -110,7 +110,7 @@ Before modifying any file, emit a write plan covering workstream files, design d {"spec_version":"v1.0","event_id":"","timestamp":"","source":{"system":"sdp-lab","component":"design"},"event_type":"decision.made","payload":{"decision_type":"write_plan","plan":[{"path":"...","action":"CREATE|MODIFY|DELETE","reason":"..."}]},"context":{"feature_id":"","workstream_id":""}} ``` Include context fields only when the ID is known at plan time. Omit unavailable fields rather than inventing placeholders. - > **Note:** Phase 1 uses prompt-level write boundaries (CLI out of scope). Aligns with `sdp/schema/contracts/orchestration-event.schema.json` via `event_type: "decision.made"`. Phase 2 CLI will emit natively. + > **Note:** Phase 1 uses prompt-level write boundaries (CLI out of scope). Aligns with `schema/contracts/orchestration-event.schema.json` via `event_type: "decision.made"`. Phase 2 CLI will emit natively. **Output format:** ``` @@ -141,6 +141,16 @@ Proceed? [y/n] - Updated `docs/workstreams/INDEX.md` - Updated `.beads-sdp-mapping.jsonl` +## Recovery + +| Symptom | Fix | +|---------|-----| +| Skill produces no output | Check working directory is project root with `docs/workstreams/backlog/` | +| "checkpoint not found" | Run `sdp-orchestrate --feature ` to create initial checkpoint | +| "workstream files missing" | Run `sdp-orchestrate --index` to verify, then `@feature` to regenerate | +| Skill hangs / no progress | Check `.sdp/log/events.jsonl` for last event; use `sdp reset --feature ` if stuck | +| Review loop exceeds 3 rounds | Use `@review --override "reason"`, `@review --partial`, or `@review --escalate` | + ## See Also - @idea β€” Requirements diff --git a/prompts/skills/discovery/SKILL.md b/prompts/skills/discovery/SKILL.md index 0edb380c..4aaa1601 100644 --- a/prompts/skills/discovery/SKILL.md +++ b/prompts/skills/discovery/SKILL.md @@ -23,8 +23,8 @@ When user invokes `@discovery "feature description"` or when `@feature` invokes 2. If memory search returns > 10 results, reduce to 2 most specific terms. ```bash -sdp memory stats # warn if index > 24h old -sdp memory search " " +sdp index stats # warn if index > 24h old +sdp index query " " ``` 3. Analyze results for: @@ -184,6 +184,46 @@ When [situation], [user segment] want to [motivation], so they can [outcome]. --- +## Write Plan (F101) + +Before creating the discovery brief, emit a write plan: + +1. **Enumerate** β€” List every file the skill will CREATE / MODIFY / DELETE with a one-line reason (discovery brief, event log). +2. **Flags:** + - `--dry-run` β€” Emit write plan only. Do NOT create, modify, or delete any file. + - `--yes` β€” Skip confirmation prompt. Execute immediately. Intended for CI/non-interactive. +3. **Confirm** β€” Present the plan to the user and wait for explicit approval (unless `--yes`). +4. **Log** β€” Append write plan event to `.sdp/log/events.jsonl` (**sanitize file paths** before logging: strip newlines, ensure valid JSON escaping): + ```json + {"spec_version":"v1.0","event_id":"","timestamp":"","source":{"system":"sdp-lab","component":"discovery"},"event_type":"decision.made","payload":{"decision_type":"write_plan","plan":[{"path":"...","action":"CREATE|MODIFY|DELETE","reason":"..."}]},"context":{"feature_id":"","workstream_id":""}} + ``` + Include context fields only when the ID is known at plan time. Omit unavailable fields rather than inventing placeholders. + > **Note:** Phase 1 uses prompt-level write boundaries (CLI out of scope). Aligns with `schema/contracts/orchestration-event.schema.json` via `event_type: "decision.made"`. Phase 2 CLI will emit natively. + +**Output format:** +``` +WRITE PLAN for @discovery : + CREATE: docs/drafts/discovery-{slug}.md β€” Discovery brief (COMPETITIVE / NOVEL tracks only) + MODIFY: .sdp/log/events.jsonl β€” Write plan event log + +Proceed? [y/n] +``` + +**Modes:** +- No flag: Show plan β†’ Confirm β†’ Execute +- `--dry-run`: Show plan β†’ STOP +- `--yes`: Show plan β†’ Execute immediately (no prompt) + +--- + +| Symptom | Fix | +|---------|-----| +| Skill produces no output | Check working directory is project root with `docs/workstreams/backlog/` | +| "checkpoint not found" | Run `sdp-orchestrate --feature ` to create initial checkpoint | +| "workstream files missing" | Run `sdp-orchestrate --index` to verify, then `@feature` to regenerate | +| Skill hangs / no progress | Check `.sdp/log/events.jsonl` for last event; use `sdp reset --feature ` if stuck | +| Review loop exceeds 3 rounds | Use `@review --override "reason"`, `@review --partial`, or `@review --escalate` | + ## See Also - `@feature` - Orchestrator that invokes @discovery diff --git a/prompts/skills/feature/SKILL.md b/prompts/skills/feature/SKILL.md index 6c5d4461..c50b7a86 100644 --- a/prompts/skills/feature/SKILL.md +++ b/prompts/skills/feature/SKILL.md @@ -1,15 +1,17 @@ --- name: feature description: Feature planning orchestrator (discovery -> idea -> ux -> design -> workstream tree) -version: 8.0.0 +version: 9.0.0 depends_on: "@discovery v1" changes: + - v9: Added --design-only (alias for --quick, explicit naming) - v8: Full product discovery flow with @discovery, @ux, impact analysis - Added --quick (skip @discovery), --infra (skip @ux) - Step 3.5: Impact analysis after @design examples: - "@feature 'Add user authentication' --default # Full interactive pipeline" - "@feature 'Add user authentication' --quick # Skip to @design only, 0 questions" + - "@feature 'Add user authentication' --design-only # Same as --quick, explicit name" - "@feature 'Add payment processing' --auto # Non-interactive, from roadmap/plan docs" --- @@ -26,7 +28,7 @@ Orchestrate product discovery, requirements, UX research, and workstream design. | Mode | When to use | Steps | Questions | |------|-------------|-------|-----------| | `--default` (or no flag) | New/exploratory feature. Full interactive discovery. | 0, 1, 2, 2.5, 3, 3.5, 4 | Interactive (3-5 questions) | -| `--quick` | User knows what they want, just needs workstreams. | 3 only | **0 questions** - goes directly to @design | +| `--quick` / `--design-only` | User knows what they want, just needs workstreams. | 3 only | **0 questions** - goes directly to @design | | `--auto` | Feature already described in roadmap/plan. Non-interactive. | 0, 3, 4 only | **0 questions** - reads from docs/ROADMAP.md | ### Mode Behavior Guarantee @@ -34,7 +36,7 @@ Orchestrate product discovery, requirements, UX research, and workstream design. **Deterministic:** Each mode produces identical behavior given identical input. No context sniffing, no heuristics, no "smart defaults." - `--default`: Always asks the same questions in the same order -- `--quick`: Always skips to @design with zero questions +- `--quick` / `--design-only`: Always skips to @design with zero questions - `--auto`: Always reads from roadmap/plan docs, never asks questions --- @@ -111,7 +113,7 @@ Output: feature ID, workstream count, file names, Beads IDs, ready-to-run comman --- -## --quick Mode (@design Only, Zero Questions) +## --quick / --design-only Mode (@design Only, Zero Questions) For users who know what they want and just need workstreams. Zero questions, deterministic. @@ -173,7 +175,7 @@ Before creating workstream files and docs, emit a write plan: {"spec_version":"v1.0","event_id":"","timestamp":"","source":{"system":"sdp-lab","component":"feature"},"event_type":"decision.made","payload":{"decision_type":"write_plan","plan":[{"path":"...","action":"CREATE|MODIFY|DELETE","reason":"..."}]},"context":{"feature_id":"","workstream_id":""}} ``` Include context fields only when the ID is known at plan time. Omit unavailable fields rather than inventing placeholders. - > **Note:** Phase 1 uses prompt-level write boundaries (CLI out of scope). Aligns with `sdp/schema/contracts/orchestration-event.schema.json` via `event_type: "decision.made"`. Phase 2 CLI will emit natively. + > **Note:** Phase 1 uses prompt-level write boundaries (CLI out of scope). Aligns with `schema/contracts/orchestration-event.schema.json` via `event_type: "decision.made"`. Phase 2 CLI will emit natively. **Output format:** ``` @@ -192,6 +194,29 @@ Proceed? [y/n] > **Note:** `--dry-run` and `--yes` are orthogonal to skill mode flags (`--default`, `--quick`, `--auto`). They can be combined with any mode (e.g. `@feature "X" --quick --dry-run`). +## Completion + +When all workstreams are created and verified, output: + +``` +@feature complete. Feature {ID}: {count} workstreams created. + Aggregate: 00-{FFF}-00 + Leaves: 00-{FFF}-01 .. 00-{FFF}-{NN} + +Next: @build 00-{FFF}-01 or @oneshot F{XX} +``` + +## Recovery + +| Symptom | Fix | +|---------|-----| +| Skill produces no output | Run `@init` first to scaffold project structure; then re-run `@feature` | +| "checkpoint not found" | Run `sdp-orchestrate --feature ` to create initial checkpoint | +| "workstream files missing" | Run `sdp-orchestrate --index` to verify, then `@feature` to regenerate | +| Skill hangs / no progress | Check `.sdp/log/events.jsonl` for last event; use `sdp reset --feature ` if stuck | +| Review loop exceeds 3 rounds | Use `@review --override "reason"`, `@review --partial`, or `@review --escalate` | +| --design-only produces no workstreams | Run `@init` to scaffold project structure, then re-run with `--design-only` | + ## See Also @discovery β€” Product discovery gate | @idea β€” Requirements | @ux β€” UX research | @design β€” Workstream planning | @build β€” Execute leaf workstream | @oneshot β€” Execute all ready leaf workstreams diff --git a/prompts/skills/go-modern/SKILL.md b/prompts/skills/go-modern/SKILL.md index e4dd96a0..40b4a54b 100644 --- a/prompts/skills/go-modern/SKILL.md +++ b/prompts/skills/go-modern/SKILL.md @@ -63,6 +63,16 @@ After modernization changes: - run `golangci-lint run ./...` where configured - use `golangci-lint run --enable-only modernize --issues-exit-code 0 ./...` for audit snapshots +## Recovery + +| Symptom | Fix | +|---------|-----| +| Skill produces no output | Check working directory is project root with `docs/workstreams/backlog/` | +| "checkpoint not found" | Run `sdp-orchestrate --feature ` to create initial checkpoint | +| "workstream files missing" | Run `sdp-orchestrate --index` to verify, then `@feature` to regenerate | +| Skill hangs / no progress | Check `.sdp/log/events.jsonl` for last event; use `sdp reset --feature ` if stuck | +| Review loop exceeds 3 rounds | Use `@review --override "reason"`, `@review --partial`, or `@review --escalate` | + ## See Also - `@build` - implementation workflow diff --git a/prompts/skills/guard/SKILL.md b/prompts/skills/guard/SKILL.md index 8f53dd18..9573051c 100644 --- a/prompts/skills/guard/SKILL.md +++ b/prompts/skills/guard/SKILL.md @@ -25,3 +25,13 @@ sdp guard deactivate # Clear ## Output ALLOWED or BLOCKED with scope details. + +## Recovery + +| Symptom | Fix | +|---------|-----| +| Skill produces no output | Check working directory is project root with `docs/workstreams/backlog/` | +| "checkpoint not found" | Run `sdp-orchestrate --feature ` to create initial checkpoint | +| "workstream files missing" | Run `sdp-orchestrate --index` to verify, then `@feature` to regenerate | +| Skill hangs / no progress | Check `.sdp/log/events.jsonl` for last event; use `sdp reset --feature ` if stuck | +| Review loop exceeds 3 rounds | Use `@review --override "reason"`, `@review --partial`, or `@review --escalate` | diff --git a/prompts/skills/hotfix/SKILL.md b/prompts/skills/hotfix/SKILL.md index 21cade47..aec6429f 100644 --- a/prompts/skills/hotfix/SKILL.md +++ b/prompts/skills/hotfix/SKILL.md @@ -1,11 +1,11 @@ --- name: hotfix -description: Emergency P0 fixes. Fast-track production deployment with minimal changes. Branch from master, immediate deploy. +description: Emergency P0 fixes. Fast-track production deployment with minimal changes. Branch from main, immediate deploy. --- # @hotfix -Emergency production fixes. Minimal changes, fast testing, merge to master with tag. +Emergency production fixes. Minimal changes, fast testing, merge to main with tag. ## When to Use @@ -15,12 +15,12 @@ Emergency production fixes. Minimal changes, fast testing, merge to master with ## Workflow -1. **Branch** β€” `git checkout master && git pull && git checkout -b hotfix/{id}-{slug}` +1. **Branch** β€” `git checkout main && git pull && git checkout -b hotfix/{id}-{slug}` 2. **Minimal fix** β€” No refactoring, fix bug only 3. **Smoke test** β€” Critical path verification -4. **Merge** β€” `git checkout master && git merge hotfix/{branch} --no-edit` +4. **Merge** β€” `git checkout main && git merge hotfix/{branch} --no-edit` 5. **Tag** β€” `git tag -a v{VERSION} -m "Hotfix: {description}"` -6. **Push** β€” `git push origin master --tags` +6. **Push** β€” `git push origin main --tags` ## Write Plan (F101) @@ -36,7 +36,7 @@ Before modifying any file, emit a write plan: {"spec_version":"v1.0","event_id":"","timestamp":"","source":{"system":"sdp-lab","component":"hotfix"},"event_type":"decision.made","payload":{"decision_type":"write_plan","plan":[{"path":"...","action":"CREATE|MODIFY|DELETE","reason":"..."}]},"context":{"feature_id":"","workstream_id":""}} ``` Include context fields only when the ID is known at plan time. Omit unavailable fields rather than inventing placeholders. - > **Note:** Phase 1 uses prompt-level write boundaries (CLI out of scope). Aligns with `sdp/schema/contracts/orchestration-event.schema.json` via `event_type: "decision.made"`. Phase 2 CLI will emit natively. + > **Note:** Phase 1 uses prompt-level write boundaries (CLI out of scope). Aligns with `schema/contracts/orchestration-event.schema.json` via `event_type: "decision.made"`. Phase 2 CLI will emit natively. **Output format:** ``` @@ -57,6 +57,16 @@ Proceed? [y/n] Hotfix merged, tagged, pushed. Issue closed. +## Recovery + +| Symptom | Fix | +|---------|-----| +| Skill produces no output | Check working directory is project root with `docs/workstreams/backlog/` | +| "checkpoint not found" | Run `sdp-orchestrate --feature ` to create initial checkpoint | +| "workstream files missing" | Run `sdp-orchestrate --index` to verify, then `@feature` to regenerate | +| Skill hangs / no progress | Check `.sdp/log/events.jsonl` for last event; use `sdp reset --feature ` if stuck | +| Review loop exceeds 3 rounds | Use `@review --override "reason"`, `@review --partial`, or `@review --escalate` | + ## See Also - @bugfix β€” P1/P2 quality fixes diff --git a/prompts/skills/idea/SKILL.md b/prompts/skills/idea/SKILL.md index 57c0fe6a..444d8b4b 100644 --- a/prompts/skills/idea/SKILL.md +++ b/prompts/skills/idea/SKILL.md @@ -181,6 +181,47 @@ User writes 500+ chars: "I need a full auth system with OAuth, MFA, session mana --- +## Write Plan (F101) + +Before creating the intent file or beads task, emit a write plan: + +1. **Enumerate** β€” List every file the skill will CREATE / MODIFY / DELETE with a one-line reason (intent file, beads task, event log). +2. **Flags:** + - `--dry-run` β€” Emit write plan only. Do NOT create, modify, or delete any file. + - `--yes` β€” Skip confirmation prompt. Execute immediately. Intended for CI/non-interactive. +3. **Confirm** β€” Present the plan to the user and wait for explicit approval (unless `--yes`). +4. **Log** β€” Append write plan event to `.sdp/log/events.jsonl` (**sanitize file paths** before logging: strip newlines, ensure valid JSON escaping): + ```json + {"spec_version":"v1.0","event_id":"","timestamp":"","source":{"system":"sdp-lab","component":"idea"},"event_type":"decision.made","payload":{"decision_type":"write_plan","plan":[{"path":"...","action":"CREATE|MODIFY|DELETE","reason":"..."}]},"context":{"feature_id":"","workstream_id":""}} + ``` + Include context fields only when the ID is known at plan time. Omit unavailable fields rather than inventing placeholders. + > **Note:** Phase 1 uses prompt-level write boundaries (CLI out of scope). Aligns with `schema/contracts/orchestration-event.schema.json` via `event_type: "decision.made"`. Phase 2 CLI will emit natively. + +**Output format:** +``` +WRITE PLAN for @idea : + CREATE: docs/intent/{task_id}.json β€” Machine-readable intent spec + CREATE: beads task β€” Feature tracking issue + MODIFY: .sdp/log/events.jsonl β€” Write plan event log + +Proceed? [y/n] +``` + +**Modes:** +- No flag: Show plan β†’ Confirm β†’ Execute +- `--dry-run`: Show plan β†’ STOP +- `--yes`: Show plan β†’ Execute immediately (no prompt) + +--- + +| Symptom | Fix | +|---------|-----| +| Skill produces no output | Check working directory is project root with `docs/workstreams/backlog/` | +| "checkpoint not found" | Run `sdp-orchestrate --feature ` to create initial checkpoint | +| "workstream files missing" | Run `sdp-orchestrate --index` to verify, then `@feature` to regenerate | +| Skill hangs / no progress | Check `.sdp/log/events.jsonl` for last event; use `sdp reset --feature ` if stuck | +| Review loop exceeds 3 rounds | Use `@review --override "reason"`, `@review --partial`, or `@review --escalate` | + ## See Also - `@design` - Workstream decomposition diff --git a/prompts/skills/init/SKILL.md b/prompts/skills/init/SKILL.md new file mode 100644 index 00000000..0fdf1eed --- /dev/null +++ b/prompts/skills/init/SKILL.md @@ -0,0 +1,138 @@ +--- +name: init +description: Initialize SDP in a new or existing project +version: 1.0.0 +changes: + - Initial release: project detection, config scaffolding, harness setup +--- + +# @init + +Initialize SDP (Spec-Driven Protocol) in the current project directory. + +## Workflow + +When user invokes `@init` or `sdp init`: + +### Step 1: Project Detection + +Auto-detect project characteristics: + +```bash +# Detect language +if [ -f "go.mod" ]; then LANG="go" +elif [ -f "pyproject.toml" ] || [ -f "requirements.txt" ]; then LANG="python" +elif [ -f "pom.xml" ] || [ -f "build.gradle" ]; then LANG="java" +elif [ -f "package.json" ]; then LANG="nodejs" +elif [ -f "Cargo.toml" ]; then LANG="rust" +else LANG="unknown" +fi + +# Detect framework (language-specific) +# Detect existing SDP artifacts +``` + +### Step 2: Ask Configuration Questions + +1. **Project name** β€” default: directory name +2. **Primary language** β€” default: detected +3. **AI harness(es)** β€” which AI tools the team uses (claude, codex, cursor, opencode, zed, warp, other). Default: detect from existing config files. +4. **Issue tracker** β€” beads (default), github-issues, linear, none + +### Step 3: Scaffold SDP Structure + +Create the following (skip existing): + +``` +docs/ + roadmap/ROADMAP.md # Feature roadmap + workstreams/ # Workstream tracking + drafts/ # Discovery drafts +.sdp/ # SDP internal state + checkpoints/ +AGENTS.md # Agent instructions (harness-neutral) +``` + +### Step 4: Configure Harnesses + +For each selected harness, create appropriate config: + +- **Claude Code** β€” `.claude/settings.json` (hooks), `.claude/commands/` (slash commands) +- **Codex** β€” `.codex/` with symlinks to `prompts/skills/` +- **Cursor** β€” `.cursor/` with `.cursorrules` and skill symlinks +- **OpenCode** β€” `.opencode/opencode.json` with agent cards + +All harness configs point to the same canonical source: `prompts/skills/` and `prompts/agents/`. + +### Step 5: Configure Quality Gates + +Based on detected language, set up appropriate quality gate commands: + +| Language | Build | Test | Lint | +|----------|-------|------|------| +| Go | `go build ./...` | `go test ./...` | `go vet ./...` | +| Python | `pip install .` | `pytest` | `ruff check .` | +| Node.js | `npm run build` | `npm test` | `npm run lint` | +| Rust | `cargo build` | `cargo test` | `cargo clippy` | +| Java | `mvn compile` | `mvn test` | `mvn checkstyle:check` | + +Write the detected gates into `AGENTS.md`. + +### Step 6: Initialize Issue Tracker + +If beads selected: +```bash +bd init +``` + +### Step 7: Verify + +- All configured harness symlinks resolve +- AGENTS.md exists with quality gates +- Issue tracker is functional (if selected) +- `sdp health` passes + +## Flags + +| Flag | Description | +|------|-------------| +| `--auto` | Skip all questions, accept defaults from detection | +| `--lang ` | Force specific language | +| `--harness ` | Comma-separated list of harnesses to configure | + +## When to Use + +- New project starting with SDP +- Existing project adopting SDP +- Adding a new AI harness to existing SDP project +- After cloning an SDP project (verify/setup) + +## Output + +``` +SDP initialized: {project_name} +Language: {detected} +Harnesses: {configured list} +Quality gates: {build}, {test}, {lint} +Issue tracker: {selected} + +Next steps: + @vision "your product idea" # Start from scratch + @reality --quick # Analyze existing codebase + @feature "add X" # Plan a feature +``` + +## Recovery + +| Symptom | Fix | +|---------|-----| +| Skill produces no output | Re-run `@init` with `--auto` flag to skip prompts; verify you have write permissions in current directory | +| Harness config not created | Check harness CLI is installed; re-run `@init --harness ` for missing harness | +| "bd init fails" | Verify beads CLI installed (`bd --version`); run `bd init` manually | +| Quality gates not detected | Manually specify: `@init --lang ` | + +## See Also + +- @vision -- Strategic planning +- @reality -- Codebase analysis +- @feature -- Feature planning diff --git a/prompts/skills/issue/SKILL.md b/prompts/skills/issue/SKILL.md index 81d72f21..99df79ce 100644 --- a/prompts/skills/issue/SKILL.md +++ b/prompts/skills/issue/SKILL.md @@ -29,6 +29,16 @@ Classify bugs and route to fix command. Issue file, routing recommendation. +## Recovery + +| Symptom | Fix | +|---------|-----| +| Skill produces no output | Check working directory is project root with `docs/workstreams/backlog/` | +| "checkpoint not found" | Run `sdp-orchestrate --feature ` to create initial checkpoint | +| "workstream files missing" | Run `sdp-orchestrate --index` to verify, then `@feature` to regenerate | +| Skill hangs / no progress | Check `.sdp/log/events.jsonl` for last event; use `sdp reset --feature ` if stuck | +| Review loop exceeds 3 rounds | Use `@review --override "reason"`, `@review --partial`, or `@review --escalate` | + ## See Also - @debug β€” Root cause analysis diff --git a/prompts/skills/oneshot/SKILL.md b/prompts/skills/oneshot/SKILL.md index 20bcee89..a480fdb7 100644 --- a/prompts/skills/oneshot/SKILL.md +++ b/prompts/skills/oneshot/SKILL.md @@ -18,7 +18,7 @@ Outer loop: `sdp-orchestrate` (or `sdp orchestrate` if available) drives phases. ## Rules 0. **Scope** β€” Do not change workstream scope mid-run. If scope must change, stop and start a new run. -1. **Get next action** β€” Run `sdp-orchestrate --feature F{XX} --next-action`. Parse the JSON output (schema: `sdp/schema/next-action.schema.json`). +1. **Get next action** β€” Run `sdp-orchestrate --feature F{XX} --next-action`. Parse the JSON output (schema: `schema/next-action.schema.json`). 2. **Execute phase and advance** β€” For `build`: run @build {ws_id}, commit, then `sdp-orchestrate --feature F{XX} --advance --result $(git rev-parse HEAD)`. For `review`: run @review F{XX}, fix P0/P1 until approved (max 3 iterations), then `sdp-orchestrate --feature F{XX} --advance`. **One advance per phase** β€” run `--advance` exactly once after build, exactly once after review. PR and CI run automatically. When action is `done`, output only: `CI GREEN - @oneshot complete`. ## Post-compaction @@ -39,7 +39,7 @@ Before the orchestration loop begins, emit a write plan for orchestrator-owned a {"spec_version":"v1.0","event_id":"","timestamp":"","source":{"system":"sdp-lab","component":"oneshot"},"event_type":"decision.made","payload":{"decision_type":"write_plan","plan":[{"path":"...","action":"CREATE|MODIFY|DELETE","reason":"..."}]},"context":{"feature_id":"","workstream_id":""}} ``` Include context fields only when the ID is known at plan time. Omit unavailable fields rather than inventing placeholders. - > **Note:** Phase 1 uses prompt-level write boundaries (CLI out of scope). Aligns with `sdp/schema/contracts/orchestration-event.schema.json` via `event_type: "decision.made"`. Phase 2 CLI will emit natively. + > **Note:** Phase 1 uses prompt-level write boundaries (CLI out of scope). Aligns with `schema/contracts/orchestration-event.schema.json` via `event_type: "decision.made"`. Phase 2 CLI will emit natively. **Output format:** ``` @@ -59,3 +59,13 @@ Proceed? [y/n] ## Claude Code Use Task tool to spawn @build and @review subagents. Each subagent gets a fresh context window. Stop hook blocks premature exit when CI phase is incomplete. + +## Recovery + +| Symptom | Fix | +|---------|-----| +| Skill produces no output | Check working directory is project root with `docs/workstreams/backlog/` | +| "checkpoint not found" | Run `sdp-orchestrate --feature ` to create initial checkpoint | +| "workstream files missing" | Run `sdp-orchestrate --index` to verify, then `@feature` to regenerate | +| Skill hangs / no progress | Check `.sdp/log/events.jsonl` for last event; use `sdp reset --feature ` if stuck | +| Review loop exceeds 3 rounds | Use `@review --override "reason"`, `@review --partial`, or `@review --escalate` | diff --git a/prompts/skills/protocol-consistency/SKILL.md b/prompts/skills/protocol-consistency/SKILL.md index 6208c1e2..2ab5e285 100644 --- a/prompts/skills/protocol-consistency/SKILL.md +++ b/prompts/skills/protocol-consistency/SKILL.md @@ -10,7 +10,7 @@ Detect drift between docs, CLI, and CI. ## Workflow 1. **Verify CLI** β€” `sdp --help`, `sdp --help` β€” commands in docs exist -2. **Validate WS schema** β€” Read `docs/workstreams/backlog/.md`, run `sdp drift detect ` +2. **Validate WS schema** β€” Read `docs/workstreams/backlog/.md`, run `sdp doctor adapters` to check for drift 3. **Validate CI** β€” `rg "sdp .*" .github/workflows hooks scripts` β€” paths valid 4. **Report** β€” Source file, observed vs expected, risk, suggested fix 5. **Track** β€” `bd create --title="Protocol drift: ..." --type=task --priority=2` @@ -18,3 +18,13 @@ Detect drift between docs, CLI, and CI. ## Output Report: scope, blocking/non-blocking mismatches, findings, recommended fixes. + +## Recovery + +| Symptom | Fix | +|---------|-----| +| Skill produces no output | Check working directory is project root with `docs/workstreams/backlog/` | +| "checkpoint not found" | Run `sdp-orchestrate --feature ` to create initial checkpoint | +| "workstream files missing" | Run `sdp-orchestrate --index` to verify, then `@feature` to regenerate | +| Skill hangs / no progress | Check `.sdp/log/events.jsonl` for last event; use `sdp reset --feature ` if stuck | +| Review loop exceeds 3 rounds | Use `@review --override "reason"`, `@review --partial`, or `@review --escalate` | diff --git a/prompts/skills/prototype/SKILL.md b/prompts/skills/prototype/SKILL.md index b4274645..a563eb5e 100644 --- a/prompts/skills/prototype/SKILL.md +++ b/prompts/skills/prototype/SKILL.md @@ -31,6 +31,47 @@ Non-negotiable: code compiles, runs, no crashes, basic security. `docs/drafts/prototype-{id}.md`, `docs/workstreams/backlog/00-FFF-*.md` +## Write Plan (F101) + +Before creating prototype scaffolding, emit a write plan: + +1. **Enumerate** β€” List every file the skill will CREATE / MODIFY / DELETE with a one-line reason (prototype doc, workstream files, event log). +2. **Flags:** + - `--dry-run` β€” Emit write plan only. Do NOT create, modify, or delete any file. + - `--yes` β€” Skip confirmation prompt. Execute immediately. Intended for CI/non-interactive. +3. **Confirm** β€” Present the plan to the user and wait for explicit approval (unless `--yes`). +4. **Log** β€” Append write plan event to `.sdp/log/events.jsonl` (**sanitize file paths** before logging: strip newlines, ensure valid JSON escaping): + ```json + {"spec_version":"v1.0","event_id":"","timestamp":"","source":{"system":"sdp-lab","component":"prototype"},"event_type":"decision.made","payload":{"decision_type":"write_plan","plan":[{"path":"...","action":"CREATE|MODIFY|DELETE","reason":"..."}]},"context":{"feature_id":"","workstream_id":""}} + ``` + Include context fields only when the ID is known at plan time. Omit unavailable fields rather than inventing placeholders. + > **Note:** Phase 1 uses prompt-level write boundaries (CLI out of scope). Aligns with `schema/contracts/orchestration-event.schema.json` via `event_type: "decision.made"`. Phase 2 CLI will emit natively. + +**Output format:** +``` +WRITE PLAN for @prototype : + CREATE: docs/drafts/prototype-{id}.md β€” Prototype specification + CREATE: docs/workstreams/backlog/00-FFF-*.md β€” Workstream files (1-3) + MODIFY: .sdp/log/events.jsonl β€” Write plan event log + +Proceed? [y/n] +``` + +**Modes:** +- No flag: Show plan β†’ Confirm β†’ Execute +- `--dry-run`: Show plan β†’ STOP +- `--yes`: Show plan β†’ Execute immediately (no prompt) + +--- + +| Symptom | Fix | +|---------|-----| +| Skill produces no output | Check working directory is project root with `docs/workstreams/backlog/` | +| "checkpoint not found" | Run `sdp-orchestrate --feature ` to create initial checkpoint | +| "workstream files missing" | Run `sdp-orchestrate --index` to verify, then `@feature` to regenerate | +| Skill hangs / no progress | Check `.sdp/log/events.jsonl` for last event; use `sdp reset --feature ` if stuck | +| Review loop exceeds 3 rounds | Use `@review --override "reason"`, `@review --partial`, or `@review --escalate` | + ## See Also - @feature β€” Full planning diff --git a/prompts/skills/reality-check/SKILL.md b/prompts/skills/reality-check/SKILL.md index 77bfd581..f5d8b5c7 100644 --- a/prompts/skills/reality-check/SKILL.md +++ b/prompts/skills/reality-check/SKILL.md @@ -28,6 +28,16 @@ Quick validation that docs match code before making changes. ~90 seconds (vs 5-1 ### Recommendation: βœ… Proceed / ⚠️ Stop / πŸ”„ Adapt ``` +## Recovery + +| Symptom | Fix | +|---------|-----| +| Skill produces no output | Check working directory is project root with `docs/workstreams/backlog/` | +| "checkpoint not found" | Run `sdp-orchestrate --feature ` to create initial checkpoint | +| "workstream files missing" | Run `sdp-orchestrate --index` to verify, then `@feature` to regenerate | +| Skill hangs / no progress | Check `.sdp/log/events.jsonl` for last event; use `sdp reset --feature ` if stuck | +| Review loop exceeds 3 rounds | Use `@review --override "reason"`, `@review --partial`, or `@review --escalate` | + ## See Also - @verify-workstream β€” Full workstream validation diff --git a/prompts/skills/reality/SKILL.md b/prompts/skills/reality/SKILL.md index 1840b5df..78402364 100644 --- a/prompts/skills/reality/SKILL.md +++ b/prompts/skills/reality/SKILL.md @@ -152,6 +152,46 @@ If PRODUCT_VISION.md exists, compare reality to vision with Vision vs Reality Ga --- +## Write Plan (F101) + +Before writing the reality-check report, emit a write plan: + +1. **Enumerate** β€” List every file the skill will CREATE / MODIFY / DELETE with a one-line reason (report file, event log). +2. **Flags:** + - `--dry-run` β€” Emit write plan only. Do NOT create, modify, or delete any file. + - `--yes` β€” Skip confirmation prompt. Execute immediately. Intended for CI/non-interactive. +3. **Confirm** β€” Present the plan to the user and wait for explicit approval (unless `--yes`). +4. **Log** β€” Append write plan event to `.sdp/log/events.jsonl` (**sanitize file paths** before logging: strip newlines, ensure valid JSON escaping): + ```json + {"spec_version":"v1.0","event_id":"","timestamp":"","source":{"system":"sdp-lab","component":"reality"},"event_type":"decision.made","payload":{"decision_type":"write_plan","plan":[{"path":"...","action":"CREATE|MODIFY|DELETE","reason":"..."}]},"context":{"feature_id":"","workstream_id":""}} + ``` + Include context fields only when the ID is known at plan time. Omit unavailable fields rather than inventing placeholders. + > **Note:** Phase 1 uses prompt-level write boundaries (CLI out of scope). Aligns with `schema/contracts/orchestration-event.schema.json` via `event_type: "decision.made"`. Phase 2 CLI will emit natively. + +**Output format:** +``` +WRITE PLAN for @reality : + CREATE: docs/reality/check.md β€” Reality-check report with health score + MODIFY: .sdp/log/events.jsonl β€” Write plan event log + +Proceed? [y/n] +``` + +**Modes:** +- No flag: Show plan β†’ Confirm β†’ Execute +- `--dry-run`: Show plan β†’ STOP +- `--yes`: Show plan β†’ Execute immediately (no prompt) + +--- + +| Symptom | Fix | +|---------|-----| +| Skill produces no output | Check working directory is project root with `docs/workstreams/backlog/` | +| "checkpoint not found" | Run `sdp-orchestrate --feature ` to create initial checkpoint | +| "workstream files missing" | Run `sdp-orchestrate --index` to verify, then `@feature` to regenerate | +| Skill hangs / no progress | Check `.sdp/log/events.jsonl` for last event; use `sdp reset --feature ` if stuck | +| Review loop exceeds 3 rounds | Use `@review --override "reason"`, `@review --partial`, or `@review --escalate` | + ## See Also - `@vision` - Strategic planning diff --git a/prompts/skills/review/SKILL.md b/prompts/skills/review/SKILL.md index ae08404c..891a3424 100644 --- a/prompts/skills/review/SKILL.md +++ b/prompts/skills/review/SKILL.md @@ -1,9 +1,10 @@ --- name: review description: Multi-agent quality review (QA + Security + DevOps + SRE + TechLead + Documentation + PromptOps) -cli: sdp quality all -version: 16.0.0 +cli: +version: 17.0.0 changes: + - "17.0.0: Post-max-retry escape hatches (--override, --partial, --escalate)" - "16.0.0: Fixed schema consistency - all 7 reviewers always spawned (F098 P1 fix)" - "15.0.0: Add risk-based reviewer selection" - "14.3.0: Add @go-modern checks for Go review surfaces" @@ -14,7 +15,7 @@ changes: # review -> **CLI:** `sdp quality all` | **LLM:** Spawn all 7 specialist subagents with risk-based depth allocation +> **LLM only:** Spawn all 7 specialist subagents with risk-based depth allocation Comprehensive multi-agent quality review. All 7 reviewers always spawned; risk patterns determine depth, not presence. @@ -59,9 +60,8 @@ Full config: `.sdp/config.yml` under `review` section. When user invokes `@review F{XX}`: -1. **Run CLI:** `sdp quality all` -2. **Determine depth:** Match risk patterns to identify which reviewers get deep focus (rest get rubber-stamp). -3. **Spawn all 7 subagents IN PARALLEL** (use your platform's subagent spawn). **DO NOT skip.** +1. **Determine depth:** Match risk patterns to identify which reviewers get deep focus (rest get rubber-stamp). +2. **Spawn all 7 subagents IN PARALLEL** (use your platform's subagent spawn). **DO NOT skip.** **All 7 roles always spawned:** qa, security, devops, sre, techlead, docs, promptops @@ -81,7 +81,7 @@ For each finding: `bd create --silent --labels "review-finding,F{XX},round-1,{ro **Role files:** `prompts/agents/qa.md`, `prompts/agents/security.md`, `prompts/agents/devops.md`, `prompts/agents/sre.md`, `prompts/agents/tech-lead.md`. Docs and PromptOps: inline. -**Docs expert:** Check drift (`sdp drift detect`), AC coverage (jq `.ac_evidence|length` vs WS file). Labels: `review-finding,F{XX},round-1,docs` +**Docs expert:** Check drift (`sdp doctor adapters`), AC coverage (jq `.ac_evidence|length` vs WS file). Labels: `review-finding,F{XX},round-1,docs` **PromptOps expert:** Review prompts/skills, prompts/agents, prompts/commands. Check: language-agnostic, no phantom CLI, no handoff lists, skill size ≀200 LOC. Labels: `review-finding,F{XX},round-1,promptops`. Output `checks` array per schema/review-verdict.schema.json. @@ -99,7 +99,7 @@ Before writing review output files (verdict, findings), emit a write plan: {"spec_version":"v1.0","event_id":"","timestamp":"","source":{"system":"sdp-lab","component":"review"},"event_type":"decision.made","payload":{"decision_type":"write_plan","plan":[{"path":"...","action":"CREATE|MODIFY|DELETE","reason":"..."}]},"context":{"feature_id":"","workstream_id":""}} ``` Include context fields only when the ID is known at plan time. Omit unavailable fields rather than inventing placeholders. - > **Note:** Phase 1 uses prompt-level write boundaries (CLI out of scope). Aligns with `sdp/schema/contracts/orchestration-event.schema.json` via `event_type: "decision.made"`. Phase 2 CLI will emit natively. + > **Note:** Phase 1 uses prompt-level write boundaries (CLI out of scope). Aligns with `schema/contracts/orchestration-event.schema.json` via `event_type: "decision.made"`. Phase 2 CLI will emit natively. **Output format:** ``` @@ -136,7 +136,7 @@ Proceed? [y/n] ```json { "feature": "F{XX}", - "verdict": "APPROVED|CHANGES_REQUESTED", + "verdict": "APPROVED|CHANGES_REQUESTED|PARTIALLY_APPROVED|ESCALATED", "timestamp": "...", "round": 1, "reviewers": { @@ -163,6 +163,11 @@ Proceed? [y/n] } ``` +Escape hatch fields: +- `--override`: add non-empty `override_reason`. +- `PARTIALLY_APPROVED`: add non-empty `partial_failing_roles` using reviewer role names. +- `ESCALATED`: add non-empty `escalation_issue`. + **Priority:** P0/P1 block; P2/P3 track only. **When verdict=CHANGES_REQUESTED** β€” output this handoff block prominently: @@ -176,6 +181,68 @@ Run `@design phase4-remediation` with findings to create workstreams. --- +## Post-Max-Retry Escape Hatches (F104) + +> **Implementation note:** These escape hatches are prompt-level constructs for LLM-driven review. The builder functions exist in `internal/orchestrate/findings.go` but the CLI review command does not yet accept `--override`/`--partial`/`--escalate` flags. When using this skill via an LLM agent, the agent writes the verdict JSON directly using the new verdict values. + +After **3 consecutive CHANGES_REQUESTED** verdicts on the same feature, the review loop must offer escape options. Present this block: + +``` +--- +## Review Max Retries Reached (3/3) + +Choose an escape hatch: + @review --override "reason" β€” Override: force APPROVED with logged justification + @review --partial β€” Partial: approve passing reviewers, create issues for failing + @review --escalate β€” Escalate: create beads issue for human review + +Default (no flag): re-run @design phase4-remediation with latest findings. +--- +``` + +### --override (Governed Override) + +Overrides verdict to APPROVED. **Requires non-empty justification string.** + +``` +@review F098 --override "P2 findings are documentation-only, no code risk" +``` + +**Rules:** +- Empty justification (`--override ""`) β†’ **REJECTED**. Must provide a reason. +- Justification logged to `.sdp/review_verdict.json` β†’ `override_reason` field. +- Justification visible in PR body (append `## Review Override: ` section). +- Branch protection still requires human approval β€” override does NOT bypass branch rules. + +### --partial (Partial Approval) + +Approves reviewers that passed, creates beads issues for failing reviewers. + +``` +@review F098 --partial +``` + +**Behavior:** +- Passing reviewers: verdict stays PASS. +- Failing reviewers: create one beads issue per failing reviewer with all their findings. +- Overall verdict: `PARTIALLY_APPROVED` (new verdict value). +- `.sdp/review_verdict.json` β†’ `verdict: "PARTIALLY_APPROVED"`, `partial_failing_roles: [...]`. + +### --escalate (Human Escalation) + +Creates a beads issue assigned to the project owner for human review. + +``` +@review F098 --escalate +``` + +**Behavior:** +- Creates: `bd create --title "Human Review Required: F098 (3 failed rounds)" --priority 1 --type task` +- Overall verdict: `ESCALATED` (new verdict value). +- `.sdp/review_verdict.json` β†’ `verdict: "ESCALATED"`, `escalation_issue: "sdplab-XXXX"`. + +--- + ## Beads `bd create --title "{AREA}: {desc}" --priority {0-3} --labels "review-finding,F{XX},round-{N},{role}" --type bug --silent` @@ -211,5 +278,16 @@ FINDINGS_CREATED: (none) PASS ``` +## Recovery + +| Symptom | Fix | +|---------|-----| +| Skill produces no output | Check working directory is project root with `docs/workstreams/backlog/` | +| "checkpoint not found" | Run `sdp-orchestrate --feature ` to create initial checkpoint | +| "workstream files missing" | Run `sdp-orchestrate --index` to verify, then `@feature` to regenerate | +| Skill hangs / no progress | Check `.sdp/log/events.jsonl` for last event; use `sdp reset --feature ` if stuck | +| Review loop exceeds 3 rounds | Use `@review --override "reason"`, `@review --partial`, or `@review --escalate` | +| Review verdict file corrupted | Delete `.sdp/review_verdict.json`, re-run `@review` | + ## See Also @oneshot β€” review-fix loop | @deploy β€” requires APPROVED verdict | @go-modern β€” Go modernization checklist diff --git a/prompts/skills/ship/SKILL.md b/prompts/skills/ship/SKILL.md new file mode 100644 index 00000000..00c4eeb9 --- /dev/null +++ b/prompts/skills/ship/SKILL.md @@ -0,0 +1,31 @@ +--- +description: Deployment orchestration from approved feature to release handoff. +agent: builder +--- + +# /ship β€” Ship Feature + +When calling `/ship {feature} [version_bump]`: + +1. Load skill: `.claude/skills/ship/SKILL.md` +2. Pre-flight: run quality gates (see AGENTS.md), verify APPROVED +3. Version: bump semver (patch/minor/major) +4. Generate: CHANGELOG, release notes +5. **EXECUTE** (do NOT propose): + - `git commit` artifacts + - `git merge feature/F{XX} β†’ main` (via PR) + - `git tag v{X.Y.Z}` + - `git push origin main v{X.Y.Z}` +6. Report summary + +## Quick Reference + +**Input:** APPROVED feature + version bump (default: patch) +**Output:** Production deployment + v{X.Y.Z} tag +**Rule:** Do NOT stop after artifacts β€” EXECUTE all git operations + +## Version Bump + +- `@ship ` β€” patch (0.5.0 β†’ 0.5.1) +- `@ship minor` β€” minor (0.5.0 β†’ 0.6.0) +- `@ship major` β€” major (0.5.0 β†’ 1.0.0) diff --git a/prompts/skills/spec-interrogate/SKILL.md b/prompts/skills/spec-interrogate/SKILL.md new file mode 100644 index 00000000..6a88004f --- /dev/null +++ b/prompts/skills/spec-interrogate/SKILL.md @@ -0,0 +1,226 @@ +--- +name: spec-interrogate +description: Use when a spec or plan may hide ambiguities and needs a context-stripped challenge before planning or implementation. +version: 1.0.0 +changes: + - "1.0.0: Initial public skill for spec hardening with auditable report and evidence contract" +--- + +# spec-interrogate + +> Challenge a text artifact with a fresh interrogator before you commit to planning or implementation. + +The interrogator receives only the artifact and invocation parameters. No chat history. No implied context. No author explanation. The goal is simple: surface what an implementer still cannot infer from the document itself. + +--- + +## Use When + +- Before `sdp phase plan` for non-trivial Discovery output +- Before committing to a risky architecture, API, or rollout plan +- When the author is too close to the document and may be missing undefined terms, missing error paths, or scope leaks + +Do not use this for code review or implementation. Use the relevant review/build skill instead. + +--- + +## Inputs + +```bash +@spec-interrogate \ + [--mode socratic|cold-read|adversarial|impl-test] \ + [--questions N] \ + [--rounds M] \ + [--feature-id F] \ + [--evidence-path PATH] \ + [--report-path PATH] +``` + +Defaults: + +- `--mode socratic` +- `--questions 5` +- `--rounds 5` +- `--evidence-path .sdp/evidence/spec-interrogate.json` +- `--report-path .sdp/reports/spec-interrogate.md` + +--- + +## Shared Output Contract + +Every run must create: + +1. A human-readable report at `report-path` +2. A machine-readable evidence file at `evidence-path` + +The report is mandatory even on `PASS`. It must contain: + +- artifact path +- selected mode +- short summary of what was tested +- ordered unresolved questions or gaps +- explicit verdict +- next action + +The evidence file must include: + +```json +{ + "interrogate_verdict": "PASS | REWORK | ABORT", + "artifact_path": "docs/discovery/my-feature/validation.md", + "feature_id": "F042", + "mode": "socratic", + "rounds_completed": 2, + "max_rounds": 5, + "open_questions_count": 0, + "open_questions": [], + "report_path": ".sdp/reports/spec-interrogate.md", + "report_summary": "No unresolved implementation-blocking questions remain." +} +``` + +Each unresolved question must be structured: + +```json +{ + "id": "Q1", + "type": "scope-ambiguity", + "impact": "plan-blocking", + "question": "What is the fallback behavior when the upstream model call times out?" +} +``` + +Stdout is not the system of record. The report and evidence files are. + +--- + +## Modes + +### `socratic` + +Iterative dialogue via document edits. + +1. Interrogator asks up to `N` high-impact questions. +2. Author edits the artifact instead of replying in chat. +3. Interrogator re-reads and checks whether the questions are resolved. +4. Repeat until convergence or `--rounds` is exhausted. + +Verdict: + +- `PASS` when no unresolved questions remain +- `REWORK` when the round cap is hit and blocking questions remain +- `ABORT` when the author explicitly stops + +### `cold-read` + +Cheap first pass. + +1. Interrogator reads once. +2. It writes: + - what it believes the artifact says + - what it still cannot infer + - what it refuses to assume +3. Unresolved inferences become `open_questions[]`. + +Verdict: + +- `PASS` when `open_questions_count = 0` +- `REWORK` otherwise +- `ABORT` when the author explicitly stops + +Accounting: `rounds_completed = 1`, `max_rounds = 1` + +### `adversarial` + +Artifact-level attack review. + +1. Interrogator reads once. +2. It lists trust-boundary gaps, abuse paths, failure modes, and mitigation holes. +3. Blocking gaps become `open_questions[]`. + +Verdict: + +- `PASS` when no blocking gaps remain +- `REWORK` otherwise +- `ABORT` when the author explicitly stops + +Accounting: `rounds_completed = 1`, `max_rounds = 1` + +### `impl-test` + +Checks whether another agent could implement the artifact without hallucinating. + +1. Interrogator tries to outline a minimal implementation plan from the artifact alone. +2. Any step that requires invented assumptions becomes `open_questions[]`. +3. The report must separate grounded steps from assumption-dependent steps. + +Verdict: + +- `PASS` when the outline requires zero invented assumptions +- `REWORK` otherwise +- `ABORT` when the author explicitly stops + +Accounting: `rounds_completed = 1`, `max_rounds = 1` + +--- + +## Question Taxonomy + +Prioritize only questions that matter for planning or implementation: + +1. `why` +2. `undefined-term` +3. `missing-error-path` +4. `scope-ambiguity` +5. `unstated-assumption` + +Do not waste rounds on style or formatting unless they obscure meaning. + +--- + +## SDP Integration + +This is agent discipline before the Plan gate, not CLI enforcement. + +```bash +@spec-interrogate docs/discovery//validation.md --feature-id + +# only after PASS: +sdp phase plan --feature-id --strict --evidence-path .sdp/evidence/plan.json +``` + +If the verdict is `REWORK`, do not call `sdp phase plan`. Resume work using the unresolved questions in the generated report. + +--- + +## Skip Rules + +Skip only when: + +- the task is trivial +- there is no Discovery artifact to interrogate +- `--skip-interrogate` is explicitly documented in beads with a reason + +"Probably fine" is not a valid skip reason. + +--- + +## Examples + +```bash +# iterative hardening +@spec-interrogate docs/discovery/my-feature/validation.md --feature-id F042 + +# cheap sanity check +@spec-interrogate docs/plans/arch-decision.md --mode cold-read + +# risky architecture review +@spec-interrogate docs/plans/auth-redesign.md --mode adversarial +``` + +--- + +## Acceptance Boundary + +This skill is for text artifacts only: specs, plans, design docs, and schema-oriented documents. +If the target is code, use review tooling instead. diff --git a/prompts/skills/strataudit/SKILL.md b/prompts/skills/strataudit/SKILL.md index 53b36457..28eb793f 100644 --- a/prompts/skills/strataudit/SKILL.md +++ b/prompts/skills/strataudit/SKILL.md @@ -85,3 +85,13 @@ The CLI can resolve configured network runtimes. It cannot create a host-native - `docs/reference/strataudit-runtime-policy.md` - `docs/reference/strataudit-output-modes.md` - `sdp-strataudit run` + +## Recovery + +| Symptom | Fix | +|---------|-----| +| Skill produces no output | Check working directory is project root with `docs/workstreams/backlog/` | +| "checkpoint not found" | Run `sdp-orchestrate --feature ` to create initial checkpoint | +| "workstream files missing" | Run `sdp-orchestrate --index` to verify, then `@feature` to regenerate | +| Skill hangs / no progress | Check `.sdp/log/events.jsonl` for last event; use `sdp reset --feature ` if stuck | +| Review loop exceeds 3 rounds | Use `@review --override "reason"`, `@review --partial`, or `@review --escalate` | diff --git a/prompts/skills/tdd/SKILL.md b/prompts/skills/tdd/SKILL.md index 1b6af97e..7dea4209 100644 --- a/prompts/skills/tdd/SKILL.md +++ b/prompts/skills/tdd/SKILL.md @@ -40,3 +40,44 @@ func (v *V) IsValid(s string) bool { return strings.Contains(s, "@") } ``` For Go refactors, prefer modern stdlib idioms from `@go-modern` when they preserve behavior. + +## Write Plan (F101) + +Before writing test files or implementation files, emit a write plan: + +1. **Enumerate** β€” List every file the skill will CREATE / MODIFY / DELETE with a one-line reason (test files, fix files, event log). +2. **Flags:** + - `--dry-run` β€” Emit write plan only. Do NOT create, modify, or delete any file. + - `--yes` β€” Skip confirmation prompt. Execute immediately. Intended for CI/non-interactive. +3. **Confirm** β€” Present the plan to the user and wait for explicit approval (unless `--yes`). +4. **Log** β€” Append write plan event to `.sdp/log/events.jsonl` (**sanitize file paths** before logging: strip newlines, ensure valid JSON escaping): + ```json + {"spec_version":"v1.0","event_id":"","timestamp":"","source":{"system":"sdp-lab","component":"tdd"},"event_type":"decision.made","payload":{"decision_type":"write_plan","plan":[{"path":"...","action":"CREATE|MODIFY|DELETE","reason":"..."}]},"context":{"feature_id":"","workstream_id":""}} + ``` + Include context fields only when the ID is known at plan time. Omit unavailable fields rather than inventing placeholders. + > **Note:** Phase 1 uses prompt-level write boundaries (CLI out of scope). Aligns with `schema/contracts/orchestration-event.schema.json` via `event_type: "decision.made"`. Phase 2 CLI will emit natively. + +**Output format:** +``` +WRITE PLAN for @tdd : + CREATE: path/to/*_test.go β€” Failing test (RED phase) + CREATE: path/to/impl.go β€” Minimal implementation (GREEN phase) + MODIFY: .sdp/log/events.jsonl β€” Write plan event log + +Proceed? [y/n] +``` + +**Modes:** +- No flag: Show plan β†’ Confirm β†’ Execute +- `--dry-run`: Show plan β†’ STOP +- `--yes`: Show plan β†’ Execute immediately (no prompt) + +--- + +| Symptom | Fix | +|---------|-----| +| Skill produces no output | Check working directory is project root with `docs/workstreams/backlog/` | +| "checkpoint not found" | Run `sdp-orchestrate --feature ` to create initial checkpoint | +| "workstream files missing" | Run `sdp-orchestrate --index` to verify, then `@feature` to regenerate | +| Skill hangs / no progress | Check `.sdp/log/events.jsonl` for last event; use `sdp reset --feature ` if stuck | +| Review loop exceeds 3 rounds | Use `@review --override "reason"`, `@review --partial`, or `@review --escalate` | diff --git a/prompts/skills/think/SKILL.md b/prompts/skills/think/SKILL.md index 16b41889..ec604d07 100644 --- a/prompts/skills/think/SKILL.md +++ b/prompts/skills/think/SKILL.md @@ -37,3 +37,13 @@ Skip parallel agents: Study β†’ Analyze (named experts) β†’ Propose options β†’ - Specificity β€” solutions for THIS project - Honesty β€” every option has cons - Clear recommendation β€” don't leave user hanging + +## Recovery + +| Symptom | Fix | +|---------|-----| +| Skill produces no output | Check working directory is project root with `docs/workstreams/backlog/` | +| "checkpoint not found" | Run `sdp-orchestrate --feature ` to create initial checkpoint | +| "workstream files missing" | Run `sdp-orchestrate --index` to verify, then `@feature` to regenerate | +| Skill hangs / no progress | Check `.sdp/log/events.jsonl` for last event; use `sdp reset --feature ` if stuck | +| Review loop exceeds 3 rounds | Use `@review --override "reason"`, `@review --partial`, or `@review --escalate` | diff --git a/prompts/skills/ux/SKILL.md b/prompts/skills/ux/SKILL.md index b618f15f..f3d7fc5d 100644 --- a/prompts/skills/ux/SKILL.md +++ b/prompts/skills/ux/SKILL.md @@ -103,6 +103,46 @@ validated_workaround: "[what users do today]" --- +## Write Plan (F101) + +Before creating the UX research file, emit a write plan: + +1. **Enumerate** β€” List every file the skill will CREATE / MODIFY / DELETE with a one-line reason (UX findings doc, event log). +2. **Flags:** + - `--dry-run` β€” Emit write plan only. Do NOT create, modify, or delete any file. + - `--yes` β€” Skip confirmation prompt. Execute immediately. Intended for CI/non-interactive. +3. **Confirm** β€” Present the plan to the user and wait for explicit approval (unless `--yes`). +4. **Log** β€” Append write plan event to `.sdp/log/events.jsonl` (**sanitize file paths** before logging: strip newlines, ensure valid JSON escaping): + ```json + {"spec_version":"v1.0","event_id":"","timestamp":"","source":{"system":"sdp-lab","component":"ux"},"event_type":"decision.made","payload":{"decision_type":"write_plan","plan":[{"path":"...","action":"CREATE|MODIFY|DELETE","reason":"..."}]},"context":{"feature_id":"","workstream_id":""}} + ``` + Include context fields only when the ID is known at plan time. Omit unavailable fields rather than inventing placeholders. + > **Note:** Phase 1 uses prompt-level write boundaries (CLI out of scope). Aligns with `schema/contracts/orchestration-event.schema.json` via `event_type: "decision.made"`. Phase 2 CLI will emit natively. + +**Output format:** +``` +WRITE PLAN for @ux : + CREATE: docs/ux/{feature}.md β€” UX research findings with YAML frontmatter + MODIFY: .sdp/log/events.jsonl β€” Write plan event log + +Proceed? [y/n] +``` + +**Modes:** +- No flag: Show plan β†’ Confirm β†’ Execute +- `--dry-run`: Show plan β†’ STOP +- `--yes`: Show plan β†’ Execute immediately (no prompt) + +--- + +| Symptom | Fix | +|---------|-----| +| Skill produces no output | Check working directory is project root with `docs/workstreams/backlog/` | +| "checkpoint not found" | Run `sdp-orchestrate --feature ` to create initial checkpoint | +| "workstream files missing" | Run `sdp-orchestrate --index` to verify, then `@feature` to regenerate | +| Skill hangs / no progress | Check `.sdp/log/events.jsonl` for last event; use `sdp reset --feature ` if stuck | +| Review loop exceeds 3 rounds | Use `@review --override "reason"`, `@review --partial`, or `@review --escalate` | + ## See Also - `@feature` - Orchestrator that auto-triggers @ux diff --git a/prompts/skills/verify-workstream/SKILL.md b/prompts/skills/verify-workstream/SKILL.md index a232d062..0e9585c1 100644 --- a/prompts/skills/verify-workstream/SKILL.md +++ b/prompts/skills/verify-workstream/SKILL.md @@ -23,6 +23,16 @@ Verification complete. Severity. Recommendation. Comparison table. @build invokes this before execution. +## Recovery + +| Symptom | Fix | +|---------|-----| +| Skill produces no output | Check working directory is project root with `docs/workstreams/backlog/` | +| "checkpoint not found" | Run `sdp-orchestrate --feature ` to create initial checkpoint | +| "workstream files missing" | Run `sdp-orchestrate --index` to verify, then `@feature` to regenerate | +| Skill hangs / no progress | Check `.sdp/log/events.jsonl` for last event; use `sdp reset --feature ` if stuck | +| Review loop exceeds 3 rounds | Use `@review --override "reason"`, `@review --partial`, or `@review --escalate` | + ## See Also - @reality-check β€” Quick single-file check diff --git a/prompts/skills/vision/SKILL.md b/prompts/skills/vision/SKILL.md index 53abda73..5216af53 100644 --- a/prompts/skills/vision/SKILL.md +++ b/prompts/skills/vision/SKILL.md @@ -28,7 +28,7 @@ Before modifying any file, emit a write plan covering PRODUCT_VISION.md, PRD.md, {"spec_version":"v1.0","event_id":"","timestamp":"","source":{"system":"sdp-lab","component":"vision"},"event_type":"decision.made","payload":{"decision_type":"write_plan","plan":[{"path":"...","action":"CREATE|MODIFY|DELETE","reason":"..."}]},"context":{"feature_id":"","workstream_id":""}} ``` Include context fields only when the ID is known at plan time. Omit unavailable fields rather than inventing placeholders. - > **Note:** Phase 1 uses prompt-level write boundaries (CLI out of scope). Aligns with `sdp/schema/contracts/orchestration-event.schema.json` via `event_type: "decision.made"`. Phase 2 CLI will emit natively. + > **Note:** Phase 1 uses prompt-level write boundaries (CLI out of scope). Aligns with `schema/contracts/orchestration-event.schema.json` via `event_type: "decision.made"`. Phase 2 CLI will emit natively. **Output format:** ``` @@ -57,6 +57,29 @@ Initial setup, quarterly review, major pivot, new market. PRODUCT_VISION.md, docs/prd/PRD.md, docs/roadmap/ROADMAP.md, docs/drafts/feature-*.md +## Completion + +When all artifacts are generated, output: + +``` +@vision complete. Artifacts created: + PRODUCT_VISION.md + docs/prd/PRD.md + docs/roadmap/ROADMAP.md + docs/drafts/feature-*.md + +Next: @reality --quick or @feature "description" +``` + +## Recovery + +| Symptom | Fix | +|---------|-----| +| Skill produces no output | Ensure `@init` was run first; verify `docs/` directory exists and is writable | +| Artifacts not generated | Check disk space and write permissions; re-run with `--yes` to skip prompts | +| PRD sections missing | Project type not detected β€” specify manually via interview answers | +| Roadmap empty | No features extracted from vision β€” re-run interview with more specific answers | + ## See Also - @idea β€” Feature-level requirements diff --git a/schema/config.schema.json b/schema/config.schema.json index 3745180d..24c69cfb 100644 --- a/schema/config.schema.json +++ b/schema/config.schema.json @@ -1,5 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://sdp.dev/schema/config/v1", "title": "SDP Project Config", "description": "Project-level SDP settings (.sdp/config.yml)", "type": "object", diff --git a/schema/contracts/beads-instructions.schema.json b/schema/contracts/beads-instructions.schema.json new file mode 100644 index 00000000..5a05adea --- /dev/null +++ b/schema/contracts/beads-instructions.schema.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://sdp.dev/contracts/beads-instructions/v1", + "title": "BeadsInstructions", + "type": "object", + "required": ["spec_version", "instructions"], + "properties": { + "spec_version": { "type": "string", "pattern": "^v\\d+\\.\\d+$" }, + "context": { "type": "string" }, + "instructions": { + "type": "array", + "items": { + "type": "object", + "required": ["step", "action", "reason"], + "properties": { + "step": { "type": "integer" }, + "action": { "type": "string" }, + "reason": { "type": "string" }, + "command": { "type": "string" }, + "expected_outcome": { "type": "string" }, + "troubleshooting": { "type": "string" } + } + } + } + } +} diff --git a/schema/contracts/beads-queue-view.schema.json b/schema/contracts/beads-queue-view.schema.json new file mode 100644 index 00000000..0188625f --- /dev/null +++ b/schema/contracts/beads-queue-view.schema.json @@ -0,0 +1,39 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://sdp.dev/contracts/beads-queue-view/v1", + "title": "BeadsQueueView", + "type": "object", + "required": ["spec_version", "timestamp", "ready_count", "blocked_count", "items", "next_action"], + "properties": { + "spec_version": { "type": "string", "pattern": "^v\\d+\\.\\d+$" }, + "timestamp": { "type": "string", "format": "date-time" }, + "ready_count": { "type": "integer", "minimum": 0 }, + "blocked_count": { "type": "integer", "minimum": 0 }, + "in_progress_count": { "type": "integer", "minimum": 0 }, + "items": { + "type": "array", + "items": { + "type": "object", + "required": ["id", "title", "status", "priority"], + "properties": { + "id": { "type": "string" }, + "title": { "type": "string" }, + "status": { "type": "string", "enum": ["ready", "blocked", "in_progress"] }, + "priority": { "type": "integer" }, + "blocked_by": { "type": "array", "items": { "type": "string" } }, + "labels": { "type": "array", "items": { "type": "string" } } + } + } + }, + "next_action": { + "type": "object", + "required": ["recommended", "reason"], + "properties": { + "recommended": { "type": "string" }, + "reason": { "type": "string" }, + "command": { "type": "string" }, + "blocking_issues": { "type": "array", "items": { "type": "string" } } + } + } + } +} diff --git a/schema/contracts/cli-mcp-mapping.json b/schema/contracts/cli-mcp-mapping.json new file mode 100644 index 00000000..32a6b42e --- /dev/null +++ b/schema/contracts/cli-mcp-mapping.json @@ -0,0 +1,225 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://sdp.dev/schemas/cli-mcp-mapping.json", + "title": "CLI to MCP Mapping Contract", + "description": "Defines the explicit contract that maps CLI registry truth and skill catalog truth into MCP tools, resources, and prompts", + "type": "object", + "properties": { + "spec_version": { + "type": "string", + "description": "Contract specification version", + "pattern": "^\\d+\\.\\d+\\.\\d+$" + }, + "cli_registry_hash": { + "type": "string", + "description": "Hash of the CLI registry snapshot used to generate this mapping", + "pattern": "^[a-f0-9]+$" + }, + "skill_catalog_hash": { + "type": "string", + "description": "Hash of the skill catalog snapshot used to generate this mapping", + "pattern": "^[a-f0-9]+$" + }, + "generated_at": { + "type": "string", + "description": "ISO 8601 timestamp when this mapping was generated", + "format": "date-time" + }, + "tools": { + "type": "array", + "description": "CLI commands mapped to MCP tools", + "items": { + "$ref": "#/definitions/tool_mapping" + } + }, + "resources": { + "type": "array", + "description": "CLI outputs mapped to MCP resources", + "items": { + "$ref": "#/definitions/resource_mapping" + } + }, + "prompts": { + "type": "array", + "description": "Skill intents mapped to MCP prompts", + "items": { + "$ref": "#/definitions/prompt_mapping" + } + } + }, + "required": ["spec_version", "cli_registry_hash", "skill_catalog_hash", "generated_at", "tools", "resources", "prompts"], + "definitions": { + "tool_mapping": { + "type": "object", + "description": "Maps a CLI command to an MCP tool", + "properties": { + "mcp_tool_name": { + "type": "string", + "description": "MCP tool name (e.g., 'sdp_scout')" + }, + "cli_command": { + "type": "string", + "description": "CLI command path (e.g., 'scout')" + }, + "description": { + "type": "string", + "description": "Tool description for MCP discovery" + }, + "parameters": { + "type": "array", + "description": "Tool parameters derived from CLI flags", + "items": { + "$ref": "#/definitions/parameter_mapping" + } + }, + "parity_status": { + "type": "string", + "enum": ["full", "partial", "deprecated", "forward"], + "description": "Parity status with CLI implementation" + }, + "source_location": { + "type": "string", + "description": "Source file where CLI command is defined (e.g., 'cmd/sdp/cmd_scout.go')" + } + }, + "required": ["mcp_tool_name", "cli_command", "description", "parity_status"] + }, + "resource_mapping": { + "type": "object", + "description": "Maps CLI output to an MCP resource", + "properties": { + "mcp_resource_uri": { + "type": "string", + "description": "MCP resource URI (e.g., 'sdp://scout')" + }, + "cli_command": { + "type": "string", + "description": "CLI command that generates this resource" + }, + "artifact_path": { + "type": "string", + "description": "Path where CLI persists the artifact (e.g., '.sdp/scout.json')" + }, + "description": { + "type": "string", + "description": "Resource description for MCP discovery" + }, + "mime_type": { + "type": "string", + "description": "MIME type of the resource content" + }, + "parity_status": { + "type": "string", + "enum": ["full", "partial", "deprecated", "forward"], + "description": "Parity status with CLI implementation" + }, + "hint_tool": { + "type": "string", + "description": "MCP tool to suggest when resource is missing" + } + }, + "required": ["mcp_resource_uri", "cli_command", "artifact_path", "description", "mime_type", "parity_status"] + }, + "prompt_mapping": { + "type": "object", + "description": "Maps a skill intent to an MCP prompt", + "properties": { + "mcp_prompt_name": { + "type": "string", + "description": "MCP prompt name (e.g., 'understand')" + }, + "intent_model": { + "type": "string", + "description": "Intent model category (e.g., 'F125:intent:understand')" + }, + "description": { + "type": "string", + "description": "Prompt description for MCP discovery" + }, + "arguments": { + "type": "array", + "description": "Prompt arguments", + "items": { + "$ref": "#/definitions/argument_mapping" + } + }, + "resources_used": { + "type": "array", + "description": "MCP resources this prompt consumes", + "items": { + "type": "string" + } + }, + "parity_status": { + "type": "string", + "enum": ["full", "partial", "deprecated", "forward"], + "description": "Parity status with skill catalog" + }, + "skill_files": { + "type": "array", + "description": "Skill files that implement this intent", + "items": { + "type": "string" + } + } + }, + "required": ["mcp_prompt_name", "intent_model", "description", "parity_status"] + }, + "parameter_mapping": { + "type": "object", + "description": "Maps a CLI flag to an MCP tool parameter", + "properties": { + "mcp_param_name": { + "type": "string", + "description": "MCP parameter name" + }, + "cli_flag": { + "type": "string", + "description": "CLI flag name (e.g., '--format')" + }, + "type": { + "type": "string", + "enum": ["string", "number", "boolean", "enum"], + "description": "Parameter type" + }, + "required": { + "type": "boolean", + "description": "Whether the parameter is required" + }, + "enum_values": { + "type": "array", + "description": "Allowed values for enum type", + "items": { + "type": "string" + } + }, + "default": { + "description": "Default value if not provided" + } + }, + "required": ["mcp_param_name", "cli_flag", "type"] + }, + "argument_mapping": { + "type": "object", + "description": "Maps a prompt argument to its definition", + "properties": { + "name": { + "type": "string", + "description": "Argument name" + }, + "description": { + "type": "string", + "description": "Argument description" + }, + "required": { + "type": "boolean", + "description": "Whether the argument is required" + }, + "default": { + "description": "Default value if not provided" + } + }, + "required": ["name", "description"] + } + } +} \ No newline at end of file diff --git a/schema/contracts/examples/handoff-flow.json b/schema/contracts/examples/handoff-flow.json new file mode 100644 index 00000000..9c2e549d --- /dev/null +++ b/schema/contracts/examples/handoff-flow.json @@ -0,0 +1,28 @@ +[ + { + "spec_version": "v1.0", + "event_id": "b0000001-0000-4000-8000-000000000001", + "timestamp": "2026-04-25T11:00:00Z", + "source": {"system": "sdp-lab", "component": "coder-agent"}, + "event_type": "handoff.initiated", + "payload": { + "to_agent": "reviewer-agent", + "artifacts": ["internal/x/foo.go", "internal/x/foo_test.go"], + "context": {"ws_id": "00-001-01", "summary": "Implementation complete, ready for review"} + }, + "metadata": {"correlation_id": "handoff-001"} + }, + { + "spec_version": "v1.0", + "event_id": "b0000001-0000-4000-8000-000000000002", + "timestamp": "2026-04-25T11:05:00Z", + "source": {"system": "sdp-lab", "component": "reviewer-agent"}, + "event_type": "handoff.completed", + "payload": { + "from_agent": "coder-agent", + "result": "approved", + "findings_count": 0 + }, + "metadata": {"correlation_id": "handoff-001"} + } +] diff --git a/schema/contracts/examples/runtime-decision-allow.json b/schema/contracts/examples/runtime-decision-allow.json new file mode 100644 index 00000000..27f3ad6d --- /dev/null +++ b/schema/contracts/examples/runtime-decision-allow.json @@ -0,0 +1,40 @@ +{ + "spec_version": "v1.0", + "decision_id": "c0000001-0000-4000-8000-000000000001", + "timestamp": "2026-04-25T10:30:00Z", + "decision_type": "scope.boundary", + "decision": "allow", + "reason": { + "code": "SCOPE_VALID", + "message": "All changed files are within declared workstream scope for 00-001-01", + "details": { + "changed_files": ["internal/x/foo.go", "internal/x/foo_test.go"], + "scope_files": ["internal/x/foo.go", "internal/x/foo_test.go"], + "drift_count": 0 + } + }, + "context": { + "request": { + "action": "write", + "resource": "internal/x/foo.go", + "parameters": {"ws_id": "00-001-01"} + }, + "actor": {"type": "agent", "id": "coder-agent", "roles": ["implementer"]}, + "environment": "production", + "workstream_id": "00-001-01", + "feature_id": "F001", + "session_id": "sess-abc123" + }, + "evidence": [ + { + "type": "test_result", + "reference": ".sdp/evidence/00-001-01-generation.json", + "summary": "All 12 tests passing" + }, + { + "type": "review_approval", + "reference": ".sdp/evidence/00-001-01-review.json", + "summary": "Code review approved, 0 findings" + } + ] +} diff --git a/schema/contracts/examples/runtime-decision-deny.json b/schema/contracts/examples/runtime-decision-deny.json new file mode 100644 index 00000000..888a9ce0 --- /dev/null +++ b/schema/contracts/examples/runtime-decision-deny.json @@ -0,0 +1,44 @@ +{ + "spec_version": "v1.0", + "decision_id": "c0000002-0000-4000-8000-000000000002", + "timestamp": "2026-04-25T10:35:00Z", + "decision_type": "security.approval", + "decision": "deny", + "reason": { + "code": "SECRET_DETECTED", + "message": "Secret scan found 1 credential leak in changed files", + "details": { + "findings": [ + { + "file": "internal/config/defaults.go", + "line": 42, + "rule": "hardcoded-api-key", + "severity": "critical" + } + ], + "total_findings": 1, + "critical_count": 1 + } + }, + "context": { + "request": { + "action": "deploy", + "resource": "sdp:prod", + "parameters": {"image_tag": "sdp:staging-abc123def456"} + }, + "actor": {"type": "system", "id": "ci-pipeline"}, + "environment": "production", + "workstream_id": "00-001-01", + "feature_id": "F001" + }, + "evidence": [ + { + "type": "security_scan", + "reference": ".sdp/secretscan/report.json", + "summary": "1 critical finding: hardcoded API key in defaults.go:42" + } + ], + "overrides": { + "overridden": false + } +} diff --git a/schema/contracts/examples/task-lifecycle.json b/schema/contracts/examples/task-lifecycle.json new file mode 100644 index 00000000..3fc02ef5 --- /dev/null +++ b/schema/contracts/examples/task-lifecycle.json @@ -0,0 +1,48 @@ +[ + { + "spec_version": "v1.0", + "event_id": "a0000001-0000-4000-8000-000000000001", + "timestamp": "2026-04-25T10:00:00Z", + "source": {"system": "sdp-lab", "component": "coder-agent"}, + "event_type": "task.started", + "payload": {"ws_id": "00-001-01", "action": "implement"}, + "context": {"workstream_id": "00-001-01", "feature_id": "F001"}, + "metadata": {"correlation_id": "task-001"} + }, + { + "spec_version": "v1.0", + "event_id": "a0000001-0000-4000-8000-000000000002", + "timestamp": "2026-04-25T10:05:00Z", + "source": {"system": "sdp-lab", "component": "coder-agent"}, + "event_type": "phase.transition", + "payload": {"from": "building", "to": "testing"}, + "metadata": {"correlation_id": "task-001"} + }, + { + "spec_version": "v1.0", + "event_id": "a0000001-0000-4000-8000-000000000003", + "timestamp": "2026-04-25T10:08:00Z", + "source": {"system": "sdp-lab", "component": "coder-agent"}, + "event_type": "evidence.generated", + "payload": {"type": "generation", "files_changed": ["internal/x/foo.go", "internal/x/foo_test.go"]}, + "metadata": {"correlation_id": "task-001"} + }, + { + "spec_version": "v1.0", + "event_id": "a0000001-0000-4000-8000-000000000004", + "timestamp": "2026-04-25T10:10:00Z", + "source": {"system": "sdp-lab", "component": "reviewer-agent"}, + "event_type": "quality.gate.passed", + "payload": {"gate": "evidence-gate", "findings_count": 0}, + "metadata": {"correlation_id": "task-001"} + }, + { + "spec_version": "v1.0", + "event_id": "a0000001-0000-4000-8000-000000000005", + "timestamp": "2026-04-25T10:12:00Z", + "source": {"system": "sdp-lab", "component": "coder-agent"}, + "event_type": "task.completed", + "payload": {"ws_id": "00-001-01", "duration_sec": 720}, + "metadata": {"correlation_id": "task-001"} + } +] diff --git a/schema/contracts/feature-card.schema.json b/schema/contracts/feature-card.schema.json new file mode 100644 index 00000000..3e4be9d5 --- /dev/null +++ b/schema/contracts/feature-card.schema.json @@ -0,0 +1,363 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://sdp.dev/contracts/feature-card/v1", + "title": "FeatureCard", + "type": "object", + "required": [ + "id", + "project_id", + "title", + "status", + "raw_request", + "created_at", + "updated_at" + ], + "properties": { + "id": { + "type": "string", + "pattern": "^feature-[a-z0-9_-]+-\\d{4}-\\d{2}-\\d{2}-\\d{3}$" + }, + "project_id": { + "type": "string", + "minLength": 1 + }, + "title": { + "type": "string", + "minLength": 1 + }, + "status": { + "type": "string", + "enum": [ + "inbox", + "clarifying", + "ready", + "executing", + "reviewing", + "blocked", + "done", + "parked", + "needs_input" + ] + }, + "raw_request": { + "type": "string", + "minLength": 1 + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "normalized_intent": { + "type": "string" + }, + "task_type": { + "type": "string", + "enum": [ + "feature", + "bugfix", + "refactor", + "review", + "research", + "process-design", + "release", + "mixed" + ] + }, + "execution_mode": { + "type": "string", + "enum": [ + "explore", + "plan", + "build", + "review", + "debug", + "docs", + "mixed" + ] + }, + "target_repo": { + "type": "string" + }, + "target_area": { + "type": "string" + }, + "scope_in": { + "type": "array", + "items": { + "type": "string" + } + }, + "scope_out": { + "type": "array", + "items": { + "type": "string" + } + }, + "non_goals": { + "type": "array", + "items": { + "type": "string" + } + }, + "risk_level": { + "type": "string", + "enum": [ + "low", + "medium", + "high", + "unknown" + ] + }, + "why_now": { + "type": "string" + }, + "links": { + "type": "array", + "items": { + "type": "string" + } + }, + "open_questions": { + "type": "array", + "items": { + "type": "string" + } + }, + "acceptance_shape": { + "type": "array", + "items": { + "type": "string" + } + }, + "recommended_next_step": { + "type": "string" + }, + "intake_artifact": { + "type": "array", + "items": { + "type": "string" + } + }, + "linked_beads_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "linked_workstreams": { + "type": "array", + "items": { + "type": "string" + } + }, + "required_artifacts": { + "type": "array", + "items": { + "type": "string" + } + }, + "required_checks": { + "type": "array", + "items": { + "type": "string" + } + }, + "linked_artifacts": { + "type": "array", + "items": { + "type": "string" + } + }, + "active_agents": { + "type": "array", + "items": { + "type": "string" + } + }, + "blocking_reasons": { + "type": "array", + "items": { + "type": "string" + } + }, + "waiting_on": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "human", + "dependency", + "review", + "qa", + "external" + ] + } + }, + "needs_feedback_from": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "author", + "admin", + "human" + ] + } + }, + "feedback_request": { + "type": "array", + "items": { + "type": "string" + } + }, + "decision_required": { + "type": "array", + "items": { + "type": "string" + } + }, + "author_update": { + "type": "array", + "items": { + "type": "string" + } + }, + "admin_action_required": { + "type": "array", + "items": { + "type": "string" + } + }, + "executor_session_id": { + "type": "string" + }, + "executor_started_at": { + "type": "string", + "format": "date-time" + }, + "last_executor_heartbeat_at": { + "type": "string", + "format": "date-time" + }, + "executor_runtime_state": { + "type": "string", + "enum": [ + "pending", + "running", + "stale", + "lost", + "completed" + ] + }, + "executor_progress_summary": { + "type": "string" + }, + "source_refs": { + "type": "array", + "items": { + "type": "string" + } + }, + "last_orchestrator_action": { + "type": "string" + }, + "last_orchestrator_reason": { + "type": "string" + }, + "last_orchestrator_at": { + "type": "string", + "format": "date-time" + }, + "recommended_next_action": { + "type": "string" + }, + "recommended_next_reason": { + "type": "string" + }, + "clarification_cycles": { + "type": "integer", + "minimum": 0 + }, + "blocked_cycles": { + "type": "integer", + "minimum": 0 + }, + "execution_attempt_count": { + "type": "integer", + "minimum": 0 + }, + "review_fail_count": { + "type": "integer", + "minimum": 0 + }, + "rollback_count": { + "type": "integer", + "minimum": 0 + }, + "review_state": { + "type": "string" + }, + "review_summary": { + "type": "string" + }, + "review_ref": { + "type": "string" + }, + "delivery_state": { + "type": "string" + }, + "delivery_target": { + "type": "string" + }, + "delivery_summary": { + "type": "string" + }, + "delivery_ref": { + "type": "string" + }, + "delivered_at": { + "type": "string", + "format": "date-time" + }, + "rollback_summary": { + "type": "string" + }, + "rollback_ref": { + "type": "string" + }, + "followup_refs": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "status": { + "const": "ready" + } + }, + "required": [ + "status" + ] + }, + "then": { + "required": [ + "normalized_intent", + "task_type", + "target_repo", + "risk_level", + "recommended_next_step" + ] + } + } + ], + "additionalProperties": false +} diff --git a/schema/contracts/openspec-import.schema.json b/schema/contracts/openspec-import.schema.json new file mode 100644 index 00000000..58b48360 --- /dev/null +++ b/schema/contracts/openspec-import.schema.json @@ -0,0 +1,371 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://sdp.dev/contracts/openspec-import/v1", + "title": "OpenSpecImportPayload", + "description": "Normalized contract for importing OpenSpec proposal, spec, design, and task artifacts into SDP planning surfaces", + "type": "object", + "required": [ + "spec_version", + "import_id", + "timestamp", + "source_metadata", + "artifacts" + ], + "properties": { + "spec_version": { + "type": "string", + "pattern": "^v\\d+\\.\\d+$", + "description": "Schema version (e.g., v1.0). Must follow semver format for compatibility tracking", + "examples": ["v1.0", "v1.1"] + }, + "import_id": { + "type": "string", + "format": "uuid", + "description": "Unique identifier for this import operation" + }, + "timestamp": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 timestamp when the import was initiated" + }, + "source_metadata": { + "type": "object", + "required": ["source_type", "source_location", "source_hash"], + "properties": { + "source_type": { + "type": "string", + "enum": ["openspec", "openspec-enterprise"], + "description": "Type of OpenSpec source" + }, + "source_location": { + "type": "string", + "description": "URI or path to the OpenSpec source (e.g., git repo URL, file path, API endpoint)" + }, + "source_hash": { + "type": "string", + "description": "Cryptographic hash of the source content for change detection (SHA-256 recommended)", + "examples": ["a3d5e9f4b2c1d8a6e7f4b3c2d1a9e8f7c6b5d4a3e2f1a9b8c7d6e5f4a3b2c1d"] + }, + "source_version": { + "type": "string", + "description": "Version identifier if source is versioned (e.g., git commit SHA, semver tag)", + "examples": ["a1b2c3d4e5f6", "v1.2.3"] + }, + "fetch_timestamp": { + "type": "string", + "format": "date-time", + "description": "When the source was fetched" + } + }, + "additionalProperties": false + }, + "mapping_confidence": { + "type": "object", + "required": ["overall_score", "confidence_level"], + "properties": { + "overall_score": { + "type": "number", + "minimum": 0, + "maximum": 1, + "description": "Overall confidence score for the mapping (0-1)" + }, + "confidence_level": { + "type": "string", + "enum": ["high", "medium", "low"], + "description": "Confidence level classification" + }, + "field_scores": { + "type": "object", + "description": "Per-field confidence scores", + "additionalProperties": { + "type": "number", + "minimum": 0, + "maximum": 1 + } + }, + "reasoning": { + "type": "string", + "description": "Human-readable explanation of the confidence assessment" + } + }, + "additionalProperties": false + }, + "unresolved_ambiguities": { + "type": "array", + "description": "List of ambiguities that could not be resolved during mapping", + "items": { + "type": "object", + "required": ["ambiguity_type", "description"], + "properties": { + "ambiguity_type": { + "type": "string", + "enum": ["field_mapping", "semantic_interpretation", "structural_mismatch", "missing_context", "conflicting_data"], + "description": "Type of ambiguity" + }, + "description": { + "type": "string", + "description": "Human-readable description of the ambiguity" + }, + "affected_fields": { + "type": "array", + "items": {"type": "string"}, + "description": "Fields affected by this ambiguity" + }, + "resolution_hint": { + "type": "string", + "description": "Suggested approach for resolving this ambiguity" + }, + "requires_human_review": { + "type": "boolean", + "description": "Whether this ambiguity requires human review" + } + }, + "additionalProperties": false + } + }, + "artifacts": { + "type": "object", + "required": ["proposal", "spec", "design", "tasks"], + "properties": { + "proposal": { + "type": "object", + "description": "OpenSpec proposal artifact", + "required": ["content", "format"], + "properties": { + "content": { + "type": "object", + "description": "Normalized proposal content", + "additionalProperties": true + }, + "format": { + "type": "string", + "enum": ["markdown", "json", "yaml", "openspec-v1"], + "description": "Format of the original proposal" + }, + "source_path": { + "type": "string", + "description": "Path or reference to the original proposal in the source" + }, + "metadata": { + "type": "object", + "description": "Additional proposal metadata", + "additionalProperties": true + } + }, + "additionalProperties": false + }, + "spec": { + "type": "object", + "description": "OpenSpec specification delta artifact", + "required": ["content", "format"], + "properties": { + "content": { + "type": "object", + "description": "Normalized spec content", + "additionalProperties": true + }, + "format": { + "type": "string", + "enum": ["openspec-spec-delta", "openspec-spec-full", "json"], + "description": "Format of the spec" + }, + "source_path": { + "type": "string", + "description": "Path or reference to the original spec in the source" + }, + "metadata": { + "type": "object", + "description": "Additional spec metadata", + "additionalProperties": true + } + }, + "additionalProperties": false + }, + "design": { + "type": "object", + "description": "OpenSpec design artifact", + "required": ["content", "format"], + "properties": { + "content": { + "type": "object", + "description": "Normalized design content", + "additionalProperties": true + }, + "format": { + "type": "string", + "enum": ["mermaid", "plantuml", "ascii", "json", "openspec-design-v1"], + "description": "Format of the design" + }, + "source_path": { + "type": "string", + "description": "Path or reference to the original design in the source" + }, + "diagram_type": { + "type": "string", + "description": "Type of diagram if applicable", + "examples": ["sequence", "flowchart", "architecture", "state-machine", "erd"] + }, + "metadata": { + "type": "object", + "description": "Additional design metadata", + "additionalProperties": true + } + }, + "additionalProperties": false + }, + "tasks": { + "type": "array", + "description": "OpenSpec task artifacts", + "items": { + "type": "object", + "required": ["task_id", "content", "format"], + "properties": { + "task_id": { + "type": "string", + "description": "Unique identifier for the task" + }, + "content": { + "type": "object", + "description": "Normalized task content", + "additionalProperties": true + }, + "format": { + "type": "string", + "enum": ["openspec-task-v1", "json", "markdown"], + "description": "Format of the task" + }, + "source_path": { + "type": "string", + "description": "Path or reference to the original task in the source" + }, + "dependencies": { + "type": "array", + "items": {"type": "string"}, + "description": "List of task IDs this task depends on" + }, + "metadata": { + "type": "object", + "description": "Additional task metadata", + "additionalProperties": true + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + }, + "import_options": { + "type": "object", + "description": "Options controlling the import behavior", + "properties": { + "create_workstreams": { + "type": "boolean", + "description": "Whether to create new workstreams for imported artifacts", + "default": false + }, + "update_existing": { + "type": "boolean", + "description": "Whether to update existing workstreams if they already exist", + "default": true + }, + "validate_contracts": { + "type": "boolean", + "description": "Whether to validate imported artifacts against SDP contracts", + "default": true + }, + "dry_run": { + "type": "boolean", + "description": "If true, validate and plan without making changes", + "default": false + }, + "target_feature": { + "type": "string", + "pattern": "^F\\d{3}$", + "description": "Target feature ID for the import (if applicable)" + } + }, + "additionalProperties": false + }, + "validation_results": { + "type": "object", + "description": "Results of validating the import payload", + "properties": { + "is_valid": { + "type": "boolean", + "description": "Whether the payload passed validation" + }, + "validation_errors": { + "type": "array", + "description": "List of validation errors if any", + "items": { + "type": "object", + "required": ["severity", "message"], + "properties": { + "severity": { + "type": "string", + "enum": ["error", "warning", "info"], + "description": "Severity of the validation issue" + }, + "message": { + "type": "string", + "description": "Human-readable validation message" + }, + "field": { + "type": "string", + "description": "Field path that caused the validation issue" + }, + "code": { + "type": "string", + "description": "Machine-readable error code" + } + }, + "additionalProperties": false + } + }, + "validated_at": { + "type": "string", + "format": "date-time", + "description": "When validation was performed" + } + }, + "additionalProperties": false + }, + "context": { + "type": "object", + "description": "Additional context for the import", + "properties": { + "workstream_id": { + "type": "string", + "pattern": "^00-\\d{3}-\\d{2}$", + "description": "SDP workstream ID if import is associated with one" + }, + "feature_id": { + "type": "string", + "pattern": "^F\\d{3}$", + "description": "SDP feature ID if import is associated with one" + }, + "session_id": { + "type": "string", + "description": "Session ID that initiated the import" + }, + "import_chain_id": { + "type": "string", + "description": "ID for chaining related imports" + }, + "git_context": { + "type": "object", + "description": "Git repository context", + "properties": { + "branch": {"type": "string"}, + "commit_sha": {"type": "string"}, + "repo_url": {"type": "string", "format": "uri"} + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false +} diff --git a/schema/contracts/portfolio-board-snapshot.schema.json b/schema/contracts/portfolio-board-snapshot.schema.json new file mode 100644 index 00000000..abbe4ec3 --- /dev/null +++ b/schema/contracts/portfolio-board-snapshot.schema.json @@ -0,0 +1,299 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://sdp.dev/contracts/portfolio-board-snapshot/v1", + "title": "PortfolioBoardSnapshot", + "type": "object", + "required": [ + "spec_version", + "timestamp", + "projects", + "totals", + "queues", + "next_action" + ], + "properties": { + "spec_version": { + "type": "string", + "pattern": "^v\\d+\\.\\d+$" + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "projects": { + "type": "array", + "items": { + "type": "object", + "required": [ + "project_id", + "name", + "counts" + ], + "properties": { + "project_id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "counts": { + "type": "object", + "properties": { + "inbox": { + "type": "integer", + "minimum": 0 + }, + "clarifying": { + "type": "integer", + "minimum": 0 + }, + "ready": { + "type": "integer", + "minimum": 0 + }, + "executing": { + "type": "integer", + "minimum": 0 + }, + "reviewing": { + "type": "integer", + "minimum": 0 + }, + "blocked": { + "type": "integer", + "minimum": 0 + }, + "done": { + "type": "integer", + "minimum": 0 + }, + "parked": { + "type": "integer", + "minimum": 0 + }, + "needs_input": { + "type": "integer", + "minimum": 0 + } + }, + "additionalProperties": false + }, + "next_action": { + "type": "object", + "properties": { + "recommended": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "target_card_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "totals": { + "type": "object", + "properties": { + "inbox": { + "type": "integer", + "minimum": 0 + }, + "clarifying": { + "type": "integer", + "minimum": 0 + }, + "ready": { + "type": "integer", + "minimum": 0 + }, + "executing": { + "type": "integer", + "minimum": 0 + }, + "reviewing": { + "type": "integer", + "minimum": 0 + }, + "blocked": { + "type": "integer", + "minimum": 0 + }, + "done": { + "type": "integer", + "minimum": 0 + }, + "parked": { + "type": "integer", + "minimum": 0 + }, + "needs_input": { + "type": "integer", + "minimum": 0 + } + }, + "additionalProperties": false + }, + "queues": { + "type": "object", + "properties": { + "waiting_on_human": { + "type": "array", + "items": { + "$ref": "#/$defs/queueItem" + } + }, + "ready_to_execute": { + "type": "array", + "items": { + "$ref": "#/$defs/queueItem" + } + }, + "blocked": { + "type": "array", + "items": { + "$ref": "#/$defs/queueItem" + } + } + }, + "additionalProperties": false + }, + "next_action": { + "type": "object", + "required": [ + "recommended", + "reason" + ], + "properties": { + "recommended": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "target_project_id": { + "type": "string" + }, + "target_card_id": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "$defs": { + "queueItem": { + "type": "object", + "required": [ + "project_id", + "card_id", + "title", + "status" + ], + "properties": { + "project_id": { + "type": "string" + }, + "card_id": { + "type": "string" + }, + "title": { + "type": "string" + }, + "status": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "recommended_next_step": { + "type": "string" + }, + "active_agents": { + "type": "array", + "items": { + "type": "string" + } + }, + "needs_feedback_from": { + "type": "array", + "items": { + "type": "string" + } + }, + "author_update": { + "type": "array", + "items": { + "type": "string" + } + }, + "admin_action_required": { + "type": "array", + "items": { + "type": "string" + } + }, + "recommended_next_action": { + "type": "string" + }, + "recommended_next_reason": { + "type": "string" + }, + "last_orchestrator_action": { + "type": "string" + }, + "clarification_cycles": { + "type": "integer", + "minimum": 0 + }, + "blocked_cycles": { + "type": "integer", + "minimum": 0 + }, + "execution_attempt_count": { + "type": "integer", + "minimum": 0 + }, + "review_fail_count": { + "type": "integer", + "minimum": 0 + }, + "rollback_count": { + "type": "integer", + "minimum": 0 + }, + "review_state": { + "type": "string" + }, + "delivery_state": { + "type": "string" + }, + "delivery_target": { + "type": "string" + }, + "rollback_ref": { + "type": "string" + }, + "followup_refs": { + "type": "array", + "items": { + "type": "string" + } + }, + "has_rollback": { + "type": "boolean" + }, + "has_followup": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false +} diff --git a/schema/contracts/project-board-snapshot.schema.json b/schema/contracts/project-board-snapshot.schema.json new file mode 100644 index 00000000..035bdfcc --- /dev/null +++ b/schema/contracts/project-board-snapshot.schema.json @@ -0,0 +1,325 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://sdp.dev/contracts/project-board-snapshot/v1", + "title": "ProjectBoardSnapshot", + "type": "object", + "required": [ + "spec_version", + "timestamp", + "project", + "columns", + "counts", + "next_action" + ], + "properties": { + "spec_version": { + "type": "string", + "pattern": "^v\\d+\\.\\d+$" + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "project": { + "type": "object", + "required": [ + "project_id", + "name" + ], + "properties": { + "project_id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "beads_prefix": { + "type": "string" + }, + "repo_url": { + "type": "string" + } + }, + "additionalProperties": false + }, + "counts": { + "type": "object", + "required": [ + "inbox", + "clarifying", + "ready", + "executing", + "reviewing", + "blocked", + "done" + ], + "properties": { + "inbox": { + "type": "integer", + "minimum": 0 + }, + "clarifying": { + "type": "integer", + "minimum": 0 + }, + "ready": { + "type": "integer", + "minimum": 0 + }, + "executing": { + "type": "integer", + "minimum": 0 + }, + "reviewing": { + "type": "integer", + "minimum": 0 + }, + "blocked": { + "type": "integer", + "minimum": 0 + }, + "done": { + "type": "integer", + "minimum": 0 + }, + "parked": { + "type": "integer", + "minimum": 0 + }, + "needs_input": { + "type": "integer", + "minimum": 0 + } + }, + "additionalProperties": false + }, + "columns": { + "type": "object", + "required": [ + "inbox", + "clarifying", + "ready", + "executing", + "reviewing", + "blocked", + "done" + ], + "properties": { + "inbox": { + "$ref": "#/$defs/cardArray" + }, + "clarifying": { + "$ref": "#/$defs/cardArray" + }, + "ready": { + "$ref": "#/$defs/cardArray" + }, + "executing": { + "$ref": "#/$defs/cardArray" + }, + "reviewing": { + "$ref": "#/$defs/cardArray" + }, + "blocked": { + "$ref": "#/$defs/cardArray" + }, + "done": { + "$ref": "#/$defs/cardArray" + }, + "parked": { + "$ref": "#/$defs/cardArray" + }, + "needs_input": { + "$ref": "#/$defs/cardArray" + } + }, + "additionalProperties": false + }, + "execution_summary": { + "type": "object", + "properties": { + "ready_count": { + "type": "integer", + "minimum": 0 + }, + "blocked_count": { + "type": "integer", + "minimum": 0 + }, + "in_progress_count": { + "type": "integer", + "minimum": 0 + }, + "next_action": { + "type": "object", + "properties": { + "recommended": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "command": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "next_action": { + "type": "object", + "required": [ + "recommended", + "reason" + ], + "properties": { + "recommended": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "target_card_id": { + "type": "string" + }, + "command": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "$defs": { + "cardSummary": { + "type": "object", + "required": [ + "id", + "title", + "status" + ], + "properties": { + "id": { + "type": "string" + }, + "title": { + "type": "string" + }, + "status": { + "type": "string" + }, + "risk_level": { + "type": "string" + }, + "recommended_next_step": { + "type": "string" + }, + "active_agents": { + "type": "array", + "items": { + "type": "string" + } + }, + "waiting_on": { + "type": "array", + "items": { + "type": "string" + } + }, + "needs_feedback_from": { + "type": "array", + "items": { + "type": "string" + } + }, + "author_update": { + "type": "array", + "items": { + "type": "string" + } + }, + "admin_action_required": { + "type": "array", + "items": { + "type": "string" + } + }, + "linked_beads_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "recommended_next_action": { + "type": "string" + }, + "recommended_next_reason": { + "type": "string" + }, + "last_orchestrator_action": { + "type": "string" + }, + "last_orchestrator_reason": { + "type": "string" + }, + "last_orchestrator_at": { + "type": "string", + "format": "date-time" + }, + "clarification_cycles": { + "type": "integer", + "minimum": 0 + }, + "blocked_cycles": { + "type": "integer", + "minimum": 0 + }, + "execution_attempt_count": { + "type": "integer", + "minimum": 0 + }, + "review_fail_count": { + "type": "integer", + "minimum": 0 + }, + "rollback_count": { + "type": "integer", + "minimum": 0 + }, + "review_state": { + "type": "string" + }, + "delivery_state": { + "type": "string" + }, + "delivery_target": { + "type": "string" + }, + "rollback_ref": { + "type": "string" + }, + "followup_refs": { + "type": "array", + "items": { + "type": "string" + } + }, + "has_rollback": { + "type": "boolean" + }, + "has_followup": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "cardArray": { + "type": "array", + "items": { + "$ref": "#/$defs/cardSummary" + } + } + }, + "additionalProperties": false +} diff --git a/schema/evidence.schema.json b/schema/evidence.schema.json index 698c1032..9e5247a9 100644 --- a/schema/evidence.schema.json +++ b/schema/evidence.schema.json @@ -1,5 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://sdp.dev/schema/evidence/v1", "title": "SDP Evidence Event", "description": "Evidence log event β€” base + 6 types: plan, generation, verification, approval, decision, lesson", "type": "object", diff --git a/schema/failure-taxonomy.schema.json b/schema/failure-taxonomy.schema.json index fe97653b..1a9e96ee 100644 --- a/schema/failure-taxonomy.schema.json +++ b/schema/failure-taxonomy.schema.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://sdp.dev/schemas/failure-taxonomy.schema.json", + "$id": "https://sdp.dev/schema/failure-taxonomy/v1", "title": "AI Failure Taxonomy Schema", "description": "Schema for classifying AI code generation failures by type, severity, and context (AC1, AC2)", "type": "array", diff --git a/schema/findings/examples/docs-findings-example.json b/schema/findings/examples/docs-findings-example.json new file mode 100644 index 00000000..0b68306f --- /dev/null +++ b/schema/findings/examples/docs-findings-example.json @@ -0,0 +1,110 @@ +{ + "spec_version": "v1.0", + "findings_id": "c3d4e5f6-7890-abcd-ef12-34567890abcd", + "timestamp": "2024-01-15T10:35:00Z", + "source": { + "check_name": "sdp-doc-sync", + "workflow": "CI", + "run_id": 1234567890, + "run_number": 42, + "repository": "fall-out-bug/sdp_lab", + "branch": "feature/F077-findings-schema", + "commit_sha": "abc123def456789012345678901234abcd" + }, + "configuration": { + "check_links": true, + "strict_mode": false, + "changelog_mode": true + }, + "findings": [ + { + "finding_key": "e5f67890abcdef12", + "severity": "error", + "category": "broken_link", + "code": "LINK_404", + "file": "docs/protocol/CONTRACTS.md", + "line": 45, + "column": 1, + "message": "Link to './schema/contracts/old.schema.json' does not exist", + "remediation": { + "hint": "Update link to point to correct schema file", + "action": "update", + "suggested_fix": "../../schema/contracts/runtime-decision.schema.json", + "doc_url": "https://sdp.dev/docs/links" + }, + "context": { + "link_target": "./schema/contracts/old.schema.json", + "link_text": "old schema", + "section": "Contract Schemas" + } + }, + { + "finding_key": "f67890abcdef1234", + "severity": "warning", + "category": "changelog", + "code": "MISSING_CHANGELOG_ENTRY", + "file": "docs/CHANGELOG.md", + "line": 1, + "message": "No changelog entry for commit abc123 on feature branch", + "remediation": { + "hint": "Add changelog entry following Keep a Changelog format", + "action": "add", + "template": "### Added\n- F077: Findings schema for CI artifacts\n" + } + }, + { + "finding_key": "7890abcdef123456", + "severity": "warning", + "category": "broken_link", + "code": "BROKEN_ANCHOR", + "file": "docs/protocol/FINDINGS_SCHEMA.md", + "line": 120, + "column": 5, + "message": "Anchor #versioning-strategy does not exist in target file", + "remediation": { + "hint": "Fix anchor name or update target section", + "action": "fix" + }, + "context": { + "link_target": "./CONTRACTS.md#versioning-strategy", + "link_text": "versioning strategy", + "expected": "#versioning-strategy", + "actual": "#versioning" + } + }, + { + "finding_key": "90abcdef12345678", + "severity": "info", + "category": "consistency", + "code": "OUTDATED_INDEX", + "file": "docs/workstreams/INDEX.md", + "line": 42, + "message": "INDEX.md entry for 00-077-01 is outdated (status: backlog, actual: done)", + "remediation": { + "hint": "Update INDEX.md to reflect current workstream status", + "action": "update" + }, + "context": { + "expected": "status: done", + "actual": "status: backlog", + "related_files": ["docs/workstreams/backlog/00-077-01.md"] + } + } + ], + "summary": { + "total": 4, + "by_severity": { + "error": 1, + "warning": 2, + "info": 1, + "hint": 0 + }, + "by_category": { + "broken_link": 2, + "changelog": 1, + "consistency": 1 + }, + "links_checked": 156, + "files_checked": 42 + } +} diff --git a/schema/findings/examples/protocol-findings-example.json b/schema/findings/examples/protocol-findings-example.json new file mode 100644 index 00000000..72f12134 --- /dev/null +++ b/schema/findings/examples/protocol-findings-example.json @@ -0,0 +1,93 @@ +{ + "spec_version": "v1.0", + "findings_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "timestamp": "2024-01-15T10:30:00Z", + "source": { + "check_name": "sdp-protocol-check", + "workflow": "CI", + "run_id": 1234567890, + "run_number": 42, + "repository": "fall-out-bug/sdp_lab", + "branch": "feature/F077-findings-schema", + "commit_sha": "abc123def456789012345678901234abcd" + }, + "configuration": { + "strict_beads": true, + "strict_all": false, + "legacy_features": ["F001", "F002", "F003"] + }, + "findings": [ + { + "finding_key": "a1b2c3d4e5f67890", + "severity": "error", + "category": "beads", + "code": "PLACEHOLDER_ID", + "file": "docs/workstreams/backlog/00-077-01.md", + "line": 20, + "message": "Beads entry must reference concrete issue id (sdplab-) in strict mode - placeholder detected", + "remediation": { + "hint": "Replace sdplab-XX with actual Beads issue ID", + "action": "update", + "template": "- sdplab-abc123: F077-01 findings-schema", + "doc_url": "https://sdp.dev/docs/beads-section" + }, + "context": { + "feature_id": "F077", + "ws_id": "00-077-01" + } + }, + { + "finding_key": "b2c3d4e5f6789012", + "severity": "warning", + "category": "acceptance_criteria", + "code": "MISSING_AC", + "file": "docs/workstreams/backlog/00-078-01.md", + "line": 35, + "message": "Acceptance Criteria section must contain at least one checkbox item", + "remediation": { + "hint": "Add checkbox items for each acceptance criterion", + "action": "add", + "template": "- [ ] Criterion description" + } + }, + { + "finding_key": "c3d4e5f678901234", + "severity": "warning", + "category": "frontmatter", + "code": "INVALID_WS_ID", + "file": "docs/workstreams/backlog/00-079-01.md", + "line": 3, + "message": "ws_id must match pattern 00-\\d{3}-\\d{2}", + "remediation": { + "hint": "Fix ws_id format to match 00-XXX-YY pattern", + "action": "fix" + }, + "context": { + "ws_id": "00-079-01" + } + }, + { + "finding_key": "d4e5f67890123456", + "severity": "info", + "category": "feature_consistency", + "code": "LEGACY_FEATURE", + "file": "docs/workstreams/backlog/00-010-01.md", + "message": "Feature F010 is marked as legacy and not tracked in ROADMAP.md" + } + ], + "summary": { + "total": 4, + "by_severity": { + "error": 1, + "warning": 2, + "info": 1, + "hint": 0 + }, + "by_category": { + "beads": 1, + "acceptance_criteria": 1, + "frontmatter": 1, + "feature_consistency": 1 + } + } +} diff --git a/schema/harness-config-manifest.schema.json b/schema/harness-config-manifest.schema.json new file mode 100644 index 00000000..a2d330e9 --- /dev/null +++ b/schema/harness-config-manifest.schema.json @@ -0,0 +1,60 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://sdp.dev/schema/harness-config-manifest/v1", + "title": "Harness Config Manifest", + "description": "Describes which AI coding harnesses are active for a project, their config file paths, the project lifecycle stage, and the rules document to generate. Used for drift detection and onboarding automation.", + "type": "object", + "required": ["version", "lifecycle_stage", "harnesses"], + "additionalProperties": false, + "properties": { + "version": { + "type": "string", + "description": "Semver version of this manifest schema. Increment on breaking changes to enable drift detection.", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", + "examples": ["1.0.0", "1.2.3-alpha.1"] + }, + "lifecycle_stage": { + "type": "string", + "description": "Project maturity stage. Controls which generation rules and defaults are applied.", + "enum": ["greenfield", "brownfield-new", "brownfield-mature"] + }, + "harnesses": { + "type": "array", + "description": "List of AI coding harnesses configured for this project.", + "minItems": 1, + "items": { "$ref": "#/definitions/harness" } + }, + "language": { + "type": "string", + "description": "Primary programming language of the project.", + "examples": ["go", "typescript", "python", "rust"] + }, + "rules_file": { + "type": "string", + "description": "Path (relative to project root) to the generated harness-agnostic patterns document.", + "examples": ["docs/reference/go-patterns.md"] + } + }, + "definitions": { + "harness": { + "type": "object", + "title": "Harness", + "required": ["name", "config_file"], + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "enum": ["claude-code", "codex-cli", "cursor", "opencode", "copilot", "zed", "warp"] + }, + "config_file": { + "type": "string", + "examples": ["CLAUDE.md", "AGENTS.md", ".cursor/rules/project.mdc"] + }, + "enabled": { + "type": "boolean", + "default": true + } + } + } + } +} diff --git a/schema/index.json b/schema/index.json index 2b98ec8c..711f13ea 100644 --- a/schema/index.json +++ b/schema/index.json @@ -9,6 +9,7 @@ { "id": "coding-workflow-predicate", "path": "coding-workflow-predicate.schema.json", "title": "SDP Coding Workflow Predicate (in-toto v1)" }, { "id": "coding-workflow-statement", "path": "coding-workflow-statement.schema.json", "title": "SDP Coding Workflow in-toto Statement (v1 envelope)" }, { "id": "review-verdict", "path": "review-verdict.schema.json", "title": "SDP Review Verdict" }, + { "id": "pi-review-run", "path": "pi-review-run.schema.json", "title": "SDP Pi Review Run" }, { "id": "ws-verdict", "path": "ws-verdict.schema.json", "title": "SDP Workstream Verdict" }, { "id": "next-action", "path": "next-action.schema.json", "title": "SDP Next Action" }, { "id": "instructions", "path": "contracts/instructions.schema.json", "title": "Instruction Payload Contract" }, diff --git a/schema/intent.schema.json b/schema/intent.schema.json index f7f1ff89..a724ea50 100644 --- a/schema/intent.schema.json +++ b/schema/intent.schema.json @@ -1 +1 @@ -{"$schema":"http://json-schema.org/draft-07/schema#","title":"SDP Intent Specification","description":"Machine-readable feature intent specification for AI-human communication","type":"object","required":["problem","users","success_criteria"],"properties":{"problem":{"type":"string","minLength":50,"description":"What problem does this feature solve? Be specific."},"users":{"type":"array","minItems":1,"maxItems":10,"items":{"type":"string","enum":["end_users","admins","developers","api_consumers","operators"]},"description":"Who are the primary users of this feature?"},"success_criteria":{"type":"array","minItems":1,"items":{"type":"object","required":["criterion","measurement"],"properties":{"criterion":{"type":"string","minLength":10},"measurement":{"type":"string","minLength":5}}},"description":"How do we measure success?"},"tradeoffs":{"type":"object","properties":{"security":{"enum":["prioritize","accept","reject"]},"performance":{"enum":["prioritize","accept","reject"]},"complexity":{"enum":["prioritize","accept","reject"]},"time_to_market":{"enum":["prioritize","accept","reject"]}}},"technical_approach":{"type":"object","properties":{"architecture":{"type":"string","enum":["monolith","microservices","serverless","event_driven"]},"storage":{"type":"string","enum":["relational_db","nosql","in_memory","file_system","none"]},"failure_mode":{"type":"string","enum":["graceful_degradation","fail_closed","queue_retry","best_effort"]},"auth_method":{"type":"string","enum":["jwt","session","oauth2","api_key","none"]}}}}} +{"$schema":"http://json-schema.org/draft-07/schema#","$id":"https://sdp.dev/schema/intent/v1","title":"SDP Intent Specification","description":"Machine-readable feature intent specification for AI-human communication","type":"object","required":["problem","users","success_criteria"],"properties":{"problem":{"type":"string","minLength":50,"description":"What problem does this feature solve? Be specific."},"users":{"type":"array","minItems":1,"maxItems":10,"items":{"type":"string","enum":["end_users","admins","developers","api_consumers","operators"]},"description":"Who are the primary users of this feature?"},"success_criteria":{"type":"array","minItems":1,"items":{"type":"object","required":["criterion","measurement"],"properties":{"criterion":{"type":"string","minLength":10},"measurement":{"type":"string","minLength":5}}},"description":"How do we measure success?"},"tradeoffs":{"type":"object","properties":{"security":{"enum":["prioritize","accept","reject"]},"performance":{"enum":["prioritize","accept","reject"]},"complexity":{"enum":["prioritize","accept","reject"]},"time_to_market":{"enum":["prioritize","accept","reject"]}}},"technical_approach":{"type":"object","properties":{"architecture":{"type":"string","enum":["monolith","microservices","serverless","event_driven"]},"storage":{"type":"string","enum":["relational_db","nosql","in_memory","file_system","none"]},"failure_mode":{"type":"string","enum":["graceful_degradation","fail_closed","queue_retry","best_effort"]},"auth_method":{"type":"string","enum":["jwt","session","oauth2","api_key","none"]}}}}} diff --git a/schema/orchestrate-checkpoint.schema.json b/schema/orchestrate-checkpoint.schema.json new file mode 100644 index 00000000..73e95faa --- /dev/null +++ b/schema/orchestrate-checkpoint.schema.json @@ -0,0 +1,166 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://sdp.dev/schema/orchestrate-checkpoint.schema.json", + "title": "Orchestrate Checkpoint", + "description": "Checkpoint schema for sdp-orchestrate state machine", + "type": "object", + "required": ["schema", "feature_id", "branch", "phase"], + "properties": { + "schema": { + "type": "string", + "const": "orchestrate.v1", + "description": "Schema version identifier" + }, + "feature_id": { + "type": "string", + "pattern": "^F[0-9]{3}$", + "description": "Feature ID (e.g., F016)" + }, + "branch": { + "type": "string", + "minLength": 1, + "description": "Git branch name" + }, + "pr_number": { + "type": "integer", + "minimum": 1, + "description": "Pull request number" + }, + "pr_url": { + "type": "string", + "description": "Pull request URL (empty if no PR)" + }, + "phase": { + "type": "string", + "enum": ["init", "build", "review", "pr", "ci", "qa", "done"], + "description": "Current orchestration phase" + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 creation timestamp" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 last update timestamp" + }, + "workstreams": { + "type": "array", + "items": { + "type": "object", + "required": ["id", "status"], + "properties": { + "id": { + "type": "string", + "pattern": "^[0-9]{2}-[0-9]{3}-[0-9]{2}$", + "description": "Workstream ID (e.g., 00-042-03)" + }, + "status": { + "type": "string", + "enum": ["pending", "in_progress", "done"], + "description": "Workstream execution status" + }, + "verdict_file": { + "type": "string", + "description": "Path to verdict file" + }, + "commit": { + "type": "string", + "description": "Git commit hash" + }, + "attempts": { + "type": "integer", + "minimum": 0, + "description": "Number of execution attempts" + }, + "dispatch": { + "type": "object", + "properties": { + "harness": { + "type": "string", + "description": "Harness name (e.g., claude, opencode)" + }, + "provider": { + "type": "string", + "description": "LLM provider" + }, + "model": { + "type": "string", + "description": "Model name" + }, + "score": { + "type": "number", + "description": "Dispatch confidence score" + }, + "reason": { + "type": "string", + "description": "Reasoning for dispatch choice" + }, + "timestamp": { + "type": "string", + "format": "date-time", + "description": "Dispatch timestamp" + }, + "cold_start": { + "type": "boolean", + "description": "Whether this was a cold start dispatch" + } + } + } + } + }, + "description": "List of workstreams and their status" + }, + "review": { + "type": "object", + "required": ["iteration", "status"], + "properties": { + "iteration": { + "type": "integer", + "minimum": 0, + "description": "Review iteration number" + }, + "verdict_file": { + "type": "string", + "description": "Path to review verdict file" + }, + "status": { + "type": "string", + "enum": ["pending", "approved"], + "description": "Review status" + } + }, + "description": "Review phase status" + }, + "qa": { + "type": "object", + "required": ["iteration", "status"], + "properties": { + "iteration": { + "type": "integer", + "minimum": 0, + "description": "QA iteration number" + }, + "verdict_file": { + "type": "string", + "description": "Path to QA verdict file" + }, + "status": { + "type": "string", + "description": "QA status" + }, + "evidence_ref": { + "type": "string", + "description": "Reference to QA evidence" + } + }, + "description": "QA phase status" + }, + "integrity": { + "type": "string", + "pattern": "^sha256:[a-f0-9]{64}$", + "description": "SHA-256 hash of checkpoint content for integrity verification" + } + } +} diff --git a/schema/pi-review-run.schema.json b/schema/pi-review-run.schema.json new file mode 100644 index 00000000..18b9e1cb --- /dev/null +++ b/schema/pi-review-run.schema.json @@ -0,0 +1,259 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://sdp.dev/schema/pi-review-run/v1", + "title": "SDP Pi Review Run", + "description": "Telemetry manifest for a single sdp pi-review run. Raw model outputs live beside this manifest and are referenced by path and sha256.", + "type": "object", + "required": [ + "run_id", + "timestamp", + "scope", + "context_packet", + "models", + "test_evidence", + "verdict_ref" + ], + "properties": { + "run_id": { + "type": "string", + "minLength": 1, + "pattern": "^[A-Za-z0-9._-]+$" + }, + "feature": { + "type": "string", + "pattern": "^F\\d{3}$" + }, + "workstreams": { + "type": "array", + "items": { + "type": "string", + "pattern": "^\\d{2}-\\d{3}-\\d{2}$" + } + }, + "round": { + "type": "integer", + "minimum": 1 + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "scope": { + "type": "object", + "required": [ + "mode", + "reviewed_files" + ], + "properties": { + "mode": { + "type": "string", + "enum": [ + "auto", + "working-tree", + "branch" + ] + }, + "base": { + "type": "string" + }, + "branch": { + "type": "string" + }, + "head_sha": { + "type": "string" + }, + "pr_number": { + "type": "integer", + "minimum": 1 + }, + "reviewed_files": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + } + }, + "omitted_files": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + } + } + }, + "additionalProperties": false + }, + "context_packet": { + "type": "object", + "required": [ + "path", + "sha256", + "diff_sha256", + "rules_sha256", + "file_hashes" + ], + "properties": { + "path": { + "type": "string", + "minLength": 1 + }, + "sha256": { + "$ref": "#/$defs/sha256" + }, + "diff_sha256": { + "$ref": "#/$defs/sha256" + }, + "rules_sha256": { + "$ref": "#/$defs/sha256" + }, + "test_evidence_sha256": { + "$ref": "#/$defs/sha256" + }, + "file_hashes": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/sha256" + } + } + }, + "additionalProperties": false + }, + "test_evidence": { + "type": "object", + "required": [ + "status", + "artifact_path" + ], + "properties": { + "status": { + "type": "string", + "enum": [ + "passed", + "failed", + "skipped" + ] + }, + "command": { + "type": "string" + }, + "exit_code": { + "type": "integer" + }, + "duration_ms": { + "type": "integer", + "minimum": 0 + }, + "artifact_path": { + "type": "string", + "minLength": 1 + }, + "skip_reason": { + "type": "string" + } + }, + "additionalProperties": false + }, + "models": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/modelRun" + } + }, + "verdict_ref": { + "type": "object", + "required": [ + "path", + "sha256" + ], + "properties": { + "path": { + "type": "string", + "minLength": 1 + }, + "sha256": { + "$ref": "#/$defs/sha256" + } + }, + "additionalProperties": false + } + }, + "$defs": { + "sha256": { + "type": "string", + "pattern": "^[a-f0-9]{64}$" + }, + "modelRun": { + "type": "object", + "required": [ + "slot", + "provider", + "model", + "role", + "status", + "artifact_path" + ], + "properties": { + "slot": { + "type": "string", + "minLength": 1 + }, + "provider": { + "type": "string", + "minLength": 1 + }, + "model": { + "type": "string", + "minLength": 1 + }, + "role": { + "type": "string", + "enum": [ + "reviewer", + "synthesizer", + "fallback" + ] + }, + "status": { + "type": "string", + "enum": [ + "ok", + "failed", + "skipped" + ] + }, + "artifact_path": { + "type": "string", + "minLength": 1 + }, + "latency_ms": { + "type": "integer", + "minimum": 0 + }, + "usage": { + "type": "object", + "properties": { + "input_tokens": { + "type": "integer", + "minimum": 0 + }, + "output_tokens": { + "type": "integer", + "minimum": 0 + }, + "cost_usd": { + "type": "number", + "minimum": 0 + } + }, + "additionalProperties": false + }, + "error": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false +} diff --git a/schema/review-verdict.schema.json b/schema/review-verdict.schema.json index f7b886c8..cea89fd7 100644 --- a/schema/review-verdict.schema.json +++ b/schema/review-verdict.schema.json @@ -11,6 +11,42 @@ "round", "timestamp" ], + "allOf": [ + { + "if": { + "properties": { + "verdict": { + "const": "PARTIALLY_APPROVED" + } + }, + "required": [ + "verdict" + ] + }, + "then": { + "required": [ + "partial_failing_roles" + ] + } + }, + { + "if": { + "properties": { + "verdict": { + "const": "ESCALATED" + } + }, + "required": [ + "verdict" + ] + }, + "then": { + "required": [ + "escalation_issue" + ] + } + } + ], "properties": { "feature": { "type": "string", @@ -21,9 +57,11 @@ "type": "string", "enum": [ "APPROVED", - "CHANGES_REQUESTED" + "CHANGES_REQUESTED", + "PARTIALLY_APPROVED", + "ESCALATED" ], - "description": "Overall verdict. APPROVED only if all reviewers PASS." + "description": "Overall verdict. APPROVED only if all reviewers PASS. PARTIALLY_APPROVED: some reviewers passed, issues filed for failing. ESCALATED: deferred to human review." }, "round": { "type": "integer", @@ -135,9 +173,205 @@ "summary": { "type": "string", "description": "Brief human-readable summary of the review" + }, + "reviewer_runtime": { + "type": "string", + "description": "Runtime that produced the verdict, e.g. native @review or pi." + }, + "context_packet": { + "type": "object", + "description": "Optional context packet metadata for external review runtimes.", + "properties": { + "path": { + "type": "string" + }, + "sha256": { + "$ref": "#/$defs/sha256" + }, + "diff_sha256": { + "$ref": "#/$defs/sha256" + }, + "test_evidence_sha256": { + "$ref": "#/$defs/sha256" + }, + "reviewed_files": { + "type": "array", + "items": { + "type": "string" + } + }, + "omitted_files": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "model_panel": { + "type": "array", + "description": "Optional model run summary for verdicts produced by an external model panel.", + "items": { + "$ref": "#/$defs/modelRun" + } + }, + "raw_artifacts": { + "type": "array", + "description": "Raw review artifacts referenced by relative path and hash.", + "items": { + "$ref": "#/$defs/rawArtifact" + } + }, + "findings_detail": { + "type": "array", + "description": "Optional structured finding details in addition to beads finding IDs.", + "items": { + "$ref": "#/$defs/findingDetail" + } + }, + "override_reason": { + "type": "string", + "minLength": 1, + "pattern": "\\S", + "description": "Justification when verdict overridden via --override" + }, + "partial_failing_roles": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "qa", + "security", + "devops", + "sre", + "techlead", + "docs", + "promptops" + ] + }, + "description": "Roles that failed in PARTIALLY_APPROVED verdict" + }, + "escalation_issue": { + "type": "string", + "minLength": 1, + "pattern": "\\S", + "description": "Beads issue ID for human escalation" } }, "$defs": { + "sha256": { + "type": "string", + "pattern": "^[a-f0-9]{64}$" + }, + "modelRun": { + "type": "object", + "required": [ + "provider", + "model", + "role", + "status" + ], + "properties": { + "slot": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "model": { + "type": "string" + }, + "role": { + "type": "string", + "enum": [ + "reviewer", + "synthesizer", + "fallback" + ] + }, + "status": { + "type": "string", + "enum": [ + "ok", + "failed", + "skipped" + ] + }, + "artifact_path": { + "type": "string" + }, + "latency_ms": { + "type": "integer", + "minimum": 0 + } + } + }, + "rawArtifact": { + "type": "object", + "required": [ + "path", + "sha256" + ], + "properties": { + "path": { + "type": "string" + }, + "sha256": { + "$ref": "#/$defs/sha256" + }, + "model": { + "type": "string" + } + } + }, + "findingDetail": { + "type": "object", + "required": [ + "priority", + "title" + ], + "properties": { + "priority": { + "type": "string", + "enum": [ + "P0", + "P1", + "P2", + "P3" + ] + }, + "title": { + "type": "string" + }, + "file": { + "type": "string" + }, + "start_line": { + "type": "integer", + "minimum": 1 + }, + "end_line": { + "type": "integer", + "minimum": 1 + }, + "reviewer": { + "type": "string" + }, + "rationale": { + "type": "string" + }, + "suggested_fix": { + "type": "string" + }, + "dedupe_key": { + "type": "string" + }, + "bead_id": { + "type": "string" + } + } + }, "reviewerResult": { "type": "object", "required": [ @@ -149,7 +383,8 @@ "type": "string", "enum": [ "PASS", - "FAIL" + "FAIL", + "BLOCKED" ] }, "findings": { @@ -182,4 +417,4 @@ } } } -} \ No newline at end of file +} diff --git a/schema/sdp-pr-gate/decision-record.schema.json b/schema/sdp-pr-gate/decision-record.schema.json new file mode 100644 index 00000000..9bf76e5c --- /dev/null +++ b/schema/sdp-pr-gate/decision-record.schema.json @@ -0,0 +1,174 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://sdp.dev/schema/sdp-pr-gate/decision-record/v1", + "title": "Decision Record v1", + "description": "Append-only decision record for a ChangePassport passport. Each entry is immutable once written.", + "type": "object", + "required": [ + "decision_id", + "passport_id", + "schema_version", + "decision", + "decided_by", + "decided_at", + "evidence_snapshot_ref", + "audit_ref", + "reason", + "signed_by" + ], + "additionalProperties": false, + "properties": { + "decision_id": { + "type": "string", + "format": "uuid", + "description": "Unique ID for this decision record entry." + }, + "passport_id": { + "type": "string", + "format": "uuid", + "description": "Reference to the passport this decision belongs to." + }, + "schema_version": { + "type": "string", + "const": "v1" + }, + "decision": { + "type": "string", + "enum": [ + "merge", + "hold", + "rework", + "escalate", + "override" + ], + "description": "The readiness decision." + }, + "decided_by": { + "$ref": "#/$defs/actor" + }, + "decided_at": { + "type": "string", + "format": "date-time" + }, + "reason": { + "type": "string", + "description": "Reason for the decision." + }, + "evidence_snapshot_ref": { + "type": "string", + "description": "Content-addressable reference to the evidence snapshot at decision time." + }, + "audit_ref": { + "type": "string", + "description": "Reference to the append-only audit log entry." + }, + "override_detail": { + "type": "object", + "description": "REQUIRED when decision is \"override\". Contains override trigger, original decision, and linkage.", + "required": [ + "trigger", + "original_decision", + "previous_decision_id" + ], + "properties": { + "trigger": { + "type": "string", + "enum": [ + "comment", + "api", + "manual" + ], + "description": "How the override was triggered." + }, + "original_decision": { + "type": "string", + "enum": [ + "hold", + "rework", + "escalate" + ], + "description": "What the system recommendation was before override." + }, + "previous_decision_id": { + "type": "string", + "format": "uuid", + "description": "Reference to the previous decision being overridden." + } + } + }, + "signed_by": { + "type": "object", + "description": "Cryptographic signature for the decision.", + "required": [ + "signer", + "algorithm", + "signature" + ], + "properties": { + "signer": { + "type": "string" + }, + "algorithm": { + "type": "string", + "enum": [ + "ed25519", + "rsa-pss-256" + ] + }, + "signature": { + "type": "string" + } + } + }, + "metadata": { + "type": "object", + "additionalProperties": true + } + }, + "$defs": { + "actor": { + "type": "object", + "required": [ + "id", + "type" + ], + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "human", + "system" + ] + }, + "name": { + "type": "string" + }, + "role": { + "type": "string" + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "decision": { + "const": "override" + } + }, + "required": [ + "decision" + ] + }, + "then": { + "required": [ + "override_detail" + ] + } + } + ] +} \ No newline at end of file diff --git a/schema/sdp-pr-gate/evidence-event.schema.json b/schema/sdp-pr-gate/evidence-event.schema.json new file mode 100644 index 00000000..b79fd2b9 --- /dev/null +++ b/schema/sdp-pr-gate/evidence-event.schema.json @@ -0,0 +1,327 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://sdp.dev/schema/sdp-pr-gate/evidence-event/v1", + "title": "ChangePassport Evidence Event", + "description": "Event emitted by an evidence provider for ingestion into sdp-pr-gate. Ingestion is idempotent. Dedup key: source + external_ref + commit_sha. 12 required fields match the manifesto core contract; artifact_uri, artifact_hash, and error_state are optional extensions.", + "type": "object", + "required": [ + "schema_version", + "source", + "external_ref", + "repository", + "pull_request", + "commit_sha", + "observed_at", + "collected_at", + "actor", + "event_type", + "status", + "summary" + ], + "additionalProperties": false, + "properties": { + "schema_version": { + "type": "string", + "const": "1", + "description": "Schema version of this evidence event. Must be '1' for v1." + }, + "source": { + "type": "string", + "minLength": 1, + "maxLength": 128, + "pattern": "^[a-z][a-z0-9-]*(\\.[a-z][a-z0-9-]*)*$", + "description": "Unique identifier of the evidence provider. Reverse-DNS format recommended (e.g. 'github-actions', 'snyk.sast', 'sonarqube'). Forms part of the dedup key with external_ref and commit_sha.", + "examples": [ + "github-actions", + "snyk.sast", + "sonarqube", + "gerrit.review", + "custom.my-scanner" + ] + }, + "external_ref": { + "type": "string", + "minLength": 1, + "maxLength": 512, + "description": "External reference ID for this evidence event. Must be unique within the source provider's namespace. Examples: CI run ID, scanner finding ID, review comment ID. Forms part of the dedup key with source and commit_sha.", + "examples": [ + "https://github.com/acme/app/actions/runs/12345", + "snyk-finding-abc123", + "gerrit-change-12345-patchset-2" + ] + }, + "repository": { + "$ref": "#/$defs/repository", + "description": "Repository identity for the change under evaluation." + }, + "pull_request": { + "$ref": "#/$defs/pull_request", + "description": "Pull request (or merge request) identity." + }, + "commit_sha": { + "type": "string", + "pattern": "^[0-9a-f]{40}$", + "description": "Full 40-character hex SHA of the commit this evidence was observed on. Forms part of the dedup key with source and external_ref." + }, + "observed_at": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 timestamp of when the evidence was originally observed by the provider (not when it was collected or submitted)." + }, + "collected_at": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 timestamp of when the evidence was collected/submitted to the API. Must be >= observed_at." + }, + "actor": { + "$ref": "#/$defs/actor", + "description": "The entity that produced or triggered this evidence." + }, + "event_type": { + "type": "string", + "enum": [ + "commit", + "ci_run", + "test_result", + "scan_finding", + "review_comment", + "approval", + "merge", + "deployment", + "custom" + ], + "description": "Category of the evidence event." + }, + "status": { + "type": "string", + "enum": [ + "success", + "failure", + "warning", + "skipped", + "degraded", + "pending" + ], + "description": "Outcome status of this evidence. 'degraded' indicates partial or low-confidence data." + }, + "summary": { + "type": "string", + "minLength": 1, + "maxLength": 2048, + "description": "Human-readable summary of the evidence event. Should be self-contained enough to display in a passport without additional context." + }, + "artifact_uri": { + "type": [ + "string", + "null" + ], + "format": "uri", + "maxLength": 2048, + "description": "URI to the artifact produced by this event. Omit or use null when no artifact exists." + }, + "artifact_hash": { + "$ref": "#/$defs/artifact_hash", + "description": "Cryptographic hash of the artifact for integrity verification. Null if artifact_uri is empty." + }, + "error_state": { + "$ref": "#/$defs/error_state", + "description": "Error details if the evidence collection encountered problems. Null if no error." + } + }, + "$defs": { + "repository": { + "type": "object", + "required": [ + "id", + "url" + ], + "additionalProperties": false, + "properties": { + "id": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Canonical repository identifier. Platform-specific format (e.g., 'acme/my-app' for GitHub, 'project/repo' for Gerrit).", + "examples": [ + "acme/my-app", + "my-gerrit-project/my-repo" + ] + }, + "url": { + "type": "string", + "format": "uri", + "description": "Canonical web URL of the repository.", + "examples": [ + "https://github.com/acme/my-app", + "https://gerrit.example.com/g/project/repo" + ] + }, + "provider": { + "type": "string", + "maxLength": 64, + "description": "Platform identifier for the repository host (e.g., 'github', 'gitlab', 'gerrit', 'bitbucket').", + "examples": [ + "github", + "gitlab", + "gerrit", + "bitbucket" + ] + }, + "branch": { + "type": "string", + "maxLength": 256, + "description": "Target branch for the pull request (base branch).", + "examples": [ + "main", + "master", + "release/1.0" + ] + } + } + }, + "pull_request": { + "type": "object", + "required": [ + "id" + ], + "additionalProperties": false, + "properties": { + "id": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Pull request identifier. Numeric ID or platform-specific format.", + "examples": [ + "42", + "!123", + "MR-456" + ] + }, + "url": { + "type": "string", + "format": "uri", + "description": "Canonical web URL of the pull request.", + "examples": [ + "https://github.com/acme/my-app/pull/42", + "https://gerrit.example.com/c/project/repo/+/123" + ] + }, + "title": { + "type": "string", + "maxLength": 512, + "description": "Title of the pull request at the time of evidence collection." + } + } + }, + "actor": { + "type": "object", + "required": [ + "type", + "id" + ], + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "human", + "agent", + "system", + "tool" + ], + "description": "Category of the actor." + }, + "id": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "Unique identifier for the actor within the source system.", + "examples": [ + "user@github.com", + "codex-agent-run-abc123", + "github-actions", + "snyk-cli" + ] + }, + "name": { + "type": "string", + "maxLength": 256, + "description": "Human-readable display name for the actor.", + "examples": [ + "Jane Doe", + "Codex Agent v2", + "GitHub Actions", + "Snyk CLI 1.1200.0" + ] + } + } + }, + "artifact_hash": { + "type": [ + "object", + "null" + ], + "description": "Cryptographic hash of the evidence artifact. Supports multiple algorithms; SHA-256 is required for v1.", + "required": [ + "algorithm", + "value" + ], + "additionalProperties": false, + "properties": { + "algorithm": { + "type": "string", + "enum": [ + "sha-256", + "sha-384", + "sha-512", + "blake2b-256", + "blake2b-512" + ], + "description": "Hash algorithm used. SHA-256 is the default and required algorithm for v1." + }, + "value": { + "type": "string", + "pattern": "^[a-f0-9]{64,128}$", + "description": "Hex-encoded hash digest. Length depends on algorithm: 64 chars for SHA-256/BLAKE2b-256, 96 for SHA-384, 128 for SHA-512/BLAKE2b-512." + } + } + }, + "error_state": { + "type": [ + "object", + "null" + ], + "description": "Error details if evidence collection encountered problems. Null means no error.", + "required": [ + "code", + "message" + ], + "additionalProperties": false, + "properties": { + "code": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[A-Z][A-Z0-9_]*(\\.[A-Z][A-Z0-9_]*)*$", + "description": "Machine-readable error code. Dot-separated hierarchical format recommended.", + "examples": [ + "PROVIDER_TIMEOUT", + "SCANNER.RATE_LIMITED", + "CI_RUN.INCOMPLETE" + ] + }, + "message": { + "type": "string", + "minLength": 1, + "maxLength": 2048, + "description": "Human-readable error message describing what went wrong." + }, + "retry_possible": { + "type": "boolean", + "default": false, + "description": "Whether retrying the evidence collection may succeed. Providers should set true for transient failures." + } + } + } + } +} \ No newline at end of file diff --git a/schema/sdp-pr-gate/examples/evidence-events/ci-run-success.json b/schema/sdp-pr-gate/examples/evidence-events/ci-run-success.json new file mode 100644 index 00000000..7dc6fed5 --- /dev/null +++ b/schema/sdp-pr-gate/examples/evidence-events/ci-run-success.json @@ -0,0 +1,33 @@ +{ + "schema_version": "1", + "source": "github-actions", + "external_ref": "https://github.com/acme/my-app/actions/runs/9876543210", + "repository": { + "id": "acme/my-app", + "url": "https://github.com/acme/my-app", + "provider": "github", + "branch": "main" + }, + "pull_request": { + "id": "42", + "url": "https://github.com/acme/my-app/pull/42", + "title": "feat: add user authentication middleware" + }, + "commit_sha": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0", + "observed_at": "2026-04-27T10:28:12Z", + "collected_at": "2026-04-27T10:28:15Z", + "actor": { + "type": "system", + "id": "github-actions", + "name": "GitHub Actions" + }, + "event_type": "ci_run", + "status": "success", + "summary": "CI pipeline passed: build (go1.23), test (342/342 passed, 0 skipped), lint (0 issues), coverage 78.3% (threshold 60%). Total duration: 4m 12s.", + "artifact_uri": "https://github.com/acme/my-app/actions/runs/9876543210", + "artifact_hash": { + "algorithm": "sha-256", + "value": "b5a2c96f507121b1f7f1c88e6c5b3d1a7e4f8c2d6b0a9e3c5f7d1b3a5c7e9f21" + }, + "error_state": null +} diff --git a/schema/sdp-pr-gate/examples/evidence-events/review-comment.json b/schema/sdp-pr-gate/examples/evidence-events/review-comment.json new file mode 100644 index 00000000..7f8357cc --- /dev/null +++ b/schema/sdp-pr-gate/examples/evidence-events/review-comment.json @@ -0,0 +1,33 @@ +{ + "schema_version": "1", + "source": "gerrit.review", + "external_ref": "gerrit-change-12345-patchset-3-comment-c7a9b1", + "repository": { + "id": "platform/auth-service", + "url": "https://gerrit.example.com/g/platform/auth-service", + "provider": "gerrit", + "branch": "main" + }, + "pull_request": { + "id": "12345", + "url": "https://gerrit.example.com/c/platform/auth-service/+/12345", + "title": "feat: implement token refresh rotation" + }, + "commit_sha": "f7e6d5c4b3a29180f7e6d5c4b3a29180f7e6d5c4", + "observed_at": "2026-04-27T11:05:22Z", + "collected_at": "2026-04-27T11:05:23Z", + "actor": { + "type": "human", + "id": "jane.doe@example.com", + "name": "Jane Doe" + }, + "event_type": "review_comment", + "status": "warning", + "summary": "Reviewer flagged concern: refresh token storage in auth/handler.go:89 uses plaintext cookie. Recommends encrypting token before setting cookie value. Not blocking but should be addressed before merge.", + "artifact_uri": "https://gerrit.example.com/c/platform/auth-service/+/12345/comment/c7a9b1", + "artifact_hash": { + "algorithm": "sha-256", + "value": "3a1b5c7d9e2f4a6b8c0d2e4f6a8b0c2d4e6f8a0b2c4d6e8f0a2b4c6d8e0f2a41" + }, + "error_state": null +} diff --git a/schema/sdp-pr-gate/examples/evidence-events/scanner-finding.json b/schema/sdp-pr-gate/examples/evidence-events/scanner-finding.json new file mode 100644 index 00000000..4f7329fa --- /dev/null +++ b/schema/sdp-pr-gate/examples/evidence-events/scanner-finding.json @@ -0,0 +1,33 @@ +{ + "schema_version": "1", + "source": "snyk.sast", + "external_ref": "snyk-sast-finding-e3b0c442-98fc-1c14-9afb-f4c8996fb924", + "repository": { + "id": "acme/my-app", + "url": "https://github.com/acme/my-app", + "provider": "github", + "branch": "main" + }, + "pull_request": { + "id": "42", + "url": "https://github.com/acme/my-app/pull/42", + "title": "feat: add user authentication middleware" + }, + "commit_sha": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0", + "observed_at": "2026-04-27T10:29:45Z", + "collected_at": "2026-04-27T10:29:47Z", + "actor": { + "type": "tool", + "id": "snyk-cli", + "name": "Snyk CLI 1.1200.0" + }, + "event_type": "scan_finding", + "status": "failure", + "summary": "SAST scan found 1 high-severity issue: SQL injection in auth/middleware.go:47 (function authenticateToken). Unsanitized user input passed directly to database query string. Severity: high. CVSS 7.5.", + "artifact_uri": "https://app.snyk.io/org/acme/project/my-app/issue/snyk-finding-e3b0c442", + "artifact_hash": { + "algorithm": "sha-256", + "value": "7d1a3b5c9e2f4a6d8b0c3e5f7a9d1b3c5e7f9a2d4b6c8e0f2a4d6b8c0e2f4a71" + }, + "error_state": null +} diff --git a/schema/sdp-pr-gate/examples/passports/passport-degraded-evidence.json b/schema/sdp-pr-gate/examples/passports/passport-degraded-evidence.json new file mode 100644 index 00000000..61a8b44f --- /dev/null +++ b/schema/sdp-pr-gate/examples/passports/passport-degraded-evidence.json @@ -0,0 +1,132 @@ +{ + "passport_id": "01912234-5678-7000-8000-000000000003", + "schema_version": "v1", + "created_at": "2026-05-01T16:00:00Z", + "generated_at": "2026-05-01T16:05:00Z", + "repository": { + "full_name": "acme/webapp", + "url": "https://github.com/acme/webapp", + "default_branch": "main", + "platform": "github" + }, + "pull_request": { + "number": 348, + "url": "https://github.com/acme/webapp/pull/348", + "title": "Update dependency versions", + "head_sha": "789abc123def4567890123456789012345678901", + "base_branch": "main", + "head_branch": "chore/deps-update", + "author": { + "id": "dependabot", + "type": "agent", + "name": "Dependabot" + } + }, + "intent": { + "summary": "Update 3 dependencies to latest patch versions.", + "claimed_type": "chore", + "source": "commit_message" + }, + "scope": { + "items": [ + { + "description": "go.sum and go.mod dependency updates", + "classification": "in_scope", + "files": [ + "go.mod", + "go.sum" + ] + } + ] + }, + "actors": { + "items": [ + { + "actor": { + "id": "dependabot", + "type": "agent", + "name": "Dependabot" + }, + "contribution_type": "agent", + "commits": [ + "789abc123def4567890123456789012345678901" + ] + } + ], + "decision_owner": { + "id": "bob", + "type": "human", + "name": "Bob Manager", + "role": "decision_owner" + } + }, + "evidence": { + "providers": [ + { + "source": "github-actions", + "status": "accepted", + "event_count": 3, + "last_collected_at": "2026-05-01T16:02:00Z" + }, + { + "source": "sonarqube", + "status": "degraded", + "event_count": 0, + "last_collected_at": "2026-05-01T16:03:00Z", + "error": { + "code": "TIMEOUT", + "message": "Scanner timed out after 300s" + } + }, + { + "source": "github-review", + "status": "unavailable", + "event_count": 0, + "error": { + "code": "NO_REVIEWERS", + "message": "No reviewers assigned yet" + } + } + ], + "total_events": 3, + "collection_status": "degraded", + "evidence_snapshot_ref": "sha256:c4b5a6f1e2d3c4b5a6f1e2d3c4b5a6f1e2d3c4b5a6f1e2d3c4b5a6f1e2d3c4b5" + }, + "findings": { + "items": [], + "unresolved_count": 0, + "summary": "No findings collected. Scanner was degraded (timeout)." + }, + "risk": { + "items": [ + { + "description": "SonarQube scanner timed out \u2014 no SAST coverage for this PR", + "category": "missing_evidence", + "severity": "medium" + }, + { + "description": "No reviewer assigned \u2014 peer review evidence unavailable", + "category": "missing_evidence", + "severity": "high" + } + ], + "summary": "2 missing evidence risks: SAST scan timed out, no peer review." + }, + "decision": { + "result": "hold", + "decided_by": { + "id": "sdp-pr-gate", + "type": "system", + "name": "ChangePassport", + "role": "system" + }, + "decided_at": "2026-05-01T16:05:00Z", + "reason": "Insufficient evidence: SAST degraded, no peer review. Awaiting reviewer assignment and scanner retry.", + "check_status": "hold" + }, + "integrity": { + "passport_hash": "cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe", + "algorithm": "sha256", + "evidence_snapshot_hash": "c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4" + } +} \ No newline at end of file diff --git a/schema/sdp-pr-gate/examples/passports/passport-happy-path.json b/schema/sdp-pr-gate/examples/passports/passport-happy-path.json new file mode 100644 index 00000000..2495589c --- /dev/null +++ b/schema/sdp-pr-gate/examples/passports/passport-happy-path.json @@ -0,0 +1,125 @@ +{ + "passport_id": "01912234-5678-7000-8000-000000000001", + "schema_version": "v1", + "created_at": "2026-05-01T10:00:00Z", + "generated_at": "2026-05-01T10:05:00Z", + "repository": { + "full_name": "acme/webapp", + "url": "https://github.com/acme/webapp", + "default_branch": "main", + "platform": "github" + }, + "pull_request": { + "number": 342, + "url": "https://github.com/acme/webapp/pull/342", + "title": "Add user profile caching layer", + "head_sha": "abc123def456abc123def456abc123def456abcd", + "base_branch": "main", + "head_branch": "feature/profile-cache", + "author": { "id": "alice", "type": "human", "name": "Alice Chen", "role": "author" }, + "labels": ["feature", "performance"], + "linked_issues": ["https://github.com/acme/webapp/issues/330"] + }, + "intent": { + "summary": "Add Redis-based caching layer for user profile lookups to reduce DB load by ~40%.", + "linked_issue": ["https://github.com/acme/webapp/issues/330"], + "claimed_type": "feature", + "source": "pr_template" + }, + "scope": { + "items": [ + { + "description": "Redis cache client and configuration", + "classification": "in_scope", + "files": ["internal/cache/redis.go", "internal/cache/redis_test.go", "config/cache.yaml"], + "confirmed_by": { "id": "alice", "type": "human", "name": "Alice Chen" }, + "confirmed_at": "2026-05-01T09:00:00Z" + }, + { + "description": "User profile service integration", + "classification": "in_scope", + "files": ["internal/user/service.go"], + "confirmed_by": { "id": "alice", "type": "human", "name": "Alice Chen" }, + "confirmed_at": "2026-05-01T09:00:00Z" + }, + { + "description": "Monitoring dashboard update", + "classification": "out_of_scope", + "confirmed_by": { "id": "alice", "type": "human", "name": "Alice Chen" }, + "confirmed_at": "2026-05-01T09:00:00Z" + } + ] + }, + "actors": { + "items": [ + { + "actor": { "id": "alice", "type": "human", "name": "Alice Chen" }, + "contribution_type": "author", + "commits": ["abc123def456abc123def456abc123def456abcd"] + }, + { + "actor": { "id": "claude-code", "type": "agent", "name": "Claude Code" }, + "contribution_type": "agent", + "commits": ["abc123def456abc123def456abc123def456abcd"] + } + ], + "decision_owner": { "id": "bob", "type": "human", "name": "Bob Manager", "role": "decision_owner" } + }, + "evidence": { + "providers": [ + { "source": "github-actions", "status": "accepted", "event_count": 3, "last_collected_at": "2026-05-01T10:02:00Z" }, + { "source": "sonarqube", "status": "accepted", "event_count": 1, "last_collected_at": "2026-05-01T10:03:00Z" }, + { "source": "github-review", "status": "accepted", "event_count": 2, "last_collected_at": "2026-05-01T10:04:00Z" } + ], + "total_events": 6, + "collection_status": "complete", + "evidence_snapshot_ref": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "commits": [ + { + "sha": "abc123def456abc123def456abc123def456abcd", + "author": "Alice Chen", + "message": "feat: add Redis caching for user profiles", + "timestamp": "2026-05-01T08:30:00Z", + "is_ai_assisted": true + } + ] + }, + "findings": { + "items": [ + { + "id": "SQ-001", + "source": "sonarqube", + "severity": "low", + "status": "resolved", + "summary": "Minor: unused import in redis.go", + "file": "internal/cache/redis.go", + "line": 12, + "external_ref": "https://sonarqube.acme.dev/issues/SQ-001", + "resolution": { + "type": "fixed", + "by": { "id": "alice", "type": "human", "name": "Alice Chen" }, + "at": "2026-05-01T09:45:00Z", + "reason": "Removed unused import in follow-up commit" + } + } + ], + "unresolved_count": 0, + "summary": "1 finding (low), resolved. No blockers." + }, + "risk": { + "items": [], + "summary": "No significant risks identified." + }, + "decision": { + "result": "merge", + "decided_by": { "id": "bob", "type": "human", "name": "Bob Manager", "role": "decision_owner" }, + "decided_at": "2026-05-01T10:05:00Z", + "reason": "All checks pass. Scope confirmed. No unresolved findings.", + "check_status": "ready" + }, + "integrity": { + "passport_hash": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2", + "algorithm": "sha256", + "evidence_snapshot_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } +} diff --git a/schema/sdp-pr-gate/examples/passports/passport-override.json b/schema/sdp-pr-gate/examples/passports/passport-override.json new file mode 100644 index 00000000..332b6689 --- /dev/null +++ b/schema/sdp-pr-gate/examples/passports/passport-override.json @@ -0,0 +1,101 @@ +{ + "passport_id": "01912234-5678-7000-8000-000000000002", + "schema_version": "v1", + "created_at": "2026-05-01T14:00:00Z", + "generated_at": "2026-05-01T14:05:00Z", + "repository": { + "full_name": "acme/webapp", + "url": "https://github.com/acme/webapp", + "default_branch": "main", + "platform": "github" + }, + "pull_request": { + "number": 345, + "url": "https://github.com/acme/webapp/pull/345", + "title": "Hotfix: fix auth token expiry check", + "head_sha": "def789abc012def789abc012def789abc012def7", + "base_branch": "main", + "head_branch": "hotfix/auth-expiry", + "author": { "id": "carol", "type": "human", "name": "Carol Dev", "role": "author" }, + "labels": ["bugfix", "hotfix"] + }, + "intent": { + "summary": "Fix auth token expiry check causing 401 errors for active users.", + "claimed_type": "bugfix", + "source": "labels" + }, + "scope": { + "items": [ + { + "description": "Auth token validation logic", + "classification": "in_scope", + "files": ["internal/auth/token.go", "internal/auth/token_test.go"] + } + ] + }, + "actors": { + "items": [ + { + "actor": { "id": "carol", "type": "human", "name": "Carol Dev" }, + "contribution_type": "author", + "commits": ["def789abc012def789abc012def789abc012def7"] + } + ], + "decision_owner": { "id": "bob", "type": "human", "name": "Bob Manager", "role": "decision_owner" } + }, + "evidence": { + "providers": [ + { "source": "github-actions", "status": "accepted", "event_count": 3, "last_collected_at": "2026-05-01T14:02:00Z" }, + { "source": "sonarqube", "status": "accepted", "event_count": 1, "last_collected_at": "2026-05-01T14:03:00Z" }, + { "source": "github-review", "status": "accepted", "event_count": 1, "last_collected_at": "2026-05-01T14:04:00Z" } + ], + "total_events": 5, + "collection_status": "complete", + "evidence_snapshot_ref": "sha256:f1e2d3c4b5a6f1e2d3c4b5a6f1e2d3c4b5a6f1e2d3c4b5a6f1e2d3c4b5a6f1e2" + }, + "findings": { + "items": [ + { + "id": "SQ-042", + "source": "sonarqube", + "severity": "medium", + "status": "open", + "summary": "Potential timing attack on token comparison", + "file": "internal/auth/token.go", + "line": 45, + "external_ref": "https://sonarqube.acme.dev/issues/SQ-042" + } + ], + "unresolved_count": 1, + "summary": "1 medium finding (SQ-042) unresolved: potential timing attack. Non-blocking." + }, + "risk": { + "items": [ + { + "description": "Timing attack finding (SQ-042) deferred to follow-up PR", + "category": "unresolved_finding", + "severity": "medium", + "mitigation": "Tracked in issue #350 for next sprint" + } + ], + "summary": "1 medium risk accepted: timing attack deferred to follow-up." + }, + "decision": { + "result": "override", + "decided_by": { "id": "bob", "type": "human", "name": "Bob Manager", "role": "decision_owner" }, + "decided_at": "2026-05-01T14:10:00Z", + "reason": "Client deadline accepted; non-blocking scanner finding tracked in #350", + "override": { + "reason": "Client deadline accepted; non-blocking scanner finding tracked in #350", + "evidence_snapshot_ref": "sha256:f1e2d3c4b5a6f1e2d3c4b5a6f1e2d3c4b5a6f1e2d3c4b5a6f1e2d3c4b5a6f1e2", + "audit_ref": "audit:override:01912234-5678-7000-8000-000000000002", + "trigger": "comment" + }, + "check_status": "ready" + }, + "integrity": { + "passport_hash": "b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3", + "algorithm": "sha256", + "evidence_snapshot_hash": "f1e2d3c4b5a6f1e2d3c4b5a6f1e2d3c4b5a6f1e2d3c4b5a6f1e2d3c4b5a6f1e2" + } +} diff --git a/schema/sdp-pr-gate/passport.schema.json b/schema/sdp-pr-gate/passport.schema.json new file mode 100644 index 00000000..7e89027c --- /dev/null +++ b/schema/sdp-pr-gate/passport.schema.json @@ -0,0 +1,751 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://sdp.dev/schema/sdp-pr-gate/passport/v1", + "title": "ChangePassport v1", + "description": "Evidence-backed merge-readiness record for one pull request. Internal namespace: sdp-pr-gate. Display name: ChangePassport.", + "type": "object", + "required": [ + "passport_id", + "schema_version", + "created_at", + "generated_at", + "repository", + "pull_request", + "intent", + "scope", + "actors", + "evidence", + "findings", + "risk", + "decision", + "integrity" + ], + "additionalProperties": false, + "properties": { + "passport_id": { + "type": "string", + "format": "uuid" + }, + "schema_version": { + "type": "string", + "const": "v1" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "generated_at": { + "type": "string", + "format": "date-time" + }, + "repository": { + "$ref": "#/$defs/repository_ref" + }, + "pull_request": { + "$ref": "#/$defs/pull_request_ref" + }, + "annotations": { + "type": "array", + "items": { + "$ref": "#/$defs/annotation" + }, + "description": "Human-authored annotations. CANNOT overwrite observed facts." + }, + "intent": { + "$ref": "#/$defs/intent" + }, + "scope": { + "$ref": "#/$defs/scope" + }, + "actors": { + "$ref": "#/$defs/actors" + }, + "evidence": { + "$ref": "#/$defs/evidence_summary" + }, + "findings": { + "$ref": "#/$defs/findings" + }, + "risk": { + "$ref": "#/$defs/risk" + }, + "decision": { + "$ref": "#/$defs/decision" + }, + "integrity": { + "$ref": "#/$defs/integrity" + } + }, + "$defs": { + "repository_ref": { + "type": "object", + "required": [ + "full_name", + "url" + ], + "properties": { + "full_name": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + }, + "default_branch": { + "type": "string" + }, + "platform": { + "type": "string", + "enum": [ + "github", + "gitlab", + "bitbucket", + "other" + ] + } + } + }, + "pull_request_ref": { + "type": "object", + "required": [ + "number", + "url", + "head_sha", + "base_branch", + "head_branch" + ], + "properties": { + "number": { + "type": "integer" + }, + "url": { + "type": "string", + "format": "uri" + }, + "title": { + "type": "string" + }, + "head_sha": { + "type": "string", + "pattern": "^[0-9a-f]{40}$" + }, + "base_branch": { + "type": "string" + }, + "head_branch": { + "type": "string" + }, + "author": { + "$ref": "#/$defs/actor_ref" + }, + "labels": { + "type": "array", + "items": { + "type": "string" + } + }, + "linked_issues": { + "type": "array", + "items": { + "type": "string", + "format": "uri" + } + } + } + }, + "actor_ref": { + "type": "object", + "required": [ + "id", + "type" + ], + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "human", + "agent", + "system", + "tool" + ] + }, + "name": { + "type": "string" + }, + "role": { + "type": "string" + } + } + }, + "annotation": { + "type": "object", + "required": [ + "author", + "content", + "created_at" + ], + "properties": { + "author": { + "$ref": "#/$defs/actor_ref" + }, + "content": { + "type": "string" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "section": { + "type": "string" + } + } + }, + "intent": { + "type": "object", + "required": [ + "summary" + ], + "properties": { + "summary": { + "type": "string" + }, + "linked_issue": { + "type": "array", + "items": { + "type": "string", + "format": "uri" + } + }, + "claimed_type": { + "type": "string", + "enum": [ + "feature", + "bugfix", + "refactor", + "security", + "performance", + "docs", + "test", + "chore", + "other" + ] + }, + "source": { + "type": "string", + "enum": [ + "pr_template", + "commit_message", + "linked_issue", + "labels", + "manual" + ] + } + } + }, + "scope": { + "type": "object", + "required": [ + "items" + ], + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "required": [ + "description", + "classification" + ], + "properties": { + "description": { + "type": "string" + }, + "classification": { + "type": "string", + "enum": [ + "in_scope", + "out_of_scope", + "unknown" + ] + }, + "files": { + "type": "array", + "items": { + "type": "string" + } + }, + "confirmed_by": { + "$ref": "#/$defs/actor_ref" + }, + "confirmed_at": { + "type": "string", + "format": "date-time" + } + } + } + }, + "scope_delta": { + "type": "array", + "items": { + "type": "object", + "required": [ + "description", + "direction" + ], + "properties": { + "description": { + "type": "string" + }, + "direction": { + "type": "string", + "enum": [ + "added", + "removed", + "changed" + ] + }, + "detected_at": { + "type": "string", + "format": "date-time" + } + } + } + } + } + }, + "actors": { + "type": "object", + "required": [ + "items" + ], + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "required": [ + "actor", + "contribution_type" + ], + "properties": { + "actor": { + "$ref": "#/$defs/actor_ref" + }, + "contribution_type": { + "type": "string", + "enum": [ + "author", + "committer", + "reviewer", + "agent", + "ci_system", + "scanner", + "tool" + ] + }, + "commits": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[0-9a-f]{40}$" + } + }, + "evidence_refs": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "decision_owner": { + "$ref": "#/$defs/actor_ref" + } + } + }, + "evidence_summary": { + "type": "object", + "required": [ + "providers", + "total_events", + "collection_status", + "evidence_snapshot_ref" + ], + "properties": { + "providers": { + "type": "array", + "items": { + "type": "object", + "required": [ + "source", + "status", + "event_count" + ], + "properties": { + "source": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "accepted", + "degraded", + "rejected", + "pending", + "unavailable" + ] + }, + "event_count": { + "type": "integer" + }, + "last_collected_at": { + "type": "string", + "format": "date-time" + }, + "error": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "message": { + "type": "string" + } + } + } + } + } + }, + "total_events": { + "type": "integer" + }, + "collection_status": { + "type": "string", + "enum": [ + "complete", + "partial", + "degraded", + "pending" + ] + }, + "evidence_snapshot_ref": { + "type": "string" + }, + "commits": { + "type": "array", + "items": { + "type": "object", + "required": [ + "sha", + "author", + "message", + "timestamp" + ], + "properties": { + "sha": { + "type": "string", + "pattern": "^[0-9a-f]{40}$" + }, + "author": { + "type": "string" + }, + "message": { + "type": "string" + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "is_ai_assisted": { + "type": "boolean" + } + } + } + } + } + }, + "findings": { + "type": "object", + "required": [ + "items" + ], + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "required": [ + "id", + "source", + "severity", + "status", + "summary" + ], + "properties": { + "id": { + "type": "string" + }, + "source": { + "type": "string" + }, + "severity": { + "type": "string", + "enum": [ + "critical", + "high", + "medium", + "low", + "info" + ] + }, + "status": { + "type": "string", + "enum": [ + "open", + "resolved", + "accepted_risk", + "deferred", + "false_positive" + ] + }, + "summary": { + "type": "string" + }, + "file": { + "type": "string" + }, + "line": { + "type": "integer" + }, + "external_ref": { + "type": "string", + "description": "External reference ID (provider-specific). May be a URI or a provider-native ID." + }, + "resolution": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "fixed", + "accepted_risk", + "deferred", + "false_positive" + ] + }, + "by": { + "$ref": "#/$defs/actor_ref" + }, + "at": { + "type": "string", + "format": "date-time" + }, + "reason": { + "type": "string" + } + } + } + } + } + }, + "unresolved_count": { + "type": "integer" + }, + "summary": { + "type": "string" + } + } + }, + "risk": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "required": [ + "description", + "category" + ], + "properties": { + "description": { + "type": "string" + }, + "category": { + "type": "string", + "enum": [ + "scope_violation", + "missing_evidence", + "unresolved_finding", + "actor_unknown", + "test_gap", + "dependency_risk", + "other" + ] + }, + "severity": { + "type": "string", + "enum": [ + "high", + "medium", + "low" + ] + }, + "mitigation": { + "type": "string" + }, + "accepted_by": { + "$ref": "#/$defs/actor_ref" + }, + "accepted_at": { + "type": "string", + "format": "date-time" + } + } + } + }, + "summary": { + "type": "string" + } + } + }, + "decision": { + "type": "object", + "required": [ + "result", + "decided_by", + "decided_at" + ], + "properties": { + "result": { + "type": "string", + "enum": [ + "merge", + "hold", + "rework", + "escalate", + "override" + ] + }, + "decided_by": { + "$ref": "#/$defs/actor_ref" + }, + "decided_at": { + "type": "string", + "format": "date-time" + }, + "reason": { + "type": "string" + }, + "override": { + "type": "object", + "required": [ + "reason", + "evidence_snapshot_ref", + "audit_ref", + "trigger", + "original_decision", + "previous_decision_id" + ], + "properties": { + "reason": { + "type": "string" + }, + "evidence_snapshot_ref": { + "type": "string" + }, + "audit_ref": { + "type": "string" + }, + "trigger": { + "type": "string", + "enum": [ + "comment", + "api", + "manual" + ] + }, + "original_decision": { + "type": "string", + "enum": [ + "hold", + "rework", + "escalate" + ], + "description": "What the system recommendation was before override." + }, + "previous_decision_id": { + "type": "string", + "format": "uuid", + "description": "Reference to the previous decision being overridden." + } + } + }, + "check_status": { + "type": "string", + "enum": [ + "ready", + "hold", + "rework", + "escalate" + ] + } + }, + "allOf": [ + { + "if": { + "properties": { + "result": { + "const": "override" + } + }, + "required": [ + "result" + ] + }, + "then": { + "required": [ + "override" + ] + } + } + ] + }, + "integrity": { + "type": "object", + "required": [ + "passport_hash", + "algorithm", + "evidence_snapshot_hash" + ], + "properties": { + "passport_hash": { + "type": "string" + }, + "algorithm": { + "type": "string", + "enum": [ + "sha256", + "sha512" + ] + }, + "evidence_snapshot_hash": { + "type": "string" + }, + "previous_passport_hash": { + "type": "string" + }, + "drift_detected": { + "type": "boolean" + }, + "drift_reason": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/schema/telemetry/sdp-trace-events.schema.json b/schema/telemetry/sdp-trace-events.schema.json new file mode 100644 index 00000000..34f3ceef --- /dev/null +++ b/schema/telemetry/sdp-trace-events.schema.json @@ -0,0 +1,396 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://sdp.dev/schema/telemetry/sdp-trace-events.schema.json", + "title": "SDP Trace Events", + "description": "Allowlist schema for SDP telemetry trace events. All attributes must be explicitly declared - unknown attributes are rejected at emit-time.", + "type": "object", + "definitions": { + "span_kind": { + "type": "string", + "enum": ["execute_tool", "invoke_agent", "delivery_loop_phase", "sdp_bead_event"] + }, + "attribute_definition": { + "type": "object", + "required": ["type", "description"], + "properties": { + "type": { + "type": "string", + "enum": ["string", "int", "float", "boolean", "string[]"] + }, + "description": {"type": "string"}, + "required": {"type": "boolean", "default": false}, + "consent_level": { + "type": "string", + "enum": ["none", "metadata", "findings", "content"], + "description": "Minimum consent level required to emit this attribute. 'none' disables all telemetry." + } + } + } + }, + "properties": { + "span_kinds": { + "type": "object", + "description": "Allowed attributes per span kind", + "properties": { + "execute_tool": { + "type": "object", + "description": "Span emitted for each tool invocation (Bash, Read, Edit, etc.)", + "properties": { + "allowed_attributes": { + "type": "object", + "properties": { + "gen_ai.operation.name": { + "type": "string", + "const": "execute_tool", + "description": "OTel GenAI semantic convention - operation name" + }, + "gen_ai.tool.name": { + "type": "string", + "description": "Tool name (Bash, Read, Edit, Grep, etc.)" + }, + "gen_ai.tool.call.id": { + "type": "string", + "description": "Unique identifier for this tool call instance" + }, + "gen_ai.tool.type": { + "type": "string", + "enum": ["host", "mcp"], + "description": "Tool type: host (native) or mcp (Model Context Protocol)" + }, + "sdp.session.id": { + "type": "string", + "description": "SDP session identifier" + }, + "sdp.epic.bead_id": { + "type": "string", + "description": "Parent epic bead ID (resolved from .sdp/state/current-feature)" + }, + "sdp.harness": { + "type": "string", + "enum": ["claude-code", "codex", "opencode", "cursor"], + "description": "Harness type" + }, + "sdp.phase.name": { + "type": "string", + "enum": ["phase_0_bootstrap", "phase_1_build", "phase_2_pr", "phase_3_codex", "phase_4_closeout"], + "description": "Delivery loop phase name" + }, + "sdp.phase.cycle_number": { + "type": "int", + "description": "Cycle number within phase" + }, + "sdp.tool.exit_code": { + "type": "int", + "description": "Tool exit code (0 for success, non-zero for failure)" + }, + "sdp.tool.duration_ms": { + "type": "int", + "description": "Tool execution duration in milliseconds" + }, + "sdp.tool.error": { + "type": "string", + "consent_level": "findings", + "description": "Error message if tool failed" + }, + "sdp.tool.input_hash": { + "type": "string", + "pattern": "^[a-f0-9]{8}$", + "consent_level": "metadata", + "description": "SHA-1 hash (first 8 chars) of tool input" + }, + "sdp.tool.output_hash": { + "type": "string", + "pattern": "^[a-f0-9]{8}$", + "consent_level": "metadata", + "description": "SHA-1 hash (first 8 chars) of tool output" + } + }, + "required": ["gen_ai.operation.name", "gen_ai.tool.name", "gen_ai.tool.call.id", "sdp.session.id", "sdp.epic.bead_id", "sdp.harness"], + "additionalProperties": false + } + } + }, + "invoke_agent": { + "type": "object", + "description": "Span emitted for skill/subagent invocation", + "properties": { + "allowed_attributes": { + "type": "object", + "properties": { + "gen_ai.operation.name": { + "type": "string", + "const": "invoke_agent", + "description": "OTel GenAI semantic convention - operation name" + }, + "gen_ai.agent.name": { + "type": "string", + "description": "Agent/skill name (review, build, codex, etc.)" + }, + "gen_ai.conversation.id": { + "type": "string", + "description": "Conversation identifier for correlation" + }, + "sdp.session.id": { + "type": "string", + "description": "SDP session identifier" + }, + "sdp.epic.bead_id": { + "type": "string", + "description": "Parent epic bead ID" + }, + "sdp.skill.name": { + "type": "string", + "description": "Skill name" + }, + "sdp.phase.name": { + "type": "string", + "enum": ["phase_0_bootstrap", "phase_1_build", "phase_2_pr", "phase_3_codex", "phase_4_closeout"], + "description": "Delivery loop phase name" + }, + "sdp.phase.cycle_number": { + "type": "int", + "description": "Cycle number within phase" + }, + "sdp.agent.duration_ms": { + "type": "int", + "description": "Agent execution duration in milliseconds" + }, + "sdp.review.verdict": { + "type": "string", + "enum": ["APPROVED", "APPROVED_WITH_SUGGESTIONS", "NEEDS_WORK", "BLOCKED"], + "consent_level": "findings", + "description": "Review verdict" + }, + "sdp.review.findings_count": { + "type": "int", + "consent_level": "findings", + "description": "Total number of findings" + }, + "sdp.review.findings_by_severity": { + "type": "string", + "consent_level": "findings", + "description": "JSON string mapping severity to count" + }, + "sdp.codex.consecutive_clean": { + "type": "int", + "description": "Number of consecutive clean codex cycles" + }, + "sdp.codex.tests_passed": { + "type": "boolean", + "description": "Whether all tests passed" + }, + "sdp.council.round": { + "type": "int", + "description": "Council round number" + }, + "sdp.council.roles": { + "type": "array", + "items": {"type": "string"}, + "description": "Council roles participating in this round" + } + }, + "required": ["gen_ai.operation.name", "gen_ai.agent.name", "sdp.session.id", "sdp.epic.bead_id"], + "additionalProperties": false + } + } + }, + "delivery_loop_phase": { + "type": "object", + "description": "Span emitted for delivery loop phase transitions", + "properties": { + "allowed_attributes": { + "type": "object", + "properties": { + "sdp.phase.name": { + "type": "string", + "enum": ["phase_0_bootstrap", "phase_1_build", "phase_2_pr", "phase_3_codex", "phase_4_closeout"], + "description": "Phase name" + }, + "sdp.phase.cycle_number": { + "type": "int", + "description": "Cycle number within phase" + }, + "sdp.session.id": { + "type": "string", + "description": "SDP session identifier" + }, + "sdp.epic.bead_id": { + "type": "string", + "description": "Parent epic bead ID" + }, + "sdp.feature_id": { + "type": "string", + "description": "Feature identifier" + }, + "sdp.workstream_count": { + "type": "int", + "description": "Number of workstreams in this feature" + }, + "sdp.phase.duration_ms": { + "type": "int", + "description": "Phase duration in milliseconds" + }, + "sdp.pr.number": { + "type": "int", + "description": "Pull request number" + }, + "sdp.quality_gate.result": { + "type": "string", + "enum": ["PASSED", "FAILED", "SKIPPED"], + "description": "Quality gate result" + }, + "sdp.closeout.children_closed": { + "type": "int", + "description": "Number of child beads closed" + }, + "sdp.closeout.duration_ms": { + "type": "int", + "description": "Closeout duration in milliseconds" + } + }, + "required": ["sdp.phase.name", "sdp.session.id", "sdp.epic.bead_id"], + "additionalProperties": false + } + } + }, + "sdp_bead_event": { + "type": "object", + "description": "Span emitted for bead state transitions", + "properties": { + "allowed_attributes": { + "type": "object", + "properties": { + "sdp.bead.id": { + "type": "string", + "description": "Bead identifier (e.g., sdplab-xxxx)" + }, + "sdp.bead.event": { + "type": "string", + "enum": ["claimed", "started", "completed", "blocked", "unblocked", "closed", "reopened"], + "description": "Bead event type" + }, + "sdp.bead.previous_status": { + "type": "string", + "enum": ["OPEN", "IN_PROGRESS", "BLOCKED", "CLOSED"], + "description": "Previous bead status" + }, + "sdp.bead.new_status": { + "type": "string", + "enum": ["OPEN", "IN_PROGRESS", "BLOCKED", "CLOSED"], + "description": "New bead status" + }, + "sdp.session.id": { + "type": "string", + "description": "SDP session identifier" + }, + "sdp.epic.bead_id": { + "type": "string", + "description": "Parent epic bead ID" + }, + "trace.id": { + "type": "string", + "description": "Associated trace ID" + } + }, + "required": ["sdp.bead.id", "sdp.bead.event"], + "additionalProperties": false + } + } + } + } + }, + "consent_levels": { + "type": "object", + "description": "Consent level definitions. 'none' disables all telemetry including local storage.", + "properties": { + "none": { + "type": "object", + "description": "Telemetry completely disabled - no local storage, no export", + "properties": { + "description": {"type": "string"}, + "example_attributes": { + "type": "array", + "items": {"type": "string"} + } + } + }, + "metadata": { + "type": "object", + "description": "Default level - only structural metadata, no content", + "properties": { + "description": {"type": "string"}, + "example_attributes": { + "type": "array", + "items": {"type": "string"} + } + } + }, + "findings": { + "type": "object", + "description": "Includes finding messages without code content", + "properties": { + "description": {"type": "string"}, + "example_attributes": { + "type": "array", + "items": {"type": "string"} + } + } + }, + "content": { + "type": "object", + "description": "Full content including code snippets and prompts (opt-in)", + "properties": { + "description": {"type": "string"}, + "example_attributes": { + "type": "array", + "items": {"type": "string"} + } + } + } + } + }, + "sampling_policy": { + "type": "object", + "description": "Sampling policy configuration", + "properties": { + "head_based": { + "type": "object", + "properties": { + "default_rate": {"type": "number", "description": "Default sampling rate (0.0 to 1.0)"} + } + }, + "tail_based": { + "type": "object", + "properties": { + "drop_rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "tool_name": {"type": "string"}, + "max_duration_ms": {"type": "int"}, + "require_error": {"type": "boolean"} + } + } + } + } + }, + "hash_based": { + "type": "object", + "properties": { + "threshold_spans": { + "type": "int", + "description": "Span count threshold to trigger hash-based sampling" + }, + "sample_rate": { + "type": "number", + "description": "1/N sampling rate when threshold exceeded" + } + } + } + } + } + }, + "required": ["span_kinds", "consent_levels", "sampling_policy"], + "additionalProperties": false +} diff --git a/schema/traceability.schema.json b/schema/traceability.schema.json index 51129d62..64e553f2 100644 --- a/schema/traceability.schema.json +++ b/schema/traceability.schema.json @@ -1 +1 @@ -{"$schema":"http://json-schema.org/draft-07/schema#","title":"TraceabilityReport","type":"object","required":["ws_id","mappings"],"properties":{"ws_id":{"type":"string","pattern":"^\\d{2}-\\d{3}-\\d{2}$"},"total_acs":{"type":"integer","minimum":0},"mapped_acs":{"type":"integer","minimum":0},"missing_acs":{"type":"integer","minimum":0},"coverage_pct":{"type":"number","minimum":0,"maximum":100},"mappings":{"type":"array","items":{"$ref":"#/definitions/ACTestMapping"}}},"definitions":{"ACTestMapping":{"type":"object","required":["ac_id","ac_description","status"],"properties":{"ac_id":{"type":"string","pattern":"^AC\\d+$"},"ac_description":{"type":"string"},"test_file":{"type":["string","null"]},"test_name":{"type":["string","null"]},"status":{"type":"string","enum":["mapped","missing","failed"]},"confidence":{"type":"number","minimum":0,"maximum":1}}}}} +{"$schema":"http://json-schema.org/draft-07/schema#","$id":"https://sdp.dev/schema/traceability/v1","title":"TraceabilityReport","type":"object","required":["ws_id","mappings"],"properties":{"ws_id":{"type":"string","pattern":"^\\d{2}-\\d{3}-\\d{2}$"},"total_acs":{"type":"integer","minimum":0},"mapped_acs":{"type":"integer","minimum":0},"missing_acs":{"type":"integer","minimum":0},"coverage_pct":{"type":"number","minimum":0,"maximum":100},"mappings":{"type":"array","items":{"$ref":"#/definitions/ACTestMapping"}}},"definitions":{"ACTestMapping":{"type":"object","required":["ac_id","ac_description","status"],"properties":{"ac_id":{"type":"string","pattern":"^AC\\d+$"},"ac_description":{"type":"string"},"test_file":{"type":["string","null"]},"test_name":{"type":["string","null"]},"status":{"type":"string","enum":["mapped","missing","failed"]},"confidence":{"type":"number","minimum":0,"maximum":1}}}}} diff --git a/schema/ux-metrics.schema.json b/schema/ux-metrics.schema.json index 239e3723..a09dd6dc 100644 --- a/schema/ux-metrics.schema.json +++ b/schema/ux-metrics.schema.json @@ -1,5 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://sdp.dev/schema/ux-metrics/v1", "title": "SDP UX Metrics", "description": "User experience metrics for SDP CLI adoption journey (local-first, no cloud dependency)", "type": "object", @@ -63,6 +64,72 @@ } }, "definitions": { + "ai_sdlc_metrics": { + "description": "AI SDLC metrics M1-M7 for measuring delivery quality and efficiency", + "type": "object", + "properties": { + "M1_review_pass_rate": { + "description": "Review pass rate - % of @review calls that return APPROVED on first try", + "type": "number", + "minimum": 0, + "maximum": 100 + }, + "M2_build_rework_rate": { + "description": "Build rework rate - avg cycles in Phase 1 before APPROVED", + "type": "number", + "minimum": 0 + }, + "M3_codex_stability_index": { + "description": "Codex stability index - avg consecutive clean codex cycles needed before exit (target >= 2)", + "type": "number", + "minimum": 0 + }, + "M4_p3_deferral_rate": { + "description": "P3 deferral rate - % features that spin out P3 at cycle-5 cap", + "type": "number", + "minimum": 0, + "maximum": 100 + }, + "M5_delivery_velocity": { + "description": "Delivery velocity - time-to-merge decomposition by phase", + "type": "object", + "properties": { + "phase_0_bootstrap_ms": {"type": "number", "minimum": 0}, + "phase_1_build_ms": {"type": "number", "minimum": 0}, + "phase_2_pr_ms": {"type": "number", "minimum": 0}, + "phase_3_codex_ms": {"type": "number", "minimum": 0}, + "phase_4_closeout_ms": {"type": "number", "minimum": 0}, + "total_ms": {"type": "number", "minimum": 0} + } + }, + "M6_cost_per_merged_feature": { + "description": "Cost per merged feature - sum of tokens * model price", + "type": "object", + "properties": { + "total_cost_usd": {"type": "number", "minimum": 0}, + "input_tokens": {"type": "number", "minimum": 0}, + "output_tokens": {"type": "number", "minimum": 0}, + "cost_by_model": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "input_tokens": {"type": "number"}, + "output_tokens": {"type": "number"}, + "cost_usd": {"type": "number"} + } + } + } + } + }, + "M7_post_merge_rework_rate": { + "description": "Post-merge rework rate (DORA 5th) - % merged PRs whose files are touched again within 14 days", + "type": "number", + "minimum": 0, + "maximum": 100 + } + } + }, "time_to_first_value": { "description": "Time from 'sdp init' to first successful feature delivery (milliseconds)", "type": "object", diff --git a/schema/ws-verdict.schema.json b/schema/ws-verdict.schema.json index 010aadec..98bf4e82 100644 --- a/schema/ws-verdict.schema.json +++ b/schema/ws-verdict.schema.json @@ -92,7 +92,13 @@ }, "evidence": { "type": "string", - "description": "File:line reference or test name proving this AC" + "description": "File:line reference or test name proving this AC. Format: 'path/to/file.go:123' or 'TestFunctionName'. Must include specific line numbers for code references.", + "examples": [ + "internal/bridge/client.go:45", + "cmd/sdp-build/main.go:123-125", + "TestBuildExecuteSuccess", + "scripts/build.sh:45" + ] } } }, diff --git a/scripts/install.sh b/scripts/install.sh index a864c267..aa4b26b0 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -1,147 +1,117 @@ -#!/bin/sh -# SDP Install Script (WS-067-06: AC7) -# Usage: curl -sSL https://raw.githubusercontent.com/fall-out-bug/sdp/main/scripts/install.sh | sh -# Or: ./install.sh [version] -# Optional env for testing/custom mirrors: -# SDP_REPO=owner/repo -# SDP_RELEASE_BASE_URL=https://host/path/to/release/ - -set -eu - -VERSION="${1:-latest}" -REPO="${SDP_REPO:-fall-out-bug/sdp}" -BINARY_NAME="sdp" -INSTALL_DIR="${HOME}/.local/bin" +#!/usr/bin/env bash +# F141-03: one-shot SDP installer for downstream repos. +# Usage: curl -fsSL https://raw.githubusercontent.com/fall-out-bug/sdp_lab/main/scripts/install.sh | bash +# +# Environment overrides: +# SDP_REPO GitHub repo slug (default: fall-out-bug/sdp_lab) +# SDP_BRANCH Branch/tag to clone (default: main) +# SDP_SOURCE_DIR Local sdp_lab checkout to use instead of cloning +# SDP_HARNESS Harness selection: auto|all|claude-code,opencode,... (default: auto) +# SDP_TARGET Target directory (default: .) +# SDP_BIN_DIR Directory for the repo-local sdp binary (default: $SDP_TARGET/.sdp/bin) +set -euo pipefail + +REPO="${SDP_REPO:-fall-out-bug/sdp_lab}" +BRANCH="${SDP_BRANCH:-main}" +SOURCE_DIR="${SDP_SOURCE_DIR:-}" +HARNESS="${SDP_HARNESS:-auto}" +TARGET="${SDP_TARGET:-.}" +TARGET_ABS="$(cd "$TARGET" 2>/dev/null && pwd -P || true)" +if [[ -z "$TARGET_ABS" ]]; then + mkdir -p "$TARGET" + TARGET_ABS="$(cd "$TARGET" && pwd -P)" +fi +BIN_DIR="${SDP_BIN_DIR:-$TARGET_ABS/.sdp/bin}" +LOCAL_SDP="$BIN_DIR/sdp" -# Detect OS and architecture -OS=$(uname -s | tr '[:upper:]' '[:lower:]') -ARCH=$(uname -m) +echo "β†’ SDP installer: harness=$HARNESS target=$TARGET_ABS" +# Detect platform +OS="$(uname -s | tr '[:upper:]' '[:lower:]')" +ARCH="$(uname -m)" case "$ARCH" in - x86_64|amd64) ARCH="x86_64" ;; - arm64|aarch64) ARCH="arm64" ;; - *) - echo "ERROR: Unsupported architecture: $ARCH" - exit 1 - ;; + x86_64) ARCH="amd64" ;; + arm64|aarch64) ARCH="arm64" ;; esac -# Map OS names for archive -case "$OS" in - darwin) ARCHIVE_OS="Darwin" ;; - linux) ARCHIVE_OS="Linux" ;; - *) - echo "ERROR: Unsupported OS: $OS" - exit 1 - ;; -esac +echo "β†’ platform: $OS/$ARCH" -# Resolve version -if [ "$VERSION" = "latest" ]; then - latest_json=$(curl -sSL "https://api.github.com/repos/${REPO}/releases/latest") - VERSION=$(printf "%s" "$latest_json" | sed -n 's/.*"tag_name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1) - if [ -z "$VERSION" ]; then - latest_url=$(curl -fsSLI -o /dev/null -w '%{url_effective}' "https://github.com/${REPO}/releases/latest" || true) - VERSION=$(printf "%s" "$latest_url" | sed -n 's#.*/tag/\([^/]*\)$#\1#p') - fi - if [ -z "$VERSION" ]; then - echo "ERROR: Could not determine latest version" - exit 1 - fi -fi +supports_current_init() { + "$1" init --help 2>&1 | grep -q -- "--harness" +} -echo "Installing SDP ${VERSION} for ${OS}/${ARCH}..." +SDP_BIN="" -# Construct archive name (matches goreleaser naming) -ARCHIVE_NAME="${BINARY_NAME}_${ARCHIVE_OS}_${ARCH}.tar.gz" -BASE_URL="${SDP_RELEASE_BASE_URL:-https://github.com/${REPO}/releases/download/${VERSION}}" -DOWNLOAD_URL="${BASE_URL}/${ARCHIVE_NAME}" +if [[ -n "$SOURCE_DIR" ]]; then + SOURCE_ROOT="$(cd "$SOURCE_DIR" && pwd -P)" + SOURCE_LABEL="$SOURCE_ROOT" + echo "β†’ using local source: $SOURCE_ROOT" +else + if ! command -v git >/dev/null 2>&1; then + echo "error: required tool 'git' not found on PATH" >&2 + echo " Please install git and re-run this script." >&2 + exit 1 + fi -# Create temp directory -TMP_DIR=$(mktemp -d) -trap 'rm -rf "$TMP_DIR"' EXIT + TMPDIR_SDP="$(mktemp -d)" + trap 'rm -rf "$TMPDIR_SDP"' EXIT + SOURCE_ROOT="$TMPDIR_SDP/sdp_lab" + SOURCE_LABEL="$REPO@$BRANCH" -# Download archive -echo "Downloading ${ARCHIVE_NAME}..." -if ! curl -sSLf "$DOWNLOAD_URL" -o "${TMP_DIR}/${ARCHIVE_NAME}"; then - echo "ERROR: Failed to download ${DOWNLOAD_URL}" - echo "" - echo "Available releases: https://github.com/${REPO}/releases" - exit 1 + echo "β†’ cloning $REPO@$BRANCH into $SOURCE_ROOT" + git clone --depth=1 --branch "$BRANCH" "https://github.com/$REPO.git" "$SOURCE_ROOT" 2>&1 fi -# Download and verify checksum (FATAL on failure - security) -CHECKSUM_URL="${BASE_URL}/checksums.txt" -echo "Verifying checksum..." -if ! curl -sSLf "$CHECKSUM_URL" -o "${TMP_DIR}/checksums.txt"; then - echo "ERROR: Could not download checksums from $CHECKSUM_URL" - echo "This could indicate a network issue or tampered release." - exit 1 +# Strategy 1: if a compatible `sdp` binary is already on PATH, use it directly. +if command -v sdp >/dev/null 2>&1; then + FOUND_SDP="$(command -v sdp)" + if supports_current_init "$FOUND_SDP"; then + echo "β†’ found compatible sdp binary on PATH: $FOUND_SDP" + SDP_BIN="$FOUND_SDP" + else + echo "warning: found incompatible sdp binary on PATH: $FOUND_SDP" >&2 + echo "warning: it does not support 'sdp init --harness'; building $SOURCE_LABEL instead" >&2 + fi fi -cd "$TMP_DIR" -if grep -Fq "${ARCHIVE_NAME}" checksums.txt; then - grep -F "${ARCHIVE_NAME}" checksums.txt > checksums.single.txt - if command -v sha256sum >/dev/null 2>&1; then - if ! sha256sum -c checksums.single.txt; then - echo "ERROR: Checksum verification FAILED!" - echo "The downloaded archive may have been tampered with." - exit 1 - fi - elif command -v shasum >/dev/null 2>&1; then - if ! shasum -a 256 -c checksums.single.txt 2>/dev/null; then - echo "ERROR: Checksum verification FAILED!" - echo "The downloaded archive may have been tampered with." - exit 1 - fi - else - echo "ERROR: No sha256 tool found (sha256sum or shasum required for checksum verification)" - echo "Install one of: coreutils (sha256sum), perl (shasum)" - exit 1 - fi - echo "βœ… Checksum verified" -else - echo "ERROR: Archive ${ARCHIVE_NAME} not found in checksums file" +# Strategy 2: clone-and-build (offline-friendly, no GitHub Releases needed in v1). +# Requires: go (1.21+) +if [[ -z "$SDP_BIN" ]]; then + if ! command -v go >/dev/null 2>&1; then + echo "error: required tool 'go' not found on PATH" >&2 + echo " Please install go and re-run this script." >&2 exit 1 + fi + + echo "β†’ building sdp binary" + mkdir -p "$BIN_DIR" + (cd "$SOURCE_ROOT" && go build -tags "sqlite_fts5" -o "$LOCAL_SDP" ./cmd/sdp 2>&1) + SDP_BIN="$LOCAL_SDP" fi -cd - > /dev/null -# Extract binary -echo "Extracting..." -tar -xzf "${TMP_DIR}/${ARCHIVE_NAME}" -C "${TMP_DIR}" +if [[ ! -f "$TARGET_ABS/sdp.manifest.yaml" ]]; then + cp "$SOURCE_ROOT/sdp.manifest.yaml" "$TARGET_ABS/sdp.manifest.yaml" + echo "β†’ installed canonical sdp.manifest.yaml" +else + echo "β†’ keeping existing sdp.manifest.yaml" +fi -# Find the binary (might be in a subdirectory) -BINARY_PATH=$(find "${TMP_DIR}" -name "${BINARY_NAME}" -type f | head -1) -if [ -z "$BINARY_PATH" ]; then - echo "ERROR: Binary not found in archive" - exit 1 +if [[ ! -d "$TARGET_ABS/prompts" ]]; then + cp -R "$SOURCE_ROOT/prompts" "$TARGET_ABS/prompts" + echo "β†’ installed canonical prompts/" +else + echo "β†’ keeping existing prompts/" fi -# Install (remove old binary first to ensure update works) -mkdir -p "${INSTALL_DIR}" -rm -f "${INSTALL_DIR}/${BINARY_NAME}" -chmod +x "${BINARY_PATH}" -mv "${BINARY_PATH}" "${INSTALL_DIR}/${BINARY_NAME}" - -echo "" -echo "βœ… SDP ${VERSION} installed to ${INSTALL_DIR}/${BINARY_NAME}" -echo "" - -# Check if in PATH -case ":$PATH:" in -*":${INSTALL_DIR}:"*) - ;; -*) - echo "⚠️ ${INSTALL_DIR} is not in your PATH" - echo "" - echo "Add this to your shell profile (~/.bashrc, ~/.zshrc, etc.):" - echo " export PATH=\"\${HOME}/.local/bin:\${PATH}\"" - echo "" - echo "Then reload: source ~/.bashrc # or ~/.zshrc" - ;; -esac +echo "β†’ running sdp init" +"$SDP_BIN" init --harness "$HARNESS" --target "$TARGET_ABS" -# Verify installation -if ! "${INSTALL_DIR}/${BINARY_NAME}" version >/dev/null 2>&1; then - echo "WARNING: Installation verification failed. Run '${BINARY_NAME} version' to check." +mkdir -p "$BIN_DIR" +if [[ "$SDP_BIN" != "$LOCAL_SDP" ]]; then + cp "$SDP_BIN" "$LOCAL_SDP" + chmod 755 "$LOCAL_SDP" fi + +echo "βœ“ SDP installed in $TARGET_ABS" +echo "βœ“ repo-local binary: $LOCAL_SDP" +echo "β†’ for this shell: export PATH=\"$BIN_DIR:\$PATH\""