From 27e755815f60bd2e0fb4003f37ff936ca97c8515 Mon Sep 17 00:00:00 2001 From: tegwick Date: Fri, 20 Mar 2026 01:29:27 +0100 Subject: [PATCH] =?UTF-8?q?perf(doi):=2013x=20speedup=20for=20/repos/doi/s?= =?UTF-8?q?ummary=20(108s=20=E2=86=92=20~6s)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two fixes: 1. skip_consistency=True in summary mode — omits C7/C13 subprocess calls (consistency_check.py) which were the main bottleneck (32 spawns for 16 repos). Full check still available per-repo via GET /repos/{slug}/doi. 2. asyncio.gather — all repos evaluated in parallel instead of sequentially. Also: rename Repositories page title from "Repos" to "Repositories". Co-Authored-By: Claude Sonnet 4.6 (1M context) --- api/doi_engine.py | 55 ++++++++++++++++++++++++------------------ api/routers/repos.py | 14 +++++++---- dashboard/src/repos.md | 4 +-- 3 files changed, 42 insertions(+), 31 deletions(-) diff --git a/api/doi_engine.py b/api/doi_engine.py index 32bbdf2..c0d4881 100644 --- a/api/doi_engine.py +++ b/api/doi_engine.py @@ -110,7 +110,11 @@ async def _run_consistency(repo_slug: str, api_base: str) -> tuple[int, int, int return fail, warn, info -async def evaluate(repo: dict, api_base: str = "http://127.0.0.1:8000") -> DoIReport: +async def evaluate( + repo: dict, + api_base: str = "http://127.0.0.1:8000", + skip_consistency: bool = False, +) -> DoIReport: slug = repo.get("slug", "unknown") results: list[CriterionResult] = [] @@ -171,14 +175,17 @@ async def evaluate(repo: dict, api_base: str = "http://127.0.0.1:8000") -> DoIRe _r("C6", "CLAUDE.md present", "standard", "fail", "CLAUDE.md not found at repo root") # C7: workplan convention — consistency check 0 FAIL - try: - fail, warn, _ = await _run_consistency(slug, api_base) - if fail == 0: - _r("C7", "Workplan convention (0 FAIL)", "standard", "pass", f"consistency: {fail} fail / {warn} warn") - else: - _r("C7", "Workplan convention (0 FAIL)", "standard", "fail", f"consistency: {fail} fail / {warn} warn") - except Exception as e: - _r("C7", "Workplan convention (0 FAIL)", "standard", "skip", f"Could not run consistency check: {e}") + if skip_consistency: + _r("C7", "Workplan convention (0 FAIL)", "standard", "skip", "Not checked in summary mode — use /repos/{slug}/doi for full check") + else: + try: + fail, warn, _ = await _run_consistency(slug, api_base) + if fail == 0: + _r("C7", "Workplan convention (0 FAIL)", "standard", "pass", f"consistency: {fail} fail / {warn} warn") + else: + _r("C7", "Workplan convention (0 FAIL)", "standard", "fail", f"consistency: {fail} fail / {warn} warn") + except Exception as e: + _r("C7", "Workplan convention (0 FAIL)", "standard", "skip", f"Could not run consistency check: {e}") # C8: SBOM ingested last_sbom = repo.get("last_sbom_at") @@ -251,21 +258,21 @@ async def evaluate(repo: dict, api_base: str = "http://127.0.0.1:8000") -> DoIRe "CLAUDE.md has no kaizen agent reference") # C13: consistency check clean (0 FAIL, 0 WARN — C-12 exempt) - try: - fail, warn, _ = await _run_consistency(slug, api_base) - # C-12 warns are legacy DB-only tasks — deduct them from warn count - c12_count = await _get(api_base, "/tasks/", {"workstream_id": None}) or [] - # Use raw counts from the script output - if fail == 0 and warn == 0: - _r("C13", "Consistency check clean (0 FAIL/WARN)", "full", "pass") - elif fail == 0 and warn > 0: - _r("C13", "Consistency check clean (0 FAIL/WARN)", "full", "warn", - f"{warn} warn(s) — C-12 legacy tasks may be exempt") - else: - _r("C13", "Consistency check clean (0 FAIL/WARN)", "full", "fail", - f"{fail} fail(s), {warn} warn(s)") - except Exception as e: - _r("C13", "Consistency check clean (0 FAIL/WARN)", "full", "skip", f"Could not run: {e}") + if skip_consistency: + _r("C13", "Consistency check clean (0 FAIL/WARN)", "full", "skip", "Not checked in summary mode — use /repos/{slug}/doi for full check") + else: + try: + fail, warn, _ = await _run_consistency(slug, api_base) + if fail == 0 and warn == 0: + _r("C13", "Consistency check clean (0 FAIL/WARN)", "full", "pass") + elif fail == 0 and warn > 0: + _r("C13", "Consistency check clean (0 FAIL/WARN)", "full", "warn", + f"{warn} warn(s) — C-12 legacy tasks may be exempt") + else: + _r("C13", "Consistency check clean (0 FAIL/WARN)", "full", "fail", + f"{fail} fail(s), {warn} warn(s)") + except Exception as e: + _r("C13", "Consistency check clean (0 FAIL/WARN)", "full", "skip", f"Could not run: {e}") # C14: host paths registered host_paths = repo.get("host_paths") or {} diff --git a/api/routers/repos.py b/api/routers/repos.py index 3c9a601..1e89102 100644 --- a/api/routers/repos.py +++ b/api/routers/repos.py @@ -1,3 +1,4 @@ +import asyncio import uuid from fastapi import APIRouter, Depends, HTTPException, status @@ -80,8 +81,7 @@ async def doi_summary(session: AsyncSession = Depends(get_session)) -> list[DoIS domain_result = await session.execute(select(Domain)) domain_map = {d.id: d.slug for d in domain_result.scalars().all()} - entries: list[DoISummaryEntry] = [] - for repo in repos: + async def _check_one(repo: ManagedRepo) -> DoISummaryEntry: repo_dict = { "slug": repo.slug, "domain_slug": domain_map.get(repo.domain_id), @@ -90,8 +90,10 @@ async def doi_summary(session: AsyncSession = Depends(get_session)) -> list[DoIS "host_paths": repo.host_paths or {}, "last_sbom_at": str(repo.last_sbom_at) if repo.last_sbom_at else None, } - report = await _doi_evaluate(repo_dict) - entries.append(DoISummaryEntry( + # skip_consistency=True: omits C7/C13 subprocess calls for speed. + # The full check is available via GET /repos/{slug}/doi. + report = await _doi_evaluate(repo_dict, skip_consistency=True) + return DoISummaryEntry( repo_slug=repo.slug, domain_slug=domain_map.get(repo.domain_id), tier=report.tier, @@ -99,7 +101,9 @@ async def doi_summary(session: AsyncSession = Depends(get_session)) -> list[DoIS standard_pass=report.standard_pass, full_pass=report.full_pass, checked_at=report.checked_at, - )) + ) + + entries: list[DoISummaryEntry] = list(await asyncio.gather(*[_check_one(r) for r in repos])) tier_order = {"none": 0, "core": 1, "standard": 2, "full": 3} entries.sort(key=lambda e: tier_order.get(e.tier, 0)) diff --git a/dashboard/src/repos.md b/dashboard/src/repos.md index 405f60c..d8f35ff 100644 --- a/dashboard/src/repos.md +++ b/dashboard/src/repos.md @@ -1,5 +1,5 @@ --- -title: Repos +title: Repositories --- ```js @@ -115,7 +115,7 @@ const doiFullCount = repoRows.filter(r => r._doiTier === "full").length; const doiNoneCount = repoRows.filter(r => r._doiTier === "none").length; ``` -# Repos +# Repositories ```js import {withDocHelp} from "./components/doc-overlay.js";