feat(audit): meta-tooling duration analyzer (8 modules, 10-section renderer)
- scripts/audit/analyze_meta_tooling_durations.py (~700 lines) - tests/test_analyze_meta_tooling_durations.py (20 unit tests, all pass) - Era detector with multi-signal weighted-vote clustering - Track indexer reusing git-history-evidence primitives - 3 op-layer indexers (sub-agent tasks, work-prefix commits, session logs) - Statistician with per-era aggregations - 10 Markdown section renderers - CLI: --json-only, --output-dir, --era-window-days, --date Strictly Meta-Tooling domain (docs/guide_meta_boundary.md). Zero src/ changes; zero GUI changes; zero Application-domain impact. Part of conductor/tracks/meta_tooling_duration_analysis_20260705/.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,227 @@
|
||||
# 1-space indent per conductor/code_styleguides/python.md §1
|
||||
from datetime import date, datetime, timedelta
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from scripts.audit.analyze_meta_tooling_durations import (
|
||||
CommitPrefixOp,
|
||||
EraBoundary,
|
||||
EraStats,
|
||||
Evidence,
|
||||
SessionOp,
|
||||
SubAgentTaskOp,
|
||||
TrackRow,
|
||||
_find_track_for_timestamp,
|
||||
_git_files_touched,
|
||||
_parse_iso_date,
|
||||
_percentile,
|
||||
_safe_median,
|
||||
assign_era_for_date,
|
||||
assign_era_name,
|
||||
build_commit_prefix_index,
|
||||
build_session_index,
|
||||
build_subagent_task_index,
|
||||
build_track_index,
|
||||
cluster_era_candidates,
|
||||
compute_era_stats,
|
||||
parse_session_name,
|
||||
parse_subagent_task_log,
|
||||
render_appendix,
|
||||
render_era_timeline,
|
||||
render_limitations,
|
||||
render_methodology,
|
||||
render_op_stats,
|
||||
render_subagent_stats,
|
||||
render_track_stats,
|
||||
render_trackless_ops,
|
||||
render_trends,
|
||||
render_use_case_predictions,
|
||||
)
|
||||
|
||||
|
||||
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
|
||||
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 == []
|
||||
|
||||
|
||||
def test_assign_era_name_first_is_early_last_is_current():
|
||||
eras_n = 3
|
||||
assert assign_era_name(0, eras_n) == "early"
|
||||
assert assign_era_name(1, eras_n) == "mid"
|
||||
assert assign_era_name(eras_n - 1, eras_n) == "current"
|
||||
|
||||
|
||||
def test_assign_era_for_date_returns_correct_index():
|
||||
eras: list[EraBoundary] = [
|
||||
EraBoundary(index=0, start=date(2026, 2, 21), end=date(2026, 3, 11), name="early", evidence=[]),
|
||||
EraBoundary(index=1, start=date(2026, 3, 12), end=date(2026, 5, 8), name="mid", evidence=[]),
|
||||
EraBoundary(index=2, start=date(2026, 5, 9), end=None, name="current", evidence=[]),
|
||||
]
|
||||
assert assign_era_for_date(date(2026, 2, 25), eras) == 0
|
||||
assert assign_era_for_date(date(2026, 4, 1), eras) == 1
|
||||
assert assign_era_for_date(date(2026, 7, 5), eras) == 2
|
||||
|
||||
|
||||
def test_assign_era_for_date_before_first_era_returns_zero():
|
||||
eras: list[EraBoundary] = [
|
||||
EraBoundary(index=0, start=date(2026, 2, 21), end=None, name="early", evidence=[]),
|
||||
]
|
||||
assert assign_era_for_date(date(2025, 1, 1), eras) == 0
|
||||
|
||||
|
||||
def test_parse_iso_date_valid_and_invalid():
|
||||
assert _parse_iso_date("2026-07-05") == date(2026, 7, 5)
|
||||
assert _parse_iso_date("garbage") is None
|
||||
|
||||
|
||||
def test_percentile_basic():
|
||||
"""Linear-interpolation percentile (numpy default)."""
|
||||
assert _percentile([1.0, 2.0, 3.0, 4.0, 5.0], 50) == 3.0
|
||||
assert _percentile([], 50) == 0.0
|
||||
|
||||
|
||||
def test_safe_median_basic():
|
||||
assert _safe_median([1.0, 2.0, 3.0]) == 2.0
|
||||
assert _safe_median([]) == 0.0
|
||||
|
||||
|
||||
def test_parse_session_name_full_format():
|
||||
d, suffix = parse_session_name("20260312_190926_gencpp_sloppy")
|
||||
assert d is not None
|
||||
assert d.year == 2026 and d.month == 3 and d.day == 12
|
||||
assert suffix == "gencpp_sloppy"
|
||||
|
||||
|
||||
def test_parse_session_name_no_suffix():
|
||||
d, suffix = parse_session_name("20260312_191301")
|
||||
assert d is not None
|
||||
assert d.year == 2026 and d.month == 3 and d.day == 12
|
||||
assert suffix == ""
|
||||
|
||||
|
||||
def test_parse_session_name_invalid_returns_none():
|
||||
assert parse_session_name("not-a-session-name") == (None, "")
|
||||
|
||||
|
||||
def test_parse_subagent_task_log_extracts_role_and_timestamp(tmp_path: Path):
|
||||
log: Path = tmp_path / "mma_tier3-worker_task_20260611_001715.log"
|
||||
log.write_text(
|
||||
"==================================================\n"
|
||||
"ROLE: tier3-worker\n"
|
||||
"TIMESTAMP: 2026-06-11 00:17:15\n"
|
||||
"--------------------------------------------------\n"
|
||||
"FULL PROMPT:\n"
|
||||
"STRICT SYSTEM DIRECTIVE: ...\n"
|
||||
"TASK: Write a unit test.\n"
|
||||
"--------------------------------------------------\n"
|
||||
"RESULT:\n"
|
||||
"Implemented.\n"
|
||||
"==================================================\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
result: tuple[str, datetime] | None = parse_subagent_task_log(log)
|
||||
assert result is not None
|
||||
role, ts = result
|
||||
assert role == "tier3-worker"
|
||||
assert ts == datetime(2026, 6, 11, 0, 17, 15)
|
||||
|
||||
|
||||
def test_parse_subagent_task_log_returns_none_for_malformed(tmp_path: Path):
|
||||
log: Path = tmp_path / "mma_tier3-worker_task_20260611_001715.log"
|
||||
log.write_text("garbage data with no ROLE or TIMESTAMP\n", encoding="utf-8")
|
||||
assert parse_subagent_task_log(log) is None
|
||||
|
||||
|
||||
def test_find_track_for_timestamp_in_window():
|
||||
tracks: list[TrackRow] = [
|
||||
TrackRow(track_id="t1", init_sha="a", end_sha="b", init_date=date(2026, 3, 1),
|
||||
end_date=date(2026, 3, 10), duration_days=9, commit_count=5,
|
||||
files_touched=3, era_index=0, status="Completed"),
|
||||
TrackRow(track_id="t2", init_sha="c", end_sha="d", init_date=date(2026, 4, 1),
|
||||
end_date=date(2026, 4, 5), duration_days=4, commit_count=2,
|
||||
files_touched=7, era_index=0, status="Completed"),
|
||||
]
|
||||
assert _find_track_for_timestamp(datetime(2026, 3, 5), tracks) == "t1"
|
||||
assert _find_track_for_timestamp(datetime(2026, 4, 3), tracks) == "t2"
|
||||
assert _find_track_for_timestamp(datetime(2026, 5, 1), tracks) is None
|
||||
|
||||
|
||||
def test_compute_era_stats_aggregates_track_durations():
|
||||
eras: list[EraBoundary] = [
|
||||
EraBoundary(index=0, start=date(2026, 3, 1), end=None, name="early", evidence=[]),
|
||||
]
|
||||
tracks: list[TrackRow] = [
|
||||
TrackRow(track_id="t1", init_sha="a", end_sha="b", init_date=date(2026, 3, 1),
|
||||
end_date=date(2026, 3, 5), duration_days=4, commit_count=10,
|
||||
files_touched=5, era_index=0, status="Completed"),
|
||||
TrackRow(track_id="t2", init_sha="c", end_sha="d", init_date=date(2026, 3, 10),
|
||||
end_date=date(2026, 3, 30), duration_days=20, commit_count=3,
|
||||
files_touched=12, era_index=0, status="Completed"),
|
||||
]
|
||||
stats = compute_era_stats(eras, tracks, subagent_ops=[], commit_ops=[], sessions=[])
|
||||
assert len(stats) == 1
|
||||
s = stats[0]
|
||||
assert s.track_count == 2
|
||||
assert s.track_duration_median == 12.0
|
||||
assert s.track_commits_median == 6.5
|
||||
assert s.track_files_median == 8.5
|
||||
|
||||
|
||||
def test_compute_era_stats_empty_input_returns_era_with_zeros():
|
||||
eras: list[EraBoundary] = [
|
||||
EraBoundary(index=0, start=date(2026, 3, 1), end=None, name="early", evidence=[]),
|
||||
]
|
||||
stats = compute_era_stats(eras, [], [], [], [])
|
||||
assert len(stats) == 1
|
||||
assert stats[0].track_count == 0
|
||||
assert stats[0].track_duration_median == 0.0
|
||||
|
||||
|
||||
def test_render_methodology_returns_string_with_section_header():
|
||||
out: str = render_methodology(era_window_days=7)
|
||||
assert out.startswith("# Meta-Tooling Duration Analysis")
|
||||
assert "## 1. Methodology" in out
|
||||
assert "7-day window" in out
|
||||
|
||||
|
||||
def test_render_use_case_predictions_handles_empty_tracks():
|
||||
out: str = render_use_case_predictions(tracks=[])
|
||||
assert "Use-Case Predictions" in out
|
||||
|
||||
|
||||
def test_build_commit_prefix_index_smoke_returns_nonempty():
|
||||
"""Integration-ish test: run on real repo, expect 1000+ commits."""
|
||||
eras: list[EraBoundary] = [
|
||||
EraBoundary(index=0, start=date(2026, 2, 21), end=None, name="early", evidence=[]),
|
||||
]
|
||||
ops: list[CommitPrefixOp] = build_commit_prefix_index(eras, tracks=[])
|
||||
assert len(ops) > 100, f"expected 100+ ops, got {len(ops)}"
|
||||
for op in ops[:5]:
|
||||
assert op.prefix in ("feat", "fix", "refactor", "perf", "test")
|
||||
Reference in New Issue
Block a user