diff --git a/conductor/archive/meta_tooling_duration_analysis_20260705/metadata.json b/conductor/archive/meta_tooling_duration_analysis_20260705/metadata.json new file mode 100644 index 00000000..2de6cef6 --- /dev/null +++ b/conductor/archive/meta_tooling_duration_analysis_20260705/metadata.json @@ -0,0 +1,80 @@ +{ + "track_id": "meta_tooling_duration_analysis_20260705", + "name": "Meta-Tooling Duration Analysis", + "description": "Empirical duration analysis of the meta-tooling that built this codebase Feb 21 - Jul 5, 2026. Era-detection via multi-signal weighted-vote (config.toml + workflow.md + track-spec content + agent-log content), 3 op-layers (sub-agent task logs, work-prefix commits, session logs), 244 tracks bucketed by era. Output: 10-section Markdown report + JSON source-of-truth. No Application-domain changes.", + "type": "analysis", + "domain": "meta-tooling", + "owner": "Tier 1 Orchestrator (spec) + Tier 2 Tech Lead (execution)", + "priority": "informational", + "initialized": "2026-07-05", + "status": "spec_drafted", + "scope": { + "new_files": [ + "scripts/audit/analyze_meta_tooling_durations.py", + "tests/test_analyze_meta_tooling_durations.py", + "docs/reports/META_TOOLING_DURATION_ANALYSIS_2026-07-05.md", + "tests/artifacts/meta_tooling_stats_2026-07-05.json" + ], + "modified_files": [], + "deleted_files": [] + }, + "blocked_by": [], + "blocks": [], + "verification_criteria": [ + "V1: --help exits 0 with documented help text", + "V2: Default invocation produces 300+ line Markdown report + JSON artifact", + "V3: --json-only mode produces only JSON", + "V4: Unit tests pass with 80% line coverage on pure-function modules", + "V5: Markdown report contains all 10 sections in documented order", + "V6: JSON artifact contains all top-level schema keys", + "V7: Script runtime <120s on current repo state" + ], + "estimated_effort": { + "method": "scope (per conductor/workflow.md Tier 1 Track Initialization Rules). NO day estimates.", + "phase_1": "1 task: era detection algorithm + multi-signal voting", + "phase_2": "3 tasks: build track index, sub-agent task index, commit-prefix index, session index", + "phase_3": "1 task: statistician + 10-section renderer", + "phase_4": "1 task: writer + unit tests", + "total_files_touched": "2 new files (script + test) + 2 new artifact files (md + json)", + "total_sites": "scripts/audit/ + tests/ + docs/reports/ + tests/artifacts/" + }, + "risk_register": [ + { + "id": "R1", + "risk": "Era detection over/under-clusters boundaries", + "likelihood": "medium", + "impact": "medium", + "mitigation": "Tunable --era-window-days flag; §1 documents chosen window + evidence chain" + }, + { + "id": "R2", + "risk": "Sub-agent task log parsing breaks on malformed files", + "likelihood": "medium", + "impact": "low", + "mitigation": "FR10 + warning log + skip-and-continue" + }, + { + "id": "R3", + "risk": "Use-case prediction buckets too small (N<5) per era", + "likelihood": "medium", + "impact": "medium", + "mitigation": "§8 reports bucket sample sizes; warns when N<5" + }, + { + "id": "R4", + "risk": "User disagrees with auto-detected era boundaries", + "likelihood": "medium", + "impact": "low", + "mitigation": "Report §1 documents algorithm + evidence; CLI override flags available" + } + ], + "regressions_and_pre_existing_failures": [], + "pre_existing_failures_remaining": [], + "deferred_to_followup_tracks": [], + "convention_compliance": { + "python_type_promotion_mandate": "All dataclasses @dataclass(frozen=True, slots=True); no dict[str, Any]; no Any; no Optional[T] returns (uses Result[T] + NIL_T per error_handling.md)", + "file_naming_rule": "scripts/audit/ is the established namespace for audit scripts per AGENTS.md", + "no_src_new_files": "Zero new src/.py files", + "audit_script_policy": "Has --help, supports --json-only, deterministic output per workflow.md Audit Script Policy" + } +} \ No newline at end of file diff --git a/conductor/archive/meta_tooling_duration_analysis_20260705/plan.md b/conductor/archive/meta_tooling_duration_analysis_20260705/plan.md new file mode 100644 index 00000000..a9e18621 --- /dev/null +++ b/conductor/archive/meta_tooling_duration_analysis_20260705/plan.md @@ -0,0 +1,2037 @@ +# Meta-Tooling Duration Analysis Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Build `scripts/audit/analyze_meta_tooling_durations.py` — a one-shot audit that produces a 10-section Markdown report + JSON source-of-truth from git history + sub-agent logs + session logs, with era-detection via multi-signal weighted-vote. Zero Application-domain changes. + +**Architecture:** Single-file audit script (`scripts/audit/analyze_meta_tooling_durations.py`, ~700 lines) with 8 logical sections (era detector, 4 indexers, statistician, renderer, writer/CLI). Reuses primitives from `scripts.audit.generate_chronology` for the per-track walker + git plumbing. Pure-function modules are unit-tested in `tests/test_analyze_meta_tooling_durations.py`; git-process-spawning functions are not unit-tested (they are exercised by the integration run). + +**Tech Stack:** Python 3.11+ stdlib (`subprocess`, `pathlib`, `json`, `dataclasses`, `datetime`, `statistics`, `re`, `argparse`, `collections`). No new dependencies. Per `conductor/code_styleguides/python.md §1`: 1-space indentation, CRLF line endings. + +--- + +## File Structure + +| File | Responsibility | New/Modified | +|---|---|---| +| `scripts/audit/analyze_meta_tooling_durations.py` | Era detector + 4 indexers + statistician + 10-section renderer + CLI | NEW | +| `tests/test_analyze_meta_tooling_durations.py` | Unit tests for pure-function modules (era clustering, log parsers, statistician, section renderers) | NEW | +| `docs/reports/META_TOOLING_DURATION_ANALYSIS_2026-07-05.md` | The extensive report (produced by the script) | NEW (artifact) | +| `tests/artifacts/meta_tooling_stats_2026-07-05.json` | The raw data dump (produced by the script) | NEW (artifact) | + +**Touched by Tier 3 worker for execution:** `scripts/audit/analyze_meta_tooling_durations.py`, `tests/test_analyze_meta_tooling_durations.py`. The 2 artifacts are produced by `uv run python scripts/audit/analyze_meta_tooling_durations.py` at the end of Phase 4. + +--- + +## Phase 1: Era Detection + +### Task 1: Era detector with multi-signal weighted-vote + +**Files:** +- Create: `scripts/audit/analyze_meta_tooling_durations.py` +- Create: `tests/test_analyze_meta_tooling_durations.py` + +- [ ] **Step 1.1: Write the failing test for era clustering** + +Open `tests/test_analyze_meta_tooling_durations.py` and add: + +```python +# 1-space indent per conductor/code_styleguides/python.md §1 +from datetime import date +from scripts.audit.analyze_meta_tooling_durations import ( + EraBoundary, + cluster_era_candidates, +) + + +def test_cluster_era_candidates_within_7_days_merges_into_one(): + """Candidates within the 7-day window collapse to a single era.""" + candidates: list[tuple[date, str, str]] = [ + (date(2026, 2, 21), "high", "config.toml@9a23941e: gemini-3-flash-preview introduced"), + (date(2026, 2, 24), "high", "config.toml@9a23941e: gemini-3.1-pro-preview introduced"), + (date(2026, 2, 26), "medium", "logs/agents/mma_tier3-worker_task_20260226_123549.log first tier3 task"), + ] + result: list[EraBoundary] = cluster_era_candidates(candidates, window_days=7) + assert len(result) == 1 + assert result[0].start == date(2026, 2, 21) + assert result[0].end is None # last era's end is None until next era + assert len(result[0].evidence) == 3 + + +def test_cluster_era_candidates_15_days_apart_produces_two_eras(): + """Candidates 15 days apart stay as 2 eras.""" + candidates: list[tuple[date, str, str]] = [ + (date(2026, 2, 21), "high", "A"), + (date(2026, 3, 21), "high", "B"), + ] + result: list[EraBoundary] = cluster_era_candidates(candidates, window_days=7) + assert len(result) == 2 + assert result[0].start == date(2026, 2, 21) + assert result[1].start == date(2026, 3, 21) + + +def test_cluster_era_candidates_empty_input_returns_empty_list(): + result: list[EraBoundary] = cluster_era_candidates([], window_days=7) + assert result == [] +``` + +- [ ] **Step 1.2: Run the test to verify it fails** + +Run: `uv run pytest tests/test_analyze_meta_tooling_durations.py -v` +Expected: FAIL with `ModuleNotFoundError: No module named 'scripts.audit.analyze_meta_tooling_durations'` or `ImportError: cannot import name 'EraBoundary'`. + +- [ ] **Step 1.3: Create the script header + dataclass definitions** + +Create `scripts/audit/analyze_meta_tooling_durations.py`: + +```python + #!/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. + + Attributes: + date: The candidate date (when the evidence suggests a transition). + weight: "high" | "medium" | "low" -- higher = stronger signal. + source: The file:line or commit reference (e.g., "config.toml@9a23941e"). + note: Human-readable explanation of why this evidence exists. + """ + + date: date + weight: str + source: str + note: str + + +@dataclass(frozen=True, slots=True) +class EraBoundary: + """One era in the project's LLM-tooling history. + + Attributes: + index: 0-based era index (0 = earliest). + start: First day of this era. + end: Last day of this era, or None if this is the current (open) era. + name: Short label like "early" | "mid" | "late" | "current". + evidence: All evidence that contributed to this era's start date. + """ + + 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. + + Attributes: + track_id: The folder name (e.g., "code_path_audit_20260607"). + init_sha: First commit touching the track folder. + end_sha: Last commit touching the track folder. + init_date: ISO date of init_sha. + end_date: ISO date of end_sha. + duration_days: end_date - init_date in calendar days. + commit_count: Number of work-prefix commits (feat|fix|refactor|perf|test). + files_touched: Unique file paths touched across all track commits. + era_index: Which era this track falls into. + status: "Completed" | "Active" | "In Progress" | "Abandoned" | "Superseded". + """ + + 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). + + Attributes: + timestamp: When the task started (ISO datetime). + tier: "tier1" | "tier3-worker" | "tier4-qa". + log_path: Path to the source log file. + duration_s: Seconds between task start and result-returned timestamp. + in_track: Track ID if this task's timestamp falls in a track's window, + else None (track-less op). + era_index: Which era this op falls into. + """ + + 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. + + Attributes: + sha: Commit SHA. + date: Commit date. + prefix: "feat" | "fix" | "refactor" | "perf" | "test" (commit-type prefix). + scope: Optional scope from "type(scope):" (e.g., "gui_2"), else "". + message: Full commit message subject. + in_track: Track ID or None. + era_index: Era index. + """ + + 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//). + + Attributes: + session_name: Folder name (e.g., "20260312_190926_gencpp_sloppy"). + start_dt: Earliest mtime in the session dir. + end_dt: Latest mtime in the session dir. + duration_min: (end_dt - start_dt) in minutes. + task_count: Number of sub-agent task logs created during this session. + era_index: Era index. + """ + + 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. + + Attributes: + era: The EraBoundary. + track_count: Number of tracks in this era. + track_duration_median: Median duration_days across tracks in this era. + track_duration_p25: 25th percentile. + track_duration_p75: 75th percentile. + track_commits_median: Median commits per track. + track_files_median: Median files_touched per track. + subagent_task_count: Total Tier 3/4/1 tasks in this era. + subagent_task_duration_median: Median duration_s. + commit_count: Total work-prefix commits in this era. + commit_count_by_prefix: Dict of prefix -> count. + session_count: Total sessions in this era. + session_duration_median: Median session duration_min. + track_less_op_count: Number of ops not attributed to any track. + """ + + 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) + +# Commit-prefix detection per conductor/workflow.md §"Commit Guidelines" +_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