Skip to content

Commit d80ffea

Browse files
feat: Add orchestrated health check workflow with CLI integration
- OrchestratedHealthCheckWorkflow with 3 modes (daily/weekly/release) - Health score calculation (0-100) with weighted categories - Trend tracking and smart recommendations - CLI: empathy orchestrate health-check --mode [daily|weekly|release] - Beautiful console output with Rich formatting - 30 comprehensive tests (91.70% coverage) - JSON export for CI/CD integration Part of meta-orchestration system (v4.0) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 2797929 commit d80ffea

5 files changed

Lines changed: 1744 additions & 2 deletions

File tree

src/empathy_os/cli.py

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -773,6 +773,7 @@ def cmd_orchestrate(args):
773773
import asyncio
774774
import json
775775

776+
from empathy_os.workflows.orchestrated_health_check import OrchestratedHealthCheckWorkflow
776777
from empathy_os.workflows.orchestrated_release_prep import OrchestratedReleasePrepWorkflow
777778
from empathy_os.workflows.test_coverage_boost import TestCoverageBoostWorkflow
778779

@@ -924,12 +925,66 @@ def cmd_orchestrate(args):
924925
logger.exception("Orchestration workflow failed")
925926
return 1
926927

928+
elif workflow_type == "health-check":
929+
# Health Check workflow
930+
mode = args.mode or "daily"
931+
project_root = args.project_root or "."
932+
focus_area = getattr(args, "focus", None)
933+
934+
print(f" Mode: {mode.upper()}")
935+
print(f" Project Root: {project_root}")
936+
if focus_area:
937+
print(f" Focus Area: {focus_area}")
938+
print()
939+
940+
# Show agents for mode
941+
mode_agents = {
942+
"daily": ["Security", "Coverage", "Quality"],
943+
"weekly": ["Security", "Coverage", "Quality", "Performance", "Documentation"],
944+
"release": [
945+
"Security",
946+
"Coverage",
947+
"Quality",
948+
"Performance",
949+
"Documentation",
950+
"Architecture",
951+
],
952+
}
953+
954+
print(f" 🔍 {mode.capitalize()} Check Agents:")
955+
for agent in mode_agents.get(mode, []):
956+
print(f" • {agent}")
957+
print()
958+
959+
# Create workflow
960+
workflow = OrchestratedHealthCheckWorkflow(mode=mode, project_root=project_root)
961+
962+
try:
963+
# Execute workflow
964+
report = asyncio.run(workflow.execute())
965+
966+
# Display results
967+
if hasattr(args, "json") and args.json:
968+
print(json.dumps(report.to_dict(), indent=2))
969+
else:
970+
print(report.format_console_output())
971+
972+
# Return appropriate exit code (70+ is passing)
973+
return 0 if report.overall_health_score >= 70 else 1
974+
975+
except Exception as e:
976+
print(f" ❌ Error executing health check workflow: {e}")
977+
print()
978+
logger.exception("Health check workflow failed")
979+
return 1
980+
927981
else:
928982
print(f" ❌ Unknown workflow type: {workflow_type}")
929983
print()
930984
print(" Available workflows:")
931985
print(" - release-prep: Release readiness validation (parallel agents)")
932986
print(" - test-coverage: Boost test coverage through sequential agent composition")
987+
print(" - health-check: Project health assessment (daily/weekly/release modes)")
933988
print()
934989
return 1
935990

@@ -3472,11 +3527,11 @@ def main():
34723527
# Orchestrate command (meta-orchestration workflows)
34733528
parser_orchestrate = subparsers.add_parser(
34743529
"orchestrate",
3475-
help="Run meta-orchestration workflows (test-coverage, release-prep)",
3530+
help="Run meta-orchestration workflows (test-coverage, release-prep, health-check)",
34763531
)
34773532
parser_orchestrate.add_argument(
34783533
"workflow",
3479-
choices=["test-coverage", "release-prep"],
3534+
choices=["test-coverage", "release-prep", "health-check"],
34803535
help="Workflow to execute",
34813536
)
34823537
parser_orchestrate.add_argument(
@@ -3515,6 +3570,16 @@ def main():
35153570
type=float,
35163571
help="Maximum critical security issues (for release-prep, default: 0)",
35173572
)
3573+
# Health-check workflow arguments
3574+
parser_orchestrate.add_argument(
3575+
"--mode",
3576+
choices=["daily", "weekly", "release"],
3577+
help="Health check mode (for health-check, default: daily)",
3578+
)
3579+
parser_orchestrate.add_argument(
3580+
"--focus",
3581+
help="Focus area for health check (for health-check, optional)",
3582+
)
35183583
parser_orchestrate.add_argument(
35193584
"--json",
35203585
action="store_true",

src/empathy_os/cli_unified.py

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,212 @@ def workflow_recommend(
538538
)
539539

540540

541+
# =============================================================================
542+
# ORCHESTRATE SUBCOMMAND GROUP (Meta-Orchestration v4.0)
543+
# =============================================================================
544+
545+
orchestrate_app = typer.Typer(help="Meta-orchestration workflows (v4.0)")
546+
app.add_typer(orchestrate_app, name="orchestrate")
547+
548+
549+
@orchestrate_app.command("health-check")
550+
def orchestrate_health_check(
551+
mode: str = typer.Option("daily", "--mode", "-m", help="Check mode: daily, weekly, release"),
552+
project_root: Path = typer.Option(Path("."), "--project-root", "-p", help="Project root path"),
553+
json_output: bool = typer.Option(False, "--json", help="Output as JSON"),
554+
):
555+
"""Run orchestrated health check with adaptive agent teams.
556+
557+
Modes:
558+
daily: Quick parallel check (3 agents: Security, Coverage, Quality)
559+
weekly: Comprehensive parallel (5 agents: adds Performance, Docs)
560+
release: Deep refinement (6 agents: adds Architecture)
561+
"""
562+
import asyncio
563+
564+
from empathy_os.workflows.orchestrated_health_check import OrchestratedHealthCheckWorkflow
565+
566+
async def run_health_check():
567+
workflow = OrchestratedHealthCheckWorkflow(mode=mode)
568+
report = await workflow.execute(project_root=str(project_root))
569+
570+
if json_output:
571+
import json
572+
573+
console.print(json.dumps(report.to_dict(), indent=2))
574+
else:
575+
# Beautiful console output
576+
console.print("\n[bold cyan]🏥 HEALTH CHECK REPORT[/bold cyan]")
577+
console.print("=" * 60)
578+
579+
# Health score with color coding
580+
score_color = (
581+
"green"
582+
if report.overall_health_score >= 80
583+
else "yellow" if report.overall_health_score >= 60 else "red"
584+
)
585+
console.print(
586+
f"\n[bold {score_color}]Health Score: {report.overall_health_score}/100 (Grade: {report.grade})[/bold {score_color}]"
587+
)
588+
console.print(f"[dim]Trend: {report.trend}[/dim]")
589+
console.print(f"[dim]Duration: {report.execution_time:.2f}s[/dim]")
590+
591+
# Issues
592+
if report.issues:
593+
console.print(f"\n[bold red]⚠️ Issues Found ({len(report.issues)}):[/bold red]")
594+
for issue in report.issues[:5]:
595+
console.print(f" • {issue}")
596+
597+
# Recommendations
598+
if report.recommendations:
599+
console.print("\n[bold yellow]💡 Recommendations:[/bold yellow]")
600+
for i, rec in enumerate(report.recommendations[:5], 1):
601+
console.print(f" {i}. {rec}")
602+
603+
console.print("\n" + "=" * 60)
604+
605+
return report
606+
607+
try:
608+
asyncio.run(run_health_check())
609+
except Exception as e:
610+
console.print(f"[bold red]Error:[/bold red] {e}")
611+
raise typer.Exit(code=1)
612+
613+
614+
@orchestrate_app.command("release-prep")
615+
def orchestrate_release_prep(
616+
project_root: Path = typer.Option(Path("."), "--project-root", "-p", help="Project root path"),
617+
min_coverage: float = typer.Option(80.0, "--min-coverage", help="Minimum test coverage %"),
618+
min_quality: float = typer.Option(7.0, "--min-quality", help="Minimum quality score (0-10)"),
619+
max_critical: int = typer.Option(0, "--max-critical", help="Max critical issues allowed"),
620+
json_output: bool = typer.Option(False, "--json", help="Output as JSON"),
621+
):
622+
"""Run orchestrated release preparation with parallel validation.
623+
624+
Runs 4 agents in parallel:
625+
- Security Auditor (vulnerability scan)
626+
- Test Coverage Analyzer (gap analysis)
627+
- Code Quality Reviewer (best practices)
628+
- Documentation Writer (completeness check)
629+
"""
630+
import asyncio
631+
632+
from empathy_os.workflows.orchestrated_release_prep import OrchestratedReleasePrepWorkflow
633+
634+
async def run_release_prep():
635+
workflow = OrchestratedReleasePrepWorkflow(
636+
quality_gates={
637+
"min_coverage": min_coverage,
638+
"min_quality_score": min_quality,
639+
"max_critical_issues": max_critical,
640+
}
641+
)
642+
report = await workflow.execute(path=str(project_root))
643+
644+
if json_output:
645+
import json
646+
647+
console.print(json.dumps(report.to_dict(), indent=2))
648+
else:
649+
console.print("\n[bold cyan]📋 RELEASE PREPARATION REPORT[/bold cyan]")
650+
console.print("=" * 60)
651+
652+
approval_color = "green" if report.approved else "red"
653+
approval_emoji = "✅" if report.approved else "❌"
654+
console.print(
655+
f"\n[bold {approval_color}]{approval_emoji} {'APPROVED' if report.approved else 'NOT APPROVED'}[/bold {approval_color}]"
656+
)
657+
console.print(f"[dim]Confidence: {report.confidence}[/dim]")
658+
console.print(f"[dim]Duration: {report.total_duration:.2f}s[/dim]")
659+
660+
# Quality gates
661+
console.print("\n[bold]Quality Gates:[/bold]")
662+
for gate in report.quality_gates:
663+
gate_emoji = "✅" if gate.passed else "❌"
664+
console.print(
665+
f" {gate_emoji} {gate.name}: {gate.actual:.1f} (threshold: {gate.threshold:.1f})"
666+
)
667+
668+
# Blockers
669+
if report.blockers:
670+
console.print("\n[bold red]🚫 Blockers:[/bold red]")
671+
for blocker in report.blockers:
672+
console.print(f" • {blocker}")
673+
674+
# Warnings
675+
if report.warnings:
676+
console.print("\n[bold yellow]⚠️ Warnings:[/bold yellow]")
677+
for warning in report.warnings:
678+
console.print(f" • {warning}")
679+
680+
console.print("\n" + "=" * 60)
681+
682+
return report
683+
684+
try:
685+
asyncio.run(run_release_prep())
686+
except Exception as e:
687+
console.print(f"[bold red]Error:[/bold red] {e}")
688+
raise typer.Exit(code=1)
689+
690+
691+
@orchestrate_app.command("test-coverage")
692+
def orchestrate_test_coverage(
693+
project_root: Path = typer.Option(Path("."), "--project-root", "-p", help="Project root path"),
694+
target: float = typer.Option(90.0, "--target", "-t", help="Target coverage percentage"),
695+
json_output: bool = typer.Option(False, "--json", help="Output as JSON"),
696+
):
697+
"""Run orchestrated test coverage boost with sequential stages.
698+
699+
Runs 3 stages sequentially:
700+
1. Coverage Analyzer → Identify gaps
701+
2. Test Generator → Create tests
702+
3. Test Validator → Verify coverage
703+
"""
704+
import asyncio
705+
706+
from empathy_os.workflows.test_coverage_boost import TestCoverageBoostWorkflow
707+
708+
async def run_test_coverage():
709+
workflow = TestCoverageBoostWorkflow(target_coverage=target)
710+
report = await workflow.execute(project_root=str(project_root))
711+
712+
if json_output:
713+
import json
714+
715+
console.print(json.dumps(report.to_dict(), indent=2))
716+
else:
717+
console.print("\n[bold cyan]🧪 TEST COVERAGE BOOST REPORT[/bold cyan]")
718+
console.print("=" * 60)
719+
720+
success_color = "green" if report.success else "red"
721+
success_emoji = "✅" if report.success else "❌"
722+
console.print(
723+
f"\n[bold {success_color}]{success_emoji} {'SUCCESS' if report.success else 'FAILED'}[/bold {success_color}]"
724+
)
725+
console.print(f"[dim]Initial: {report.initial_coverage:.1f}%[/dim]")
726+
console.print(f"[dim]Final: {report.final_coverage:.1f}%[/dim]")
727+
console.print(f"[dim]Improvement: +{report.improvement:.1f}%[/dim]")
728+
console.print(f"[dim]Duration: {report.total_duration:.2f}s[/dim]")
729+
730+
# Stage results
731+
console.print("\n[bold]Stage Results:[/bold]")
732+
for i, stage in enumerate(report.stage_results, 1):
733+
stage_emoji = "✅" if stage["success"] else "❌"
734+
console.print(f" {stage_emoji} Stage {i}: {stage.get('description', 'N/A')}")
735+
736+
console.print("\n" + "=" * 60)
737+
738+
return report
739+
740+
try:
741+
asyncio.run(run_test_coverage())
742+
except Exception as e:
743+
console.print(f"[bold red]Error:[/bold red] {e}")
744+
raise typer.Exit(code=1)
745+
746+
541747
# =============================================================================
542748
# TELEMETRY SUBCOMMAND GROUP
543749
# =============================================================================

src/empathy_os/workflows/__init__.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@
6666
# Keyboard Conductor (v3.6) - keyboard shortcut generation
6767
from .keyboard_shortcuts import KeyboardShortcutWorkflow
6868
from .manage_documentation import ManageDocumentationCrew, ManageDocumentationCrewResult
69+
70+
# Meta-orchestration workflows (v4.0)
71+
from .orchestrated_health_check import HealthCheckReport, OrchestratedHealthCheckWorkflow
72+
from .orchestrated_release_prep import OrchestratedReleasePrepWorkflow, ReleaseReadinessReport
6973
from .perf_audit import PerformanceAuditWorkflow
7074
from .pr_review import PRReviewResult, PRReviewWorkflow
7175
from .refactor_plan import RefactorPlanWorkflow
@@ -319,6 +323,7 @@ def list_workflows() -> list[dict]:
319323
"DocumentationOrchestrator",
320324
# Health check crew integration (v3.1)
321325
"HealthCheckWorkflow",
326+
"HealthCheckReport",
322327
# Keyboard Conductor (v3.6)
323328
"KeyboardShortcutWorkflow",
324329
"ManageDocumentationCrew",
@@ -360,4 +365,8 @@ def list_workflows() -> list[dict]:
360365
"refresh_workflow_registry",
361366
"steps_from_tier_map",
362367
"validate_step_config",
368+
# Meta-orchestration workflows (v4.0)
369+
"OrchestratedHealthCheckWorkflow",
370+
"OrchestratedReleasePrepWorkflow",
371+
"ReleaseReadinessReport",
363372
]

0 commit comments

Comments
 (0)