diff --git a/.github/workflows/check-runbooks.yml b/.github/workflows/check-runbooks.yml new file mode 100644 index 00000000..cf44a5cf --- /dev/null +++ b/.github/workflows/check-runbooks.yml @@ -0,0 +1,21 @@ +name: Check Runbooks Consistency + +# Runs on every PR (no path filter on purpose): renaming or removing an agent +# must trip this check even when nobody touched strategy/runbooks.json, since a +# dangling roster slug breaks the app's one-click team deploy. +on: + pull_request: + push: + branches: [main] + +jobs: + check-runbooks: + name: runbook rosters reference real agent slugs + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Validate runbook rosters + run: | + chmod +x scripts/check-runbooks.sh + ./scripts/check-runbooks.sh diff --git a/scripts/check-runbooks.sh b/scripts/check-runbooks.sh new file mode 100755 index 00000000..ad1d7c74 --- /dev/null +++ b/scripts/check-runbooks.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash +# +# check-runbooks.sh — enforce that strategy/runbooks.json stays in sync with the +# real agent roster. +# +# strategy/runbooks.json is the machine-readable roster for the NEXUS scenario +# runbooks: the Agency Agents app reads it to turn a runbook into a one-click +# team deploy, mapping each roster slug to a catalog agent. If a slug there +# doesn't resolve to a real agent file, the app can't deploy that team — so this +# check fails the build when: +# 1. runbooks.json is not valid JSON, or an entry is missing a required field +# 2. any roster `agents[]` slug does not match an agent .md filename stem +# 3. any `doc` path does not exist +# 4. a runbook `slug` is duplicated +# +# Slugs are the agent .md filename stem (the corpus id), e.g. +# engineering/engineering-frontend-developer.md -> "engineering-frontend-developer". +# Uses python3 (already required by check-agent-originality.sh) for JSON; no jq, +# so it runs the same on macOS and CI. Mirrors scripts/check-divisions.sh. +# +# Usage: ./scripts/check-runbooks.sh + +set -euo pipefail +cd "$(dirname "$0")/.." + +command -v python3 >/dev/null 2>&1 || { + echo "ERROR: python3 is required for the runbooks check." >&2 + exit 2 +} + +python3 - <<'PYEOF' +import json, os, subprocess, sys + +JSON = "strategy/runbooks.json" +errors = [] + +if not os.path.isfile(JSON): + print(f"ERROR {JSON} not found"); sys.exit(1) + +try: + data = json.load(open(JSON)) +except json.JSONDecodeError as e: + print(f"ERROR {JSON} is not valid JSON: {e}"); sys.exit(1) + +# Real slugs = filename stems of tracked agent .md files under division dirs. +NON_DIVISION = {"integrations", "examples", "strategy", "scripts", ".github"} +tracked = subprocess.check_output(["git", "ls-files", "*/*.md"]).decode().splitlines() +real = {os.path.basename(p)[:-3] for p in tracked if p.split("/")[0] not in NON_DIVISION} + +runbooks = data.get("runbooks") +if not isinstance(runbooks, list) or not runbooks: + print(f"ERROR {JSON} has no 'runbooks' array"); sys.exit(1) + +seen_slugs = set() +total_refs = 0 +for rb in runbooks: + rid = rb.get("slug", "") + for field in ("slug", "title", "mode", "doc", "roster"): + if field not in rb: + errors.append(f"runbook '{rid}' is missing required field \"{field}\"") + if rb.get("slug") in seen_slugs: + errors.append(f"duplicate runbook slug '{rb.get('slug')}'") + seen_slugs.add(rb.get("slug")) + doc = rb.get("doc") + if doc and not os.path.isfile(doc): + errors.append(f"runbook '{rid}': doc path does not exist: {doc}") + for g in rb.get("roster", []): + for slug in g.get("agents", []): + total_refs += 1 + if slug not in real: + errors.append(f"runbook '{rid}' / group '{g.get('group','?')}': " + f"slug '{slug}' does not match any agent .md filename stem") + +if errors: + print(f"FAILED: {len(errors)} runbook consistency error(s). " + f"strategy/runbooks.json must reference real agent slugs.\n") + for e in errors: + print(f" ERROR {e}") + sys.exit(1) + +print(f"PASSED: {len(runbooks)} runbooks, {total_refs} agent slug references — " + f"all resolve to real agent files.") +PYEOF diff --git a/strategy/runbooks.json b/strategy/runbooks.json new file mode 100644 index 00000000..9547fa45 --- /dev/null +++ b/strategy/runbooks.json @@ -0,0 +1,175 @@ +{ + "_note": "Machine-readable rosters for the NEXUS scenario runbooks in strategy/runbooks/. Consumed by the Agency Agents app to turn a runbook into a one-click team deploy: it reads the roster, maps each slug to a catalog agent, and installs the set. `agents[]` entries are SLUGS — the agent .md filename stem (the corpus id), e.g. engineering/engineering-frontend-developer.md -> \"engineering-frontend-developer\"; specialized/agents-orchestrator.md -> \"agents-orchestrator\" (note: the stem is NOT always division-prefixed, and display names are prefixed/drift — so rosters reference slugs, which are rename-proof and testable). `mode` is the NEXUS activation mode (Full | Sprint | Micro) that sizes the team; `roster` groups preserve the runbook's phase structure via `activation`. `doc` is the prose runbook the app renders. Keep this in sync with the markdown in strategy/runbooks/; every slug must resolve to a real agent file (scripts/check-runbooks.sh can guard this, mirroring check-divisions.sh). strategy/ holds orchestration doctrine, not installable agents — it is NOT a division (see divisions.json).", + "runbooks": [ + { + "slug": "startup-mvp", + "title": "Startup MVP Build", + "mode": "NEXUS-Sprint", + "duration": "4-6 weeks", + "summary": "Idea to live product with real users, fast — without skipping QA.", + "doc": "strategy/runbooks/scenario-startup-mvp.md", + "roster": [ + { + "group": "Core Team", + "activation": "always", + "agents": [ + "agents-orchestrator", + "project-manager-senior", + "product-sprint-prioritizer", + "design-ux-architect", + "engineering-frontend-developer", + "engineering-backend-architect", + "engineering-devops-automator", + "testing-evidence-collector", + "testing-reality-checker" + ] + }, + { + "group": "Growth Team", + "activation": "week 3+", + "agents": [ + "marketing-growth-hacker", + "marketing-content-creator", + "marketing-social-media-strategist" + ] + }, + { + "group": "Support Team", + "activation": "as needed", + "agents": [ + "design-brand-guardian", + "support-analytics-reporter", + "engineering-rapid-prototyper", + "engineering-ai-engineer", + "testing-performance-benchmarker", + "support-infrastructure-maintainer" + ] + } + ] + }, + { + "slug": "enterprise-feature", + "title": "Enterprise Feature Development", + "mode": "NEXUS-Sprint", + "duration": "6-12 weeks", + "summary": "Ship a major feature into an existing enterprise product with non-negotiable compliance, security, and quality gates, and multi-stakeholder alignment.", + "doc": "strategy/runbooks/scenario-enterprise-feature.md", + "roster": [ + { + "group": "Core Team", + "activation": "always", + "agents": [ + "agents-orchestrator", + "project-management-project-shepherd", + "project-manager-senior", + "product-sprint-prioritizer", + "design-ux-architect", + "design-ux-researcher", + "design-ui-designer", + "engineering-frontend-developer", + "engineering-backend-architect", + "engineering-senior-developer", + "engineering-devops-automator", + "testing-evidence-collector", + "testing-api-tester", + "testing-reality-checker", + "testing-performance-benchmarker" + ] + }, + { + "group": "Compliance & Governance", + "activation": "as needed", + "agents": [ + "support-legal-compliance-checker", + "design-brand-guardian", + "support-finance-tracker", + "support-executive-summary-generator" + ] + }, + { + "group": "Quality Assurance", + "activation": "as needed", + "agents": [ + "testing-test-results-analyzer", + "testing-workflow-optimizer", + "project-management-experiment-tracker" + ] + } + ] + }, + { + "slug": "marketing-campaign", + "title": "Multi-Channel Marketing Campaign", + "mode": "NEXUS-Sprint", + "duration": "2-4 weeks", + "summary": "Launch a coordinated, brand-consistent campaign across channels that drives measurable acquisition and engagement.", + "doc": "strategy/runbooks/scenario-marketing-campaign.md", + "roster": [ + { + "group": "Campaign Core", + "activation": "always", + "agents": [ + "marketing-social-media-strategist", + "marketing-content-creator", + "marketing-growth-hacker", + "design-brand-guardian", + "support-analytics-reporter" + ] + }, + { + "group": "Platform Specialists", + "activation": "as needed", + "agents": [ + "marketing-twitter-engager", + "marketing-tiktok-strategist", + "marketing-instagram-curator", + "marketing-reddit-community-builder", + "marketing-app-store-optimizer" + ] + }, + { + "group": "Support", + "activation": "as needed", + "agents": [ + "product-trend-researcher", + "project-management-experiment-tracker", + "support-executive-summary-generator", + "support-legal-compliance-checker" + ] + } + ] + }, + { + "slug": "incident-response", + "title": "Incident Response", + "mode": "NEXUS-Micro", + "duration": "Minutes to hours", + "summary": "Detection through post-mortem for a production incident — fast response without cutting corners.", + "doc": "strategy/runbooks/scenario-incident-response.md", + "roster": [ + { + "group": "P0 Critical Response", + "activation": "always", + "agents": [ + "support-infrastructure-maintainer", + "engineering-devops-automator", + "engineering-backend-architect", + "engineering-frontend-developer", + "support-support-responder", + "support-executive-summary-generator" + ] + }, + { + "group": "Verification & Post-Mortem", + "activation": "post-fix", + "agents": [ + "testing-evidence-collector", + "testing-api-tester", + "testing-workflow-optimizer", + "product-sprint-prioritizer" + ] + } + ] + } + ] +}