Write `.code-report` at the repo root grouping flag-only violations by severity, then by kind, then by file. Errors are listed first (these block CI; an LLM cleanup pass should fix them). Advisories follow (CLAUDE.md guidance with existing-codebase debt -- worth addressing but they d
(report_path: Path, flag_only: list[Violation])
| 2498 | |
| 2499 | |
| 2500 | def _write_report(report_path: Path, flag_only: list[Violation]) -> None: |
| 2501 | """Write `.code-report` at the repo root grouping flag-only violations |
| 2502 | by severity, then by kind, then by file. Errors are listed first |
| 2503 | (these block CI; an LLM cleanup pass should fix them). Advisories |
| 2504 | follow (CLAUDE.md guidance with existing-codebase debt -- worth |
| 2505 | addressing but they don't fail the build). Skips writing when there's |
| 2506 | nothing to report and removes any stale report from a previous run.""" |
| 2507 | if not flag_only: |
| 2508 | if report_path.exists(): |
| 2509 | report_path.unlink() |
| 2510 | return |
| 2511 | |
| 2512 | errors_by_kind: dict[str, list[Violation]] = {} |
| 2513 | advisory_by_kind: dict[str, list[Violation]] = {} |
| 2514 | for v in flag_only: |
| 2515 | bucket = advisory_by_kind if v.kind in _ADVISORY_KINDS else errors_by_kind |
| 2516 | bucket.setdefault(v.kind, []).append(v) |
| 2517 | |
| 2518 | out: list[str] = [_REPORT_HEADER] |
| 2519 | |
| 2520 | if errors_by_kind: |
| 2521 | total = sum(len(vs) for vs in errors_by_kind.values()) |
| 2522 | out.append( |
| 2523 | f"\n## Errors ({total}) -- block CI\n\n" |
| 2524 | "These violations fail `code-verify.py --check`. An LLM cleanup\n" |
| 2525 | "pass should fix them before the next CI run -- the rules below\n" |
| 2526 | "encode CLAUDE.md's hard requirements (Qt conventions, hotpath\n" |
| 2527 | "safety, fence-protected non-ASCII, line endings, …).\n" |
| 2528 | ) |
| 2529 | for kind in sorted(errors_by_kind): |
| 2530 | entries = errors_by_kind[kind] |
| 2531 | out.append(f"\n### `{kind}` ({len(entries)})\n") |
| 2532 | for v in entries: |
| 2533 | out.append(f"- `{v.path}:{v.line}` — {v.message}\n") |
| 2534 | |
| 2535 | if advisory_by_kind: |
| 2536 | total = sum(len(vs) for vs in advisory_by_kind.values()) |
| 2537 | out.append( |
| 2538 | f"\n## Advisories ({total}) -- CI passes, fix when convenient\n\n" |
| 2539 | "CLAUDE.md guidance with broad existing-code debt. New code\n" |
| 2540 | "should aim to clear these. They populate the report so an\n" |
| 2541 | "incremental cleanup pass has a checklist; CI does not block.\n" |
| 2542 | ) |
| 2543 | for kind in sorted(advisory_by_kind): |
| 2544 | entries = advisory_by_kind[kind] |
| 2545 | out.append(f"\n### `{kind}` ({len(entries)})\n") |
| 2546 | for v in entries: |
| 2547 | out.append(f"- `{v.path}:{v.line}` — {v.message}\n") |
| 2548 | |
| 2549 | out.append(f"\n---\n\n_Total flagged: {len(flag_only)}_\n") |
| 2550 | _write_lf(report_path, "".join(out)) |
| 2551 | |
| 2552 | |
| 2553 | # --------------------------------------------------------------------------- |