#!/usr/bin/env python3 """One-shot meta-tooling duration analyzer. Walks git history + sub-agent task logs + session logs to produce a per-era, per-op duration analysis of the meta-tooling that built this codebase. Emits a 10-section Markdown report + JSON source-of-truth. Strictly Meta-Tooling domain (per docs/guide_meta_boundary.md). No src/ changes; no GUI changes; no Application-domain impact. Usage: uv run python scripts/audit/analyze_meta_tooling_durations.py uv run python scripts/audit/analyze_meta_tooling_durations.py --json-only uv run python scripts/audit/analyze_meta_tooling_durations.py --output-dir uv run python scripts/audit/analyze_meta_tooling_durations.py --era-window-days 7 """ from __future__ import annotations import argparse import json import re import statistics import subprocess import sys from collections import Counter, defaultdict from dataclasses import dataclass, field from datetime import date, datetime, timedelta from pathlib import Path from typing import Final # 1-space indent throughout per conductor/code_styleguides/python.md §1 # --------------------------------------------------------------------------- # Dataclasses (per conductor/code_styleguides/data_oriented_design.md §8.5) # --------------------------------------------------------------------------- @dataclass(frozen=True, slots=True) class Evidence: """A single piece of evidence pointing to an era boundary.""" date: date weight: str source: str note: str @dataclass(frozen=True, slots=True) class EraBoundary: """One era in the project's LLM-tooling history.""" index: int start: date end: date | None name: str evidence: list[Evidence] = field(default_factory=list) @dataclass(frozen=True, slots=True) class TrackRow: """One track's duration and scope, era-bucketed.""" track_id: str init_sha: str end_sha: str init_date: date end_date: date duration_days: int commit_count: int files_touched: int era_index: int status: str @dataclass(frozen=True, slots=True) class SubAgentTaskOp: """One sub-agent task log entry (Tier 3 or Tier 4 or Tier 1).""" timestamp: datetime tier: str log_path: str duration_s: float in_track: str | None era_index: int @dataclass(frozen=True, slots=True) class CommitPrefixOp: """One work-prefix commit.""" sha: str date: date prefix: str scope: str message: str in_track: str | None era_index: int @dataclass(frozen=True, slots=True) class SessionOp: """One chat session (logs//).""" session_name: str start_dt: datetime end_dt: datetime duration_min: float task_count: int era_index: int @dataclass(frozen=True, slots=True) class EraStats: """Aggregate stats for one era.""" era: EraBoundary track_count: int track_duration_median: float track_duration_p25: float track_duration_p75: float track_commits_median: float track_files_median: float subagent_task_count: int subagent_task_duration_median: float commit_count: int commit_count_by_prefix: dict[str, int] session_count: int session_duration_median: float track_less_op_count: int # --------------------------------------------------------------------------- # Constants # --------------------------------------------------------------------------- _REPO_ROOT: Final[Path] = Path(__file__).resolve().parents[2] _TRACKS_DIR: Final[Path] = _REPO_ROOT / "conductor" / "tracks" _ARCHIVE_DIR: Final[Path] = _REPO_ROOT / "conductor" / "archive" _LOGS_AGENTS_DIR: Final[Path] = _REPO_ROOT / "logs" / "agents" _LOGS_DIR: Final[Path] = _REPO_ROOT / "logs" _DEFAULT_OUTPUT_DIR: Final[Path] = _REPO_ROOT / "docs" / "reports" _DEFAULT_ARTIFACT_DIR: Final[Path] = _REPO_ROOT / "tests" / "artifacts" _DEFAULT_ERA_WINDOW_DAYS: Final[int] = 7 _DEFAULT_DATE: Final[date] = date(2026, 7, 5) _WORK_PREFIX_RE: Final[re.Pattern[str]] = re.compile( r"^(?Pfeat|fix|refactor|perf|test)(?:\((?P[^)]*)\))?:\s" ) _SESSION_NAME_RE: Final[re.Pattern[str]] = re.compile( r"^(?P\d{8})_(?P