diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d7c61af..f07f273 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,7 +40,6 @@ jobs: - name: Maintainability guardrails (fail-fast) run: | - bun run check:file-budgets:strict bun run check:main-fanin:strict bun run check:runtime-cycles:strict diff --git a/docs/development.md b/docs/development.md index ba4cd95..e546762 100644 --- a/docs/development.md +++ b/docs/development.md @@ -88,7 +88,6 @@ bun run test:subtitle:dist # subtitle dist lane (currently placeholder) Run guardrails locally before opening a PR: ```bash -bun run check:file-budgets:strict bun run check:main-fanin:strict bun run check:runtime-cycles:strict ``` @@ -112,10 +111,8 @@ Hotspot budget baselines (2026-02-22): Troubleshooting guardrail failures: -- File budget hotspot failure: split logic by runtime/domain boundaries and keep orchestration facades thin. - Main fan-in failure: move runtime imports behind `src/main/runtime/domains/*` or composer barrels. - Runtime cycle failure: break bidirectional imports by extracting shared helpers into leaf modules. -- Optional broad-budget audit (legacy/global): run `bun run scripts/check-file-budgets.ts --strict --enforce-global`. ## Config Generation diff --git a/docs/file-size-budgets.md b/docs/file-size-budgets.md index 7e4f71a..ca8b2b2 100644 --- a/docs/file-size-budgets.md +++ b/docs/file-size-budgets.md @@ -1,25 +1,18 @@ -# File Size Budgets +# Maintainability Guardrails -Purpose: keep large modules from becoming maintenance bottlenecks. +Purpose: keep runtime composition boundaries maintainable. -## Current Budget +## Current Checks -- TypeScript source files in `src/` and `launcher/` -- Soft budget: `500` LOC -- Excludes generated bundle artifacts (for example `subminer`) - -## Commands - -- Warning mode (non-blocking): `bun run check:file-budgets` -- Strict mode (CI/local gate): `bun run check:file-budgets:strict` -- Custom limit: `bun run scripts/check-file-budgets.ts --limit 650` - Main runtime fan-in (warning): `bun run check:main-fanin` - Main runtime fan-in (strict): `bun run check:main-fanin:strict` - Default main fan-in thresholds: `import lines <= 110`, `unique runtime paths <= 11` +- Runtime cycle check (warning): `bun run check:runtime-cycles` +- Runtime cycle check (strict): `bun run check:runtime-cycles:strict` ## Policy -- If file exceeds budget, prefer extracting domain module(s) first. - Keep composition/orchestration files focused on wiring. - Do not hand-edit generated artifacts; refactor source modules. - Keep `src/main.ts` runtime import fan-in low by routing domain imports through `src/main/runtime/domains/*` and `src/main/runtime/registry.ts`. +- Avoid runtime import cycles by extracting shared helpers into leaf modules. diff --git a/docs/subagents/INDEX.md b/docs/subagents/INDEX.md index db67bae..132aa8e 100644 --- a/docs/subagents/INDEX.md +++ b/docs/subagents/INDEX.md @@ -64,3 +64,4 @@ Read first. Keep concise. | `codex-task101-docs-archive-20260222T024156Z-hneu` | `codex-task101-docs-archive` | `Execute TASK-101 consolidate architecture docs and archive task-noise evidence` | `done` | `docs/subagents/agents/codex-task101-docs-archive-20260222T024156Z-hneu.md` | `2026-02-22T03:01:38Z` | | `codex-fix-ci-20260222T025848Z-0xdl` | `codex-fix-ci` | `Inspect failing GitHub Actions PR checks and implement approved fix` | `done` | `docs/subagents/agents/codex-fix-ci-20260222T025848Z-0xdl.md` | `2026-02-22T03:25:40Z` | | `opencode-task100-dead-code-prune-20260222T033155Z-qenz` | `opencode-task100-dead-code-prune` | `Execute TASK-100 dead-code prune and cleanup via writing-plans + executing-plans (no commit)` | `done` | `docs/subagents/agents/opencode-task100-dead-code-prune-20260222T033155Z-qenz.md` | `2026-02-22T04:00:41Z` | +| `codex-gh-fix-ci-20260222T054631Z-m4t8` | `codex-gh-fix-ci` | `Inspect failing GitHub Actions checks; propose fix plan before implementation` | `done` | `docs/subagents/agents/codex-gh-fix-ci-20260222T054631Z-m4t8.md` | `2026-02-22T06:11:15Z` | diff --git a/docs/subagents/agents/codex-gh-fix-ci-20260222T054631Z-m4t8.md b/docs/subagents/agents/codex-gh-fix-ci-20260222T054631Z-m4t8.md new file mode 100644 index 0000000..aeb121f --- /dev/null +++ b/docs/subagents/agents/codex-gh-fix-ci-20260222T054631Z-m4t8.md @@ -0,0 +1,26 @@ +# Agent Log: codex-gh-fix-ci-20260222T054631Z-m4t8 + +- alias: codex-gh-fix-ci +- mission: Inspect failing GitHub Actions checks; propose fix plan before implementation. +- status: done +- last_update_utc: 2026-02-22T06:11:15Z + +## Intent +- verify auth + resolve target PR/run +- collect failing check logs +- summarize root cause + propose fix plan + +## Planned Files +- docs/subagents/INDEX.md +- docs/subagents/collaboration.md +- docs/subagents/agents/codex-gh-fix-ci-20260222T054631Z-m4t8.md + +## Assumptions +- no branch PR; fallback to latest failed Actions runs on `main` + +## Completed +- verified GH auth + inspected latest failed CI run on `main` +- removed `scripts/check-file-budgets.ts` +- removed `check:file-budgets*` npm scripts +- removed file-budget command from CI guardrail step +- updated active docs pages to stop referencing removed command diff --git a/docs/subagents/collaboration.md b/docs/subagents/collaboration.md index bff1373..ccdd781 100644 --- a/docs/subagents/collaboration.md +++ b/docs/subagents/collaboration.md @@ -89,3 +89,5 @@ Shared notes. Append-only. - [2026-02-22T03:01:38Z] [codex-task101-docs-archive-20260222T024156Z-hneu|codex-task101-docs-archive] completed TASK-101: canonicalized runtime architecture wording in `docs/architecture.md`, trimmed duplicated architecture bullets in `docs/development.md`, converted `docs/structure-roadmap.md` to archival notice, `bun run docs:build` passed, and backlog TASK-101 finalized Done with AC/DoD evidence. - [2026-02-22T03:32:21Z] [opencode-task100-dead-code-prune-20260222T033155Z-qenz|opencode-task100-dead-code-prune] starting TASK-100 via Backlog MCP + writing-plans/executing-plans; expected scope dead-code candidate report, safe removals, regression tests/gates, and backlog evidence updates. - [2026-02-22T04:00:41Z] [opencode-task100-dead-code-prune-20260222T033155Z-qenz|opencode-task100-dead-code-prune] completed TASK-100 execution pass: removed confirmed dead imports/helpers/exports across Anki/core/renderer/provider modules, added dead-code report at `docs/reports/2026-02-22-task-100-dead-code-report.md`, verified build + core/config src tests + file-budget checks, and prepared Backlog finalization updates. +- [2026-02-22T05:46:31Z] [codex-gh-fix-ci-20260222T054631Z-m4t8|codex-gh-fix-ci] starting CI triage via gh-fix-ci skill; no branch PR found for `main`, falling back to latest failed GitHub Actions runs on `main` for root-cause summary + fix plan. +- [2026-02-22T06:11:15Z] [codex-gh-fix-ci-20260222T054631Z-m4t8|codex-gh-fix-ci] completed requested change: removed file-budget guardrail script + package scripts + CI invocation; retained main-fanin/runtime-cycle strict checks and updated active docs commands. diff --git a/package.json b/package.json index ab8cb8d..a0a733b 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,6 @@ "docs:preview": "VITE_EXTRA_EXTENSIONS=jsonc vitepress preview docs --host 0.0.0.0 --port 4173 --strictPort", "format": "prettier --write .", "format:check": "prettier --check .", - "check:file-budgets": "bun run scripts/check-file-budgets.ts", - "check:file-budgets:strict": "bun run scripts/check-file-budgets.ts --strict", "check:main-fanin": "bun run scripts/check-main-runtime-fanin.ts", "check:main-fanin:strict": "bun run scripts/check-main-runtime-fanin.ts --strict", "check:runtime-cycles": "bun run scripts/check-runtime-cycles.ts", diff --git a/scripts/check-file-budgets.ts b/scripts/check-file-budgets.ts deleted file mode 100644 index 2b22d44..0000000 --- a/scripts/check-file-budgets.ts +++ /dev/null @@ -1,174 +0,0 @@ -import { spawnSync } from 'node:child_process'; -import fs from 'node:fs'; -import path from 'node:path'; - -type FileBudgetResult = { - file: string; - lines: number; -}; - -type HotspotBudget = { - file: string; - baseline: number; - limit: number; -}; - -type HotspotStatus = HotspotBudget & { - lines: number; - delta: number; - overLimit: boolean; -}; - -const DEFAULT_LINE_LIMIT = 500; -const TARGET_DIRS = ['src', 'launcher']; -const TARGET_EXTENSIONS = new Set(['.ts']); -const IGNORE_NAMES = new Set(['.DS_Store']); -const HOTSPOT_BUDGETS: readonly HotspotBudget[] = [ - { file: 'src/main.ts', baseline: 2978, limit: 3150 }, - { file: 'src/config/service.ts', baseline: 116, limit: 140 }, - { file: 'src/core/services/tokenizer.ts', baseline: 232, limit: 260 }, - { file: 'launcher/main.ts', baseline: 101, limit: 130 }, - { file: 'src/config/resolve.ts', baseline: 33, limit: 55 }, - { file: 'src/main/runtime/composers/contracts.ts', baseline: 13, limit: 30 }, -]; - -function parseArgs(argv: string[]): { strict: boolean; limit: number; enforceGlobal: boolean } { - let strict = false; - let limit = DEFAULT_LINE_LIMIT; - let enforceGlobal = false; - - for (let i = 0; i < argv.length; i += 1) { - const arg = argv[i]; - if (arg === '--strict') { - strict = true; - continue; - } - if (arg === '--enforce-global') { - enforceGlobal = true; - continue; - } - if (arg === '--limit') { - const raw = argv[i + 1]; - const parsed = Number.parseInt(raw ?? '', 10); - if (!Number.isFinite(parsed) || parsed <= 0) { - throw new Error(`Invalid --limit value: ${raw ?? ''}`); - } - limit = parsed; - i += 1; - continue; - } - } - - return { strict, limit, enforceGlobal }; -} - -function resolveFilesWithRipgrep(): string[] { - const rg = spawnSync('rg', ['--files', ...TARGET_DIRS], { - cwd: process.cwd(), - encoding: 'utf8', - }); - if (rg.status !== 0) { - throw new Error(`rg --files failed:\n${rg.stderr || rg.stdout}`); - } - - return rg.stdout - .split('\n') - .map((line) => line.trim()) - .filter((line) => line.length > 0) - .filter((file) => TARGET_EXTENSIONS.has(path.extname(file))) - .filter((file) => !IGNORE_NAMES.has(path.basename(file))); -} - -function countLines(content: string): number { - if (content.length === 0) return 0; - return content.split('\n').length; -} - -function collectOverBudgetFiles(files: string[], limit: number): FileBudgetResult[] { - const results: FileBudgetResult[] = []; - for (const file of files) { - const content = fs.readFileSync(file, 'utf8'); - const lines = countLines(content); - if (lines > limit) { - results.push({ file, lines }); - } - } - return results.sort((a, b) => b.lines - a.lines || a.file.localeCompare(b.file)); -} - -function collectHotspotStatuses(hotspots: readonly HotspotBudget[]): HotspotStatus[] { - return hotspots.map((hotspot) => { - const content = fs.readFileSync(hotspot.file, 'utf8'); - const lines = countLines(content); - return { - ...hotspot, - lines, - delta: lines - hotspot.baseline, - overLimit: lines > hotspot.limit, - }; - }); -} - -function printGlobalReport( - overBudget: FileBudgetResult[], - limit: number, - strict: boolean, - enforceGlobal: boolean, -): void { - const mode = strict ? 'strict' : 'warning'; - if (overBudget.length === 0) { - console.log(`[OK] file budget check (${mode}) — no files over ${limit} LOC`); - return; - } - - const heading = strict && enforceGlobal ? '[FAIL]' : '[WARN]'; - console.log( - `${heading} file budget check (${mode}) — ${overBudget.length} files over ${limit} LOC`, - ); - for (const item of overBudget) { - console.log(` - ${item.file}: ${item.lines} LOC`); - } - console.log(' Hint: split by runtime/domain boundaries; keep composition roots thin.'); -} - -function printHotspotReport(hotspots: readonly HotspotStatus[], strict: boolean): void { - const mode = strict ? 'strict' : 'warning'; - const overLimit = hotspots.filter((hotspot) => hotspot.overLimit); - - if (overLimit.length === 0) { - console.log(`[OK] hotspot budget check (${mode}) — no hotspots over limit`); - } else { - const heading = strict ? '[FAIL]' : '[WARN]'; - console.log( - `${heading} hotspot budget check (${mode}) — ${overLimit.length} hotspots over limit`, - ); - } - - for (const hotspot of hotspots) { - const status = hotspot.overLimit ? 'OVER' : 'OK'; - const delta = hotspot.delta >= 0 ? `+${hotspot.delta}` : `${hotspot.delta}`; - console.log( - ` - [${status}] ${hotspot.file}: baseline=${hotspot.baseline}, limit=${hotspot.limit}, current=${hotspot.lines}, delta=${delta}`, - ); - } -} - -function main(): void { - const { strict, limit, enforceGlobal } = parseArgs(process.argv.slice(2)); - const files = resolveFilesWithRipgrep(); - const overBudget = collectOverBudgetFiles(files, limit); - const hotspotStatuses = collectHotspotStatuses(HOTSPOT_BUDGETS); - - printGlobalReport(overBudget, limit, strict, enforceGlobal); - printHotspotReport(hotspotStatuses, strict); - - const overGlobalLimit = overBudget.length > 0; - const hotspotOverLimit = hotspotStatuses.some((hotspot) => hotspot.overLimit); - const strictFailure = hotspotOverLimit || (strict && enforceGlobal && overGlobalLimit); - - if (strict && strictFailure) { - process.exitCode = 1; - } -} - -main();