Files
dev-puppeteer/my-deepagent-seed/schemas/validate.py
chungyeong 1fe59d16ca chore: my-deepagent-seed (BudgetTracker PoC + v0.1.0 seed assets)
Pre-flight assets prepared on the main machine before the new-machine
rewrite of my-deepagent in Python.

- poc/: BudgetTracker + CostMiddleware + MockChatModel PoC.
  Validates wrap_model_call pattern, SQLite WAL + ON CONFLICT upsert,
  per-scope cap accounting. 5/5 pytest PASS in isolated uv venv.
- schemas/: 10 personas (Anthropic Sonnet/Opus/Haiku + DeepSeek mix),
  3 workflows (spec-and-review, bug-fix-with-reproduction,
  code-investigation), 4 artifact JSON Schemas (dev/spec@1,
  dev/phase-plan@1, dev/review-finding-batch@1, common/final-report@1).
- schemas/validate.py: pydantic + Draft202012 cross-validation.
  18/18 assets verified.
- README.md: new-machine bootstrap instructions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 19:39:37 +09:00

179 lines
5.1 KiB
Python

"""Validate all seed assets. Run from schemas/ directory."""
import json
import sys
from pathlib import Path
from typing import Any, Literal
import jsonschema
import yaml
from pydantic import BaseModel, Field, ValidationError
class FilesystemPermissionSpec(BaseModel):
operations: list[Literal["read", "write", "edit", "ls"]]
paths: list[str]
mode: Literal["allow", "deny"] = "allow"
class PersonaSubagent(BaseModel):
name: str
description: str
system_prompt: str
allowed_tools: list[str] = []
model: str | None = None
permissions: list[FilesystemPermissionSpec] = []
interrupt_on: dict[str, Any] = {}
class Persona(BaseModel):
name: str
version: int = Field(ge=1)
description: str | None = None
backend: Literal["openrouter", "anthropic", "openai", "google", "fake"]
model: str
provider_origin: str
capabilities: list[str]
max_risk_level: Literal["low", "medium", "high"]
allowed_roles: list[str] | None = None
system_prompt: str
allowed_tools: list[str] | None = None
subagents: list[PersonaSubagent] = []
permissions: list[FilesystemPermissionSpec] = []
interrupt_on: dict[str, Any] | None = None
model_params: dict[str, Any] = {}
deepagents_backend: Literal[
"state", "local_shell", "filesystem", "composite", "langsmith"
] = "local_shell"
fallback_model: str | None = None
max_cost_per_call_usd: float | None = None
class ExpectedArtifact(BaseModel):
path: str
schema_: str = Field(alias="schema")
model_config = {"populate_by_name": True}
class WorkflowPhase(BaseModel):
key: str
title: str
risk: Literal["low", "medium", "high"]
role: str
expected_artifact: ExpectedArtifact | None = None
gates: list[str] = []
timeout_seconds: int | None = None
instructions: str
max_budget_usd: float | None = None
class WorkflowRole(BaseModel):
id: str
required_capabilities: list[str]
preferred_backends: list[str] = []
fallback_personas: list[str] = []
class WorkflowTemplate(BaseModel):
name: str
version: int = Field(ge=1)
description: str | None = None
roles: list[WorkflowRole]
phases: list[WorkflowPhase]
default_gates: list[str] = []
max_total_budget_usd: float | None = None
ROOT = Path(__file__).parent
CAPABILITIES = {
"spec_write",
"phase_planning",
"task_dag_planning",
"code_edit",
"test_first_development",
"code_review",
"evidence_check",
"command_execute",
"backtest_run",
"metric_extract",
"failure_mining",
"objective_eval",
"final_report_compose",
}
errors: list[str] = []
def validate_personas() -> int:
count = 0
for f in sorted((ROOT / "personas").glob("*.yaml")):
try:
data = yaml.safe_load(f.read_text())
p = Persona.model_validate(data)
unknown_caps = set(p.capabilities) - CAPABILITIES
if unknown_caps:
raise ValueError(f"unknown capabilities: {unknown_caps}")
print(f" ok personas/{f.name}")
count += 1
except (ValidationError, ValueError, yaml.YAMLError) as e:
errors.append(f"personas/{f.name}: {e}")
print(f" FAIL personas/{f.name}: {e}")
return count
def validate_workflows() -> int:
count = 0
for f in sorted((ROOT / "workflows").glob("*.yaml")):
try:
data = yaml.safe_load(f.read_text())
w = WorkflowTemplate.model_validate(data)
role_ids = {r.id for r in w.roles}
for ph in w.phases:
if ph.role not in role_ids:
raise ValueError(
f"phase '{ph.key}' references unknown role '{ph.role}'"
)
print(f" ok workflows/{f.name}")
count += 1
except (ValidationError, ValueError, yaml.YAMLError) as e:
errors.append(f"workflows/{f.name}: {e}")
print(f" FAIL workflows/{f.name}: {e}")
return count
def validate_artifact_schemas() -> int:
count = 0
for f in sorted((ROOT / "artifacts").rglob("*.json")):
try:
schema = json.loads(f.read_text())
jsonschema.Draft202012Validator.check_schema(schema)
if "$id" not in schema:
raise ValueError("missing $id")
print(f" ok artifacts/{f.relative_to(ROOT / 'artifacts')}")
count += 1
except (
jsonschema.exceptions.SchemaError,
ValueError,
json.JSONDecodeError,
) as e:
errors.append(f"artifacts/{f.relative_to(ROOT / 'artifacts')}: {e}")
print(f" FAIL artifacts/{f.relative_to(ROOT / 'artifacts')}: {e}")
return count
if __name__ == "__main__":
print("Personas:")
p_count = validate_personas()
print("\nWorkflows:")
w_count = validate_workflows()
print("\nArtifact schemas:")
a_count = validate_artifact_schemas()
print(f"\nTotal: personas={p_count}, workflows={w_count}, artifacts={a_count}")
if errors:
print(f"\nERRORS: {len(errors)}")
for e in errors:
print(f" - {e}")
sys.exit(1)
print("\nAll seeds validated.")