Add strategy/runbooks.json — NEXUS runbook rosters by slug + CI guard (#664)

The app can't reliably resolve runbook rosters from display names (catalog
slugs are inconsistently division-prefixed, and names drift). This adds a
machine-readable manifest so the app reads rosters as data and maps each
slug to a catalog agent for one-click team deploy.

- strategy/runbooks.json: the 4 NEXUS scenarios (startup-mvp,
  enterprise-feature, marketing-campaign, incident-response), each with
  mode, duration, summary, doc, and a grouped roster. Every agents[] entry
  is a verified slug = the agent .md filename stem (the corpus id), resolved
  against the live roster — not a slugified display name. (Notably
  "Senior Project Manager" is project-manager-senior, NOT
  project-management-senior-project-manager, which naive mapping assumes.)
- scripts/check-runbooks.sh + .github/workflows/check-runbooks.yml: guard
  (mirrors check-divisions.sh) failing the build if any roster slug doesn't
  resolve to a real agent file, a doc path is missing, or JSON is malformed —
  so renaming/removing an agent can't silently break the app's deploy.

All 64 slug references verified; guard passes and fails correctly.


Claude-Session: https://claude.ai/code/session_01WKnDRWM4izsB8WAXKszhsq

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Michael Sitarzewski
2026-07-05 12:41:01 -05:00
committed by GitHub
parent 90ae2b27d1
commit cb45d3ea8c
3 changed files with 279 additions and 0 deletions
+21
View File
@@ -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
+83
View File
@@ -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", "<no 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
+175
View File
@@ -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"
]
}
]
}
]
}