Add Phase 10-12 implementation: multi-tenant, marketplace, observability

Major additions:
- marketplace/: Agent template registry with FTS5 search, ratings, versioning
- observability/: Prometheus metrics, distributed tracing, structured logging
- ledger/migrations/: Database migration scripts for multi-tenant support
- tests/governance/: 15 new test files for phases 6-12 (295 total tests)
- bin/validate-phases: Full 12-phase validation script

New features:
- Multi-tenant support with tenant isolation and quota enforcement
- Agent marketplace with semantic versioning and search
- Observability with metrics, tracing, and log correlation
- Tier-1 agent bootstrap scripts

Updated components:
- ledger/api.py: Extended API for tenants, marketplace, observability
- ledger/schema.sql: Added tenant, project, marketplace tables
- testing/framework.ts: Enhanced test framework
- checkpoint/checkpoint.py: Improved checkpoint management

Archived:
- External integrations (Slack/GitHub/PagerDuty) moved to .archive/
- Old checkpoint files cleaned up

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
profit
2026-01-24 18:39:47 -05:00
co-authored by Claude Opus 4.5
parent 4b5b3b0a2d
commit 8c6e7831e9
184 changed files with 16249 additions and 13673 deletions
+70 -7
View File
@@ -594,15 +594,52 @@ class CheckpointManager:
return name.startswith(".") or name in SKIP_DIRS or "evd-" in name
def parse_phase(content: str) -> str:
content_lower = content.lower()
if "complete" in content_lower and "in_progress" not in content_lower:
return "complete"
if "in_progress" in content_lower or "in progress" in content_lower:
return "in_progress"
if "blocked" in content_lower:
"""Parse phase status from STATUS.md content.
Looks for explicit status in 'Current Phase' section first,
then falls back to checking the header area only (first 500 chars).
This prevents activity log entries from triggering false status.
"""
# First, try to find explicit status in Current Phase section
# Pattern: **STATUS** or **STATUS:** or **PHASE X: NAME** (Status)
phase_pattern = r'\*\*\s*(COMPLETE|BLOCKED|IN[_\s]?PROGRESS|NEEDS[_\s]?REVIEW|NOT[_\s]?STARTED)\s*\*\*'
phase_match = re.search(phase_pattern, content[:1000], re.IGNORECASE)
if phase_match:
status = phase_match.group(1).lower().replace(" ", "_").replace("-", "_")
if "complete" in status:
return "complete"
if "progress" in status:
return "in_progress"
if "block" in status:
return "blocked"
if "review" in status:
return "needs_review"
return "not_started"
# Check for status in parentheses after phase name: (Complete) or (Blocked)
paren_pattern = r'\((?:status:\s*)?(Complete|Blocked|In[_\s]?Progress|Needs[_\s]?Review)\)'
paren_match = re.search(paren_pattern, content[:1000], re.IGNORECASE)
if paren_match:
status = paren_match.group(1).lower()
if "complete" in status:
return "complete"
if "progress" in status:
return "in_progress"
if "block" in status:
return "blocked"
if "review" in status:
return "needs_review"
# Fallback: check header area only (first 500 chars) for keywords
header = content[:500].lower()
if "blocked" in header:
return "blocked"
if "needs_review" in content_lower or "needs review" in content_lower:
if "in_progress" in header or "in progress" in header:
return "in_progress"
if "needs_review" in header or "needs review" in header:
return "needs_review"
if "complete" in header:
return "complete"
return "not_started"
def parse_timestamp(content: str) -> Optional[str]:
@@ -1472,6 +1509,32 @@ Examples:
print(f" ⚠️ Needs Review: {summary.get('needs_review', 0):3d}")
print(f" ⬜ Not Started: {summary.get('not_started', 0):3d}")
# Check project state for blockers
project_state_path = Path("/opt/agent-governance/project_state.yaml")
if project_state_path.exists():
try:
import yaml
with open(project_state_path) as f:
project_state = yaml.safe_load(f)
overall = project_state.get("project", {}).get("overall_status", "unknown")
blockers = project_state.get("blockers", [])
pending_blockers = [b for b in blockers if b.get("status") == "pending"]
if overall != "complete" or pending_blockers:
print(f"\n[PROJECT STATE WARNING]")
print(f" Overall Status: {overall.upper()}")
if pending_blockers:
print(f" Pending Blockers: {len(pending_blockers)}")
for b in pending_blockers:
print(f" ❗ {b.get('id', 'unknown')}: {b.get('description', 'No description')}")
if b.get("resolution_doc"):
print(f" See: {b.get('resolution_doc')}")
except ImportError:
pass # yaml not available, skip
except Exception as e:
print(f"\n[PROJECT STATE ERROR] Could not load: {e}")
# Active directories (non-complete)
active_dirs = [d for d in checkpoint.directory_statuses
if d.phase in ("in_progress", "blocked", "needs_review")]