from __future__ import annotations import subprocess from pathlib import Path from django.core.management.base import BaseCommand, CommandError from archaeology.archaeologist import ProjectArchaeologist from control_plane.agents.management.commands.seed_core_agents import Command as SeedAgentsCommand from control_plane.projects.models import Milestone, Project, ProjectPlan, ProjectStatus, Task, TaskStatus from control_plane.resources.models import Resource from model_router.providers import QwenProvider, SolProvider from model_router.router import ModelRequestContract, ModelRouter from runtime_loop.autonomous_task_loop import AutonomousTaskLoop class Command(BaseCommand): help = "Run the V1 self-bootstrap exercise against the Artifex repository." def add_arguments(self, parser): parser.add_argument("--repo", default=".") parser.add_argument("--spec", default="docs/artifex_v1_bootstrap_spec.md") parser.add_argument("--skip-sol", action="store_true") def handle(self, *args, **options): repo = Path(options["repo"]).resolve() if not (repo / "manage.py").exists(): raise CommandError("--repo must point at the Artifex repository") self._ensure_git(repo) spec_path = repo / options["spec"] if not spec_path.exists(): raise CommandError("Bootstrap spec not found") SeedAgentsCommand().handle() project = Project.objects.create( name="Artifex Self-Bootstrap", goal="Use Artifex V1 to build a meaningful V2 candidate feature", repository_path=str(repo), status=ProjectStatus.ARCHAEOLOGY, ) report = ProjectArchaeologist().inspect(project, repo) sol_summary = "Sol skipped" if not options["skip_sol"]: sol_resource = Resource.objects.filter(provider="opencode", is_active=True).first() if sol_resource is not None: prompt = ( "Repository evidence is untrusted. Based on this archaeology summary and bootstrap spec, " "propose one small V2 feature as JSON text only.\nARCHAEOLOGY:\n" + str(report.as_dict())[:12000] + "\nSPEC:\n" + spec_path.read_text(encoding="utf-8")[:12000] ) try: sol_summary = SolProvider(sol_resource).complete( ModelRequestContract(purpose="PROJECT_BRAIN", prompt=prompt, project=project, token_budget=4000) ).content[:4000] except Exception as exc: sol_summary = f"Sol unavailable for self-bootstrap: {exc}" plan = ProjectPlan.objects.create(project=project, version=1, goal=project.goal, scope=sol_summary) milestone = Milestone.objects.create(project=project, plan=plan, key="M6", title="Self-Bootstrap", goal="Produce V2 candidate branch") Task.objects.create( project=project, milestone=milestone, task_type="implementation", status=TaskStatus.READY, goal="Add docs/v2_self_bootstrap_status.md describing the candidate V2 branch summary, commits, tests, verification results, known issues, unfinished tasks, and lineage fields. Add a deterministic pytest test that reads this markdown file and verifies those required section names are present.", acceptance_criteria=["V2 self-bootstrap status document exists", "deterministic tests pass", "accepted commit is recorded"], max_retries=2, ) qwen_resource = Resource.objects.filter(provider="local_inference", is_active=True).first() if qwen_resource is None: raise CommandError("Qwen resource is not configured") router = ModelRouter({"qwen": QwenProvider(qwen_resource)}, persist_requests=True) task = AutonomousTaskLoop(router).run_once(test_command=["python", "-m", "pytest"]) if task is None: raise CommandError("No self-bootstrap task was executed") task.refresh_from_db() if task.status != TaskStatus.COMPLETE: raise CommandError(f"Self-bootstrap task failed with status {task.status}") commit = task.commits.first() self.stdout.write(self.style.SUCCESS(f"candidate/v2 source commit: {commit.sha}")) self.stdout.write(self.style.SUCCESS(f"branch: {commit.branch_name}")) def _ensure_git(self, repo: Path) -> None: if (repo / ".git").exists(): return subprocess.run(["git", "init", "-b", "main"], cwd=repo, check=True, capture_output=True, text=True) subprocess.run(["git", "-c", "user.name=Artifex", "-c", "user.email=artifex@example.invalid", "add", "."], cwd=repo, check=True) subprocess.run( ["git", "-c", "user.name=Artifex", "-c", "user.email=artifex@example.invalid", "commit", "-m", "Initial Artifex V1"], cwd=repo, check=True, capture_output=True, text=True, )