From dso
Use when verifying project health after completing work, before closing tasks, or when you need confidence that code, CI, staging deployment, and live environment are all passing. Does not fix issues — only detects and reports them.
npx claudepluginhub navapbc/digital-service-orchestra --plugin dso-devThis skill is limited to using the following tools:
<SUB-AGENT-GUARD>
Provides Ktor server patterns for routing DSL, plugins (auth, CORS, serialization), Koin DI, WebSockets, services, and testApplication testing.
Conducts multi-source web research with firecrawl and exa MCPs: searches, scrapes pages, synthesizes cited reports. For deep dives, competitive analysis, tech evaluations, or due diligence.
Provides demand forecasting, safety stock optimization, replenishment planning, and promotional lift estimation for multi-location retailers managing 300-800 SKUs.
"ERROR: /dso:validate-work cannot run in sub-agent context — it requires the Agent tool to dispatch its own sub-agents. Invoke this skill directly from the orchestrator instead."
Do NOT proceed with any skill logic if the Agent tool is unavailable.
Comprehensive project health verification using parallel sub-agents. Detects issues across local checks, CI, staging deployment, and live environment — but does NOT fix them. Reports a pass/fail summary with actionable details for each failure.
/dso:debug-everything instead)/dso:onboarding)/dso:sprint)Five sub-agents across two batches. Batch 1 (4 agents) runs in parallel. Batch 2 (1 agent) is gated on staging deployment health from Batch 1. No commits between batches because this skill is read-only (no files modified).
Batch 1 (parallel): Local ─┐
CI ────┤
Issues ┼─→ Collect Results ─→ Gate Check
Deploy ┘ │
▼
Batch 2 (gated): Staging Test
(only if Deploy passed)
Before launching sub-agents, read all project-specific values from workflow-config.yaml via read-config.sh.
REPO_ROOT=$(git rev-parse --show-toplevel)
# CLAUDE_PLUGIN_ROOT is set by the plugin loader; required for locating plugin scripts
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:?CLAUDE_PLUGIN_ROOT must be set by the plugin loader}"
PLUGIN_SCRIPTS_DIR="$PLUGIN_ROOT/scripts"
READ_CONFIG="$PLUGIN_SCRIPTS_DIR/read-config.sh"
CONFIG_FILE="$REPO_ROOT/workflow-config.yaml"
# Staging config
STAGING_URL=$("$READ_CONFIG" staging.url "$CONFIG_FILE" 2>/dev/null || true)
STAGING_DEPLOY_CHECK=$("$READ_CONFIG" staging.deploy_check "$CONFIG_FILE" 2>/dev/null || true)
STAGING_TEST=$("$READ_CONFIG" staging.test "$CONFIG_FILE" 2>/dev/null || true)
STAGING_ROUTES=$("$READ_CONFIG" staging.routes "$CONFIG_FILE" 2>/dev/null || echo "/")
STAGING_HEALTH_PATH=$("$READ_CONFIG" staging.health_path "$CONFIG_FILE" 2>/dev/null || echo "/health")
# Commands config
VALIDATE_CMD=$("$READ_CONFIG" commands.validate "$CONFIG_FILE" 2>/dev/null || true)
TEST_E2E_CMD=$("$READ_CONFIG" commands.test_e2e "$CONFIG_FILE" 2>/dev/null || true)
TEST_VISUAL_CMD=$("$READ_CONFIG" commands.test_visual "$CONFIG_FILE" 2>/dev/null || true)
DB_STATUS_CMD=$("$READ_CONFIG" database.status_cmd "$CONFIG_FILE" 2>/dev/null || true)
# CI config
INTEGRATION_WORKFLOW=$("$READ_CONFIG" ci.integration_workflow "$CONFIG_FILE" 2>/dev/null || true)
# Staging relevance classifier script (project-provided, optional)
STAGING_RELEVANCE_SCRIPT=$("$READ_CONFIG" staging.relevance_script "$CONFIG_FILE" 2>/dev/null || true)
# Visual baseline path for pre-check
VISUAL_BASELINE_PATH=$("$READ_CONFIG" visual.baseline_directory "$CONFIG_FILE" 2>/dev/null || true)
If STAGING_URL is empty or absent, set stagingConfigured = false. All staging sub-agents (Sub-Agent 4 and Sub-Agent 5) will be SKIPPED with the message: "SKIPPED (staging not configured)".
Before launching sub-agents, check for a scope file that limits which domains
to verify. This is written by callers (e.g., /dso:debug-everything) that have
already verified some domains.
/tmp/validate-work-scope-*.json files. If multiple exist, use the
most recent one (highest timestamp in filename).generatedAt < 1 hour old):
domains array to determine which domains to check.local, ci, issues, deploy, staging_test."Loaded domain scope from <file> — checking only: <domain list>. Skipped domains (verified by caller): <skipped list>."staging_test (Sub-Agent 5) always runs regardless of scope
— it is the only validation that catches live environment regressions.scopedDomains to the list from the file. For any domain NOT in the
list, skip its sub-agent and mark it as SKIPPED (verified by caller) in
the final report.scopedDomains = all. This is the backward-compatible path.Skip this step entirely if stagingConfigured = false (staging URL absent).
Before launching staging-related sub-agents, determine whether the current changes affect the deployed application. Changes that only impact local development tooling (skills, agent guidance, shell scripts, docs, tracker metadata) do not need staging verification.
Compute the changed files for the current branch:
REPO_ROOT=$(git rev-parse --show-toplevel)
CHANGED_FILES=$(git diff --name-only main...HEAD 2>/dev/null || git diff --name-only HEAD~1...HEAD 2>/dev/null || echo "")
If CHANGED_FILES is empty, set stagingRelevant = false and skip to Step 1 (no staging test needed — nothing was deployed).
If STAGING_RELEVANCE_SCRIPT is set (from staging.relevance_script config key):
echo "$CHANGED_FILES" | bash "$REPO_ROOT/$STAGING_RELEVANCE_SCRIPT"
STAGING_RELEVANCE=$?
Interpret exit codes:
stagingRelevant = truestagingRelevant = falsestagingRelevant = falseIf STAGING_RELEVANCE_SCRIPT is absent: set stagingRelevant = true and proceed with all staging sub-agents. (Conservative default: without a classifier, assume changes may affect staging.)
Set stagingRelevant = true/false based on the result. Pass this flag to
Step 1 and Step 3 to control sub-agent dispatch.
Note: If scopedDomains was set in Step 0b, only launch sub-agents for
domains in the scoped list. Skip the others and record them as
SKIPPED (verified by caller) in the report.
Note: If stagingConfigured = false, skip Sub-Agent 4 and mark it as
SKIPPED (staging not configured) in the report.
Note: If stagingRelevant = false (from Step 0c), skip Sub-Agent 4
(Staging Deployment Check). Mark it as
SKIPPED (non-deployment changes only) in the report.
Subagent: subagent_type="general-purpose", model="haiku"
Read prompt from: $PLUGIN_ROOT/skills/validate-work/prompts/local-validation.md
Prepend a ### Config Values block to the prompt:
### Config Values
VALIDATE_CMD=<value from commands.validate, or ABSENT>
TEST_E2E_CMD=<value from commands.test_e2e, or ABSENT>
TEST_VISUAL_CMD=<value from commands.test_visual, or ABSENT>
DB_STATUS_CMD=<value from database.status_cmd, or ABSENT>
Subagent: subagent_type="general-purpose", model="haiku"
Read prompt from: $PLUGIN_ROOT/skills/validate-work/prompts/ci-status.md
Prepend a ### Config Values block to the prompt:
### Config Values
PLUGIN_SCRIPTS_DIR=<absolute path to plugin scripts directory>
INTEGRATION_WORKFLOW=<value from ci.integration_workflow, or ABSENT>
Subagent: subagent_type="general-purpose", model="haiku"
Read prompt from: $PLUGIN_ROOT/skills/validate-work/prompts/tickets-health.md
Prepend a ### Config Values block to the prompt:
### Config Values
PLUGIN_SCRIPTS_DIR=<absolute path to plugin scripts directory>
Subagent: subagent_type="general-purpose", model="sonnet"
Skip if stagingConfigured = false or stagingRelevant = false (see above).
Read prompt from: $PLUGIN_ROOT/skills/validate-work/prompts/staging-deployment-check.md
Replace config placeholders in the prompt before dispatching:
{STAGING_URL} → value of STAGING_URL{STAGING_DEPLOY_CHECK} → value of STAGING_DEPLOY_CHECK (or ABSENT if not set){STAGING_HEALTH_PATH} → value of STAGING_HEALTH_PATH (default: /health)When STAGING_DEPLOY_CHECK is absent, Sub-Agent 4 uses Mode D (generic HTTP health check) as described in the prompt.
After all Batch 1 sub-agents complete:
Before launching the browser-based staging test, check the visual regression
baseline state. Skip this step if TEST_VISUAL_CMD is absent.
Visual tests only run on CI (Linux) — they always skip on macOS because font rendering differs ~11%, causing false pixel-diff failures.
bash ".claude/scripts/dso check-visual-baseline.sh"
If on macOS (the common local case): Report VISUAL_REGRESSION=skipped_macos.
This is expected, not a bug. Baselines are generated and compared on CI (Linux).
If on Linux and visual command passes: Visual regression baselines confirm
the UI matches expectations. Pass VISUAL_REGRESSION=pass as context.
If on Linux and visual command fails: Pass the diff output as context.
Pass VISUAL_REGRESSION=fail with the diff details.
If baselines don't exist: Pass VISUAL_REGRESSION=no_baselines. Recommend
running the visual baseline workflow to generate them.
Note: The staging-environment-test.md prompt uses a tiered approach (deterministic pre-checks,
@playwright/cli run-codebatching, API-driven checks where possible). See/dso:playwright-debugfor the 3-tier process it follows.
Pre-check: If stagingConfigured = false or stagingRelevant = false (from
Step 0 or 0c), skip this entire step. Mark Sub-Agent 5 as
SKIPPED (staging not configured) or SKIPPED (non-deployment changes only)
in the report and proceed directly to Step 4 (Final Report).
Subagent: subagent_type="general-purpose", model="sonnet"
This sub-agent performs validation of the live staging environment.
Read prompt from: $PLUGIN_ROOT/skills/validate-work/prompts/staging-environment-test.md
Replace config placeholders in the prompt before dispatching:
{STAGING_URL} → value of STAGING_URL{STAGING_TEST} → value of STAGING_TEST (or ABSENT if not set){STAGING_ROUTES} → value of STAGING_ROUTES (default: /){STAGING_HEALTH_PATH} → value of STAGING_HEALTH_PATH (default: /health)When STAGING_TEST is absent, Sub-Agent 5 uses Mode D (generic tiered validation)
as described in the prompt.
Append visual regression context to the sub-agent prompt based on Step 2b results:
### Visual Regression Context
VISUAL_REGRESSION={pass|fail|skipped_macos|no_baselines|skipped}
{if fail: include diff output identifying changed elements}
{if pass: "Visual regression baselines pass — skip full-page screenshots, focus on staging-specific behavior."}
{if skipped_macos: "Visual tests skip on macOS (font rendering differs ~11%). Baselines are generated and compared on CI (Linux). This is expected — not a bug."}
{if no_baselines: "No visual baselines exist. Run the visual baseline workflow to generate them."}
{if skipped: "Visual regression test command not configured — skipped."}
Append change scope context to the sub-agent prompt when called from /dso:sprint (or any caller that provides a CHANGED_FILES list):
Check if the current invocation context contains a ### Sprint Change Scope block (written by /dso:sprint Phase 7 Step 1). If present, append it verbatim to the sub-agent prompt:
### Change Scope
CHANGED_FILES:
<list of files from the Sprint Change Scope block, one per line>
If no CHANGED_FILES context was provided by the caller, omit the ### Change Scope block entirely — the sub-agent will then default to full browser automation (safe fallback).
Aggregate all sub-agent results into a single report:
## Validation Report
### Summary
| Domain | Status | Details |
|-----------------|-------------------|----------------------------------|
| Local checks | PASS/WARN/FAIL | format, lint, types, tests, DB |
| CI workflow | PASS/FAIL | workflow URL, duration |
| Issue health | PASS/FAIL | issue count, blocked count |
| Staging deploy | PASS/FAIL/SKIPPED | env health, endpoint status |
| Staging test | PASS/FAIL/SKIPPED | phase results |
### Overall: PASS / WARN / FAIL (X of N domains passing)
**WARN** means static checks pass but E2E tests were skipped locally — regressions
may only be caught by CI (adds ~15-30 min latency to regression discovery).
### Failures / Warnings (if any)
[Details for each failing or warning domain, including evidence from sub-agents]
If Local checks = WARN:
- Include the full WARNING block from the local-validation sub-agent
- Include the port conflict pre-check result
- Explicitly call out: "E2E was silently skipped. Run the project's E2E command
to verify locally, or push and wait for CI to run the full E2E suite."
### Recommended Actions
[For each failure or warning, suggest which skill or command to run — do NOT run them]
Overall status rules:
| Failure / Warning Domain | Recommended Action |
|---|---|
| Local checks FAIL | Run /dso:debug-everything |
| Local checks WARN (E2E skipped) | Run the project's E2E command manually to close the gap, or push and wait for CI E2E results |
| Local checks WARN (port conflict) | Identify and stop the conflicting process on the E2E port, then re-run the E2E command |
| CI workflow fails | Check failed job logs via gh run view, fix locally, re-push |
| Issue health fails | Run /dso:tickets-health |
| Staging deploy not ready | Deployment still in progress. Wait and re-run /dso:validate-work, or check environment console manually |
| Staging deploy unhealthy | Check environment health, review deployment logs |
| Staging test fails | Run /staging-test to create bugs with TDD criteria and screenshot evidence |
| Staging test inconclusive | Wait 5 minutes and re-run /dso:validate-work, or run /staging-test for targeted investigation |
| Playwright cannot reach staging | Site unreachable despite health endpoint passing. Check environment health manually |
| Database not running | Run the project's database start command (see database.ensure_cmd in config) |
| Unpushed commits | Push with git push before expecting CI/staging updates |
All sub-agents dispatched by this skill are read-only. The orchestrator MUST NOT instruct sub-agents to fix any issue it discovers, and sub-agents MUST NOT use modifying tools or commands regardless of how the situation is framed.
Sub-agents must STOP and report — never fix. Prohibited tools and commands for all sub-agents:
git commit, git push, git add, git checkout, git reset.claude/scripts/dso ticket transition, .claude/scripts/dso ticket createmake, pip install, npm install, poetry installEach prompt file in prompts/ contains a ## READ-ONLY ENFORCEMENT section with this hard-stop language. If a sub-agent rationalizes fixing a problem ("it's a quick fix", "it will save time"), that is a violation — it must TERMINATE its turn and report the finding instead.
orchestration.max_agents — respects the project's concurrency limit (see dso-config.conf; 4 in Batch 1, 1 in Batch 2)pwd first — per CLAUDE.md requirementworkflow-config.yaml via read-config.sh