diff --git a/docs/todo/TODO_SCAN_REPORT.md b/docs/todo/TODO_SCAN_REPORT.md
new file mode 100644
index 000000000..fc3803d15
--- /dev/null
+++ b/docs/todo/TODO_SCAN_REPORT.md
@@ -0,0 +1,177 @@
+# TODO Scan Report
+
+- Generated: `2025-12-25 20:56:07Z` (UTC)
+- Repo root: `/Users/rmac/Documents/GitHub/metabuilder`
+- Pattern: `\b(TODO|FIXME|HACK|XXX)\b`
+- Excludes: `docs/todo/`, `**/node_modules/`, `**/.next/`, `**/coverage/`, `**/dist/`, `**/build/`, `**/.git/`
+
+## Summary
+- Total matches: **152**
+- By marker: `TODO`=152
+- By top-level directory:
+ - `dbal`: 6
+ - `docs`: 95
+ - `frontends`: 36
+ - `tools`: 15
+
+## Matches
+
+### `dbal` (6)
+- `dbal/cpp/README.Linting.md:306` — - TODO/FIXME comments count
+- `dbal/cpp/lint.sh:137` — echo "Checking for TODO/FIXME comments..."
+- `dbal/cpp/lint.sh:138` — TODO_COUNT=$(grep -r "TODO\|FIXME" src/ include/ || true | wc -l)
+- `dbal/cpp/lint.sh:140` — echo -e "${YELLOW}⚠ Found $TODO_COUNT TODO/FIXME comments${NC}"
+- `dbal/cpp/lint.sh:141` — grep -rn "TODO\|FIXME" src/ include/ || true
+- `dbal/cpp/lint.sh:143` — echo -e "${GREEN}✓ No TODO/FIXME comments${NC}"
+
+### `docs` (95)
+- `docs/CONTRIBUTING.md:23` — - Leave TODO comments for missing functionality.
+- `docs/CONTRIBUTING.md:185` — 5. **TODOs** - Leave TODO comments for missing functionality
+- `docs/CONTRIBUTING.md:275` — TODO: Links below use ../docs/... from docs/CONTRIBUTING.md and resolve to docs/docs; update to correct relative paths (including security and copilot).
+- `docs/CONTRIBUTING.md:279` — TODO: E2E tests guide lives under frontends/nextjs/e2e; update this link.
+- `docs/DOCS_ORGANIZATION_COMPLETE.md:8` — TODO: This file is in docs/ so ./docs/ links are broken; root-level file list is outdated (README/CONTRIBUTING are not at repo root).
+- `docs/DOCS_ORGANIZATION_GUIDE.md:5` — TODO: This file is already in docs/; links that start with ./docs/ are broken and should be updated to correct relative paths.
+- `docs/DOCS_ORGANIZATION_GUIDE.md:139` — - Search for "ERROR:", "TODO:", "FIXME:" to find action items
+- `docs/NAVIGATION.md:144` — TODO: docs/src/ is missing; add the folder or update/remove the src links below.
+- `docs/README.md:5` — TODO: This file lives under docs/; links that start with ./docs/ are broken and PRD/SECURITY paths are outdated.
+- `docs/README.md:230` — TODO: src/README.md does not exist under docs/; confirm correct location or add missing docs/src.
+- `docs/README.md:261` — TODO: Manual deployment docs are not under docs/deployment; update this link to the correct location.
+- `docs/START_HERE.md:5` — TODO: This file is in docs/ so ./docs/ links are broken; the root-level file list below is outdated (README/CONTRIBUTING live elsewhere or do not exist).
+- `docs/START_HERE.md:16` — - Leave TODO comments for missing functionality.
+- `docs/architecture/css-as-abstract-system.md:275` — ## TODO
+- `docs/architecture/deployment.md:298` — TODO: Security guide lives at ../security/SECURITY.md; update this link.
+- `docs/architecture/packages.md:496` — TODO: Development guide link should point to docs/guides/getting-started.md (current file does not exist).
+- `docs/architecture/packages.md:498` — TODO: Component guidelines link points to a non-existent docs/components/README.md; update to correct location.
+- `docs/architecture/security.md:260` — TODO: Security guidelines live at ../security/SECURITY.md; update this link.
+- `docs/architecture/testing.md:243` — TODO: E2E tests live under frontends/nextjs/e2e; update this link.
+- `docs/archive/src/lib/CODE_TO_DOCS_MAPPING.md:9` — TODO: Audit component/hook documentation coverage, replace TBD values, and update `/src/` path references to match the current `frontends/nextjs/src` layout.
+- `docs/database/PRISMA_SETUP.md:140` — TODO: The migration guide link should be relative to docs/database (remove ./docs/).
+- `docs/deployments/CI_CD_REPAIRS.md:129` — 2. **quality-check**: Scans for console.log, TODO comments
+- `docs/deployments/CI_CD_SUMMARY.md:38` — 4. **Quality Check** - Console.log and TODO detection
+- `docs/deployments/CI_CD_SUMMARY.md:327` — TODO: Links below are repo-relative but this file is in docs/deployments; update paths for workflow README and E2E guide.
+- `docs/deployments/CI_FIX_SUMMARY.md:226` — # src/client.cpp # TODO: Implement
+- `docs/deployments/CI_FIX_SUMMARY.md:227` — # src/errors.cpp # TODO: Implement
+- `docs/deployments/GITHUB_WORKFLOWS_AUDIT.md:25` — - ✅ Code quality checks (console.log, TODO detection)
+- `docs/guides/SASS_QUICK_REFERENCE.md:287` — TODO: The design tokens link should not use ../docs/ from docs/guides; update to the correct relative path.
+- `docs/guides/getting-started.md:313` — TODO: Links in this section use ./ from docs/guides; update to correct relative paths (architecture/reference/troubleshooting) and fix README/SECURITY/COMPONENT_MAP locations be...
+- `docs/implementation/DBAL_INTEGRATION.md:285` — # TODO: deployment docs live under docs/deployments/; update this reference.
+- `docs/implementation/DBAL_INTEGRATION.md:391` — TODO: Fix related doc links (deployments path and local implementation docs).
+- `docs/implementation/DBAL_INTEGRATION.md:408` — TODO: No LICENSE file exists at repo root; update to correct location (e.g., docs/LICENSE) or add one.
+- `docs/migrations/FILE_RELOCATION_GUIDE.md:83` — TODO: This repo does not have root README/PRD/SECURITY/LIMITED files as listed; update for current structure (docs/* locations).
+- `docs/migrations/FILE_RELOCATION_GUIDE.md:92` — TODO: Example path uses /workspaces/spark-template and root file list no longer matches this repo.
+- `docs/migrations/MIGRATION_STATUS.md:159` — 3. Audit log storage using console.log - TODO: database implementation
+- `docs/migrations/MIGRATION_STATUS.md:160` — 4. Modular package storage stubbed - TODO: database implementation
+- `docs/migrations/RELOCATION_SUMMARY.md:79` — TODO: This repo does not have root README/PRD/SECURITY as listed; update to current docs locations.
+- `docs/refactoring/REFACTORING_INDEX.md:360` — TODO: Paths below are wrong from docs/refactoring; update copilot/PRD and schema/src references to valid locations.
+- `docs/refactoring/REFACTORING_SUMMARY.md:450` — TODO: Update copilot instructions and PRD links to correct relative paths from docs/refactoring.
+- `docs/reference/DOCUMENTATION_FINDINGS.md:605` — TODO: Core doc links below point to docs/reference; update to correct paths under docs/ (README, PRD, SECURITY).
+- `docs/reference/IMPROVEMENT_ROADMAP_INDEX.md:8` — TODO: This file uses ./docs/... links which are broken from docs/reference/; update to correct relative paths.
+- `docs/reference/PRIORITY_ACTION_PLAN.md:9` — TODO: This file uses ./docs/... links which are broken from docs/reference/; update to correct relative paths.
+- `docs/reference/STUB_DETECTION_IMPLEMENTATION.md:25` — - TODO/FIXME comments
+- `docs/reference/STUB_DETECTION_IMPLEMENTATION.md:152` — - `
TODO" src/
+- `docs/reference/STUB_DETECTION_IMPLEMENTATION.md:377` — TODO: Update reference links to correct repo-relative paths from docs/reference (stub-detection docs, workflows, scripts).
+- `docs/reference/STUB_DETECTION_QUICK_START.md:28` — - **10 medium** severity (marked as TODO/mock)
+- `docs/reference/STUB_DETECTION_QUICK_START.md:29` — - **179 low** severity (mostly TODO comments)
+- `docs/reference/STUB_DETECTION_QUICK_START.md:39` — | Returns null | `return null // TODO` | 🟡 Medium |
+- `docs/reference/STUB_DETECTION_QUICK_START.md:40` — | TODO comment | `// TODO: implement this` | 🟡 Medium |
+- `docs/reference/STUB_DETECTION_QUICK_START.md:41` — | Placeholder UI | `
TODO: build UI
` | 🟠 High |
+- `docs/reference/STUB_DETECTION_QUICK_START.md:57` — ### Find All TODO Comments
+- `docs/reference/STUB_DETECTION_QUICK_START.md:59` — grep -r "TODO:" src/ | grep -v test
+- `docs/reference/STUB_DETECTION_QUICK_START.md:149` — TODO: doc links below should be relative to docs/reference (use ../stub-detection/...).
+- `docs/reference/STUB_DETECTION_QUICK_START.md:173` — ### Tip 2: Create Issues Instead of TODO
+- `docs/reference/STUB_DETECTION_QUICK_START.md:176` — // TODO: add caching
+- `docs/reference/STUB_DETECTION_QUICK_START.md:238` — "code": "{ // TODO: Replace with proper database query"
+- `docs/reference/STUB_DETECTION_SUMMARY.md:44` — 4. **🟡 TODO Comments** - `// TODO:` or `// FIXME:` markers
+- `docs/reference/STUB_DETECTION_SUMMARY.md:45` — 5. **🟠 Placeholder Text** - `
TODO: build UI
`
+- `docs/reference/STUB_DETECTION_SUMMARY.md:86` — - ✅ **10 medium severity** - Some marked as TODO/mock
+- `docs/reference/STUB_DETECTION_SUMMARY.md:87` — - ✅ **179 low severity** - Mostly TODO comments
+- `docs/reference/STUB_DETECTION_SUMMARY.md:144` — ### Example 3: TODO Comment
+- `docs/reference/STUB_DETECTION_SUMMARY.md:147` — // TODO: implement processing // ← Detected: 🟡 Medium
+- `docs/reference/STUB_DETECTION_SUMMARY.md:230` — TODO: Stub-detection doc references below should be relative to docs/reference (use ../stub-detection/...).
+- `docs/reference/TESTING_IMPLEMENTATION_SUMMARY.md:7` — TODO: This file uses ../docs/... links that resolve to docs/docs; update to correct relative paths.
+- `docs/stub-detection/OVERVIEW.md:19` — - **Low**: 179 (TODO comments)
+- `docs/stub-detection/OVERVIEW.md:22` — - TODO/FIXME comments: 167
+- `docs/stub-detection/OVERVIEW.md:77` — TODO: Links below should be relative to docs/stub-detection (drop docs/ prefix).
+- `docs/stub-detection/OVERVIEW.md:108` — ### 4. TODO Comments (🟡 Medium)
+- `docs/stub-detection/OVERVIEW.md:111` — // TODO: implement this ← Detected
+- `docs/stub-detection/OVERVIEW.md:119` — return
TODO: Build dashboard
// ← Detected
+- `docs/stub-detection/OVERVIEW.md:208` — ### Fix TODO Comment
+- `docs/stub-detection/OVERVIEW.md:211` — // TODO: implement feature
+- `docs/stub-detection/QUICK_REFERENCE.md:10` — | `
TODO
` | 🟠 High | See Pattern 4 | Implement component |
+- `docs/stub-detection/QUICK_REFERENCE.md:12` — | `// TODO:` comment | 🟡 Medium | See Pattern 6 | Create issue, implement |
+- `docs/stub-detection/QUICK_REFERENCE.md:71` — ### Replace TODO Comments
+- `docs/stub-detection/QUICK_REFERENCE.md:74` — // TODO: implement feature
+- `docs/stub-detection/QUICK_REFERENCE.md:92` — # TODO comments
+- `docs/stub-detection/QUICK_REFERENCE.md:93` — grep -r "TODO\|FIXME" src/
+- `docs/stub-detection/QUICK_REFERENCE.md:154` — // TODO: implement
+- `docs/stub-detection/README.md:12` — - Contains TODO/FIXME comments indicating incomplete work
+- `docs/stub-detection/README.md:51` — return
TODO: Build dashboard
+- `docs/stub-detection/README.md:88` — | `// TODO:` or `// FIXME:` | Medium | Known issue, incomplete |
+- `docs/stub-detection/README.md:260` — return null // TODO: load config
+- `docs/stub-detection/README.md:278` — return
TODO: Build dashboard
+- `docs/stub-detection/README.md:315` — ### Pattern 6: TODO Comments
+- `docs/stub-detection/README.md:317` — **Problem**: Code has TODO/FIXME comments indicating incomplete work
+- `docs/stub-detection/README.md:319` — **Fix**: Create GitHub issue and remove TODO from code
+- `docs/stub-detection/README.md:324` — // TODO: Add real email validation
+- `docs/stub-detection/README.md:389` — // TODO: add caching (#TBD)
+
+### `frontends` (36)
+- `frontends/nextjs/src/components/GitHubActionsFetcher.tsx:66` — // TODO: Replace with proper GitHub OAuth authentication in Next.js
+- `frontends/nextjs/src/components/GitHubActionsFetcher.tsx:140` — // TODO: Replace with Next.js API route that calls an LLM service
+- `frontends/nextjs/src/components/GitHubActionsFetcher.tsx:291` — // TODO: Replace with Next.js API route that calls an LLM service
+- `frontends/nextjs/src/components/ScreenshotAnalyzer.tsx:64` — // TODO: Replace with Next.js API route that calls an LLM service
+- `frontends/nextjs/src/components/ui/accordion.ts:2` — // TODO: Update imports to use @/components/ui directly
+- `frontends/nextjs/src/components/ui/alert-dialog.ts:2` — // TODO: Update imports to use @/components/ui directly
+- `frontends/nextjs/src/components/ui/alert.ts:2` — // TODO: Update imports to use @/components/ui directly
+- `frontends/nextjs/src/components/ui/avatar.ts:2` — // TODO: Update imports to use @/components/ui directly
+- `frontends/nextjs/src/components/ui/badge.ts:2` — // TODO: Update imports to use @/components/ui directly
+- `frontends/nextjs/src/components/ui/button.ts:2` — // TODO: Update imports to use @/components/ui directly
+- `frontends/nextjs/src/components/ui/card.ts:2` — // TODO: Update imports to use @/components/ui directly
+- `frontends/nextjs/src/components/ui/checkbox.ts:2` — // TODO: Update imports to use @/components/ui directly
+- `frontends/nextjs/src/components/ui/dialog.ts:2` — // TODO: Update imports to use @/components/ui directly
+- `frontends/nextjs/src/components/ui/dropdown-menu.ts:2` — // TODO: Update imports to use @/components/ui directly
+- `frontends/nextjs/src/components/ui/input.ts:2` — // TODO: Update imports to use @/components/ui directly
+- `frontends/nextjs/src/components/ui/label.ts:2` — // TODO: Update imports to use @/components/ui directly
+- `frontends/nextjs/src/components/ui/progress.ts:2` — // TODO: Update imports to use @/components/ui directly
+- `frontends/nextjs/src/components/ui/radio-group.ts:2` — // TODO: Update imports to use @/components/ui directly
+- `frontends/nextjs/src/components/ui/scroll-area.ts:2` — // TODO: Update imports to use @/components/ui directly
+- `frontends/nextjs/src/components/ui/select.ts:2` — // TODO: Update imports to use @/components/ui directly
+- `frontends/nextjs/src/components/ui/separator.ts:2` — // TODO: Update imports to use @/components/ui directly
+- `frontends/nextjs/src/components/ui/sheet.ts:2` — // TODO: Update imports to use @/components/ui directly
+- `frontends/nextjs/src/components/ui/skeleton.ts:2` — // TODO: Update imports to use @/components/ui directly
+- `frontends/nextjs/src/components/ui/slider.ts:2` — // TODO: Update imports to use @/components/ui directly
+- `frontends/nextjs/src/components/ui/sonner.tsx:94` — // TODO: Implement dismiss by ID
+- `frontends/nextjs/src/components/ui/switch.ts:2` — // TODO: Update imports to use @/components/ui directly
+- `frontends/nextjs/src/components/ui/table.ts:2` — // TODO: Update imports to use @/components/ui directly
+- `frontends/nextjs/src/components/ui/tabs.ts:2` — // TODO: Update imports to use @/components/ui directly
+- `frontends/nextjs/src/components/ui/textarea.ts:2` — // TODO: Update imports to use @/components/ui directly
+- `frontends/nextjs/src/hooks/useAuth.ts:28` — // TODO: Implement proper auth with backend/Prisma
+- `frontends/nextjs/src/hooks/useKV.ts:9` — // TODO: Implement proper persistent KV storage with Database/Prisma
+- `frontends/nextjs/src/lib/lua/sandboxed-lua-engine.ts:19` — // TODO: Enforce maxMemory limit in sandbox execution.
+- `frontends/nextjs/src/lib/security/secure-db/log-operation.ts:28` — // TODO: Replace with proper audit log storage
+- `frontends/nextjs/src/lib/security/secure-db/operations/get-audit-logs.ts:14` — // TODO: Replace with proper audit log storage query using the requested limit.
+- `frontends/nextjs/src/lib/security/secure-db/operations/verify-credentials.ts:16` — // TODO: Track failed login attempts and enforce account lockout/backoff.
+- `frontends/nextjs/src/lib/security/secure-db/rate-limit-store.ts:1` — // TODO: Load rate limit settings from config/DB instead of hardcoding.
+
+### `tools` (15)
+- `tools/analyze-implementation-completeness.ts:104` — if (body.includes('TODO') || body.includes('FIXME')) {
+- `tools/analyze-test-coverage.ts:292` — // Generate TODO items
+- `tools/analyze-test-coverage.ts:294` — console.log("TODO - CREATE TESTS FOR:");
+- `tools/autobot.sh:9` — codex exec --full-auto --sandbox="danger-full-access" --cd="/Users/rmac/Documents/GitHub/metabuilder/docs/todo/" "Please resolve my TODO list, Scan project for TODO items." || e...
+- `tools/check-function-coverage.js:184` — console.log(`🎯 TODO - CREATE TESTS FOR:`);
+- `tools/detect-stub-implementations.ts:24` — name: 'TODO comment in function',
+- `tools/detect-stub-implementations.ts:25` — pattern: /\/\/\s*TODO|\/\/\s*FIXME|\/\/\s*XXX|\/\/\s*HACK/i,
+- `tools/detect-stub-implementations.ts:28` — description: 'Function has TODO/FIXME comment'
+- `tools/detect-stub-implementations.ts:53` — pattern: /<[A-Z]\w*[^>]*>\s*(placeholder|TODO|FIXME|stub|mock|example|not implemented)/i,
+- `tools/detect-stub-implementations.ts:130` — if (line.match(/stub|placeholder|mock|not implemented|TODO.*implementation/i)) {
+- `tools/generate-stub-report.ts:138` — report += ' return null // TODO: implement API call\n'
+- `tools/generate-stub-report.ts:151` — report += ' return
TODO: Build dashboard
\n'
+- `tools/generate-stub-report.ts:185` — report += '- [ ] All TODO/FIXME comments reference GitHub issues\n'
+- `tools/generate-stub-report.ts:194` — report += '3. **Convert stubs to issues** - Don\'t use TODO in code, create GitHub issues\n'
+- `tools/generate-stub-report.ts:196` — report += '5. **Use linting rules** - Configure ESLint to catch console.log and TODO\n\n'
diff --git a/docs/todo/TODO_STATUS.md b/docs/todo/TODO_STATUS.md
new file mode 100644
index 000000000..50be354df
--- /dev/null
+++ b/docs/todo/TODO_STATUS.md
@@ -0,0 +1,31 @@
+# TODO List Status
+
+- Generated: `2025-12-25 20:56:07Z` (UTC)
+- Directory: `/Users/rmac/Documents/GitHub/metabuilder/docs/todo`
+- Total items: **852** (`open`=829, `done`=23)
+
+| File | Open | Done | Total |
+|------|-----:|-----:|------:|
+| `0-kickstart.md` | 8 | 0 | 8 |
+| `1-TODO.md` | 4 | 0 | 4 |
+| `10-SECURITY-TODO.md` | 40 | 2 | 42 |
+| `11-DEPLOYMENT-TODO.md` | 40 | 0 | 40 |
+| `12-DOCUMENTATION-TODO.md` | 45 | 0 | 45 |
+| `13-DECLARATIVE-UI-TODO.md` | 45 | 1 | 46 |
+| `14-MULTI-TENANT-TODO.md` | 35 | 0 | 35 |
+| `15-BUILD-FIXES-TODO.md` | 34 | 6 | 40 |
+| `16-SCRIPTS-TOOLING-TODO.md` | 37 | 3 | 40 |
+| `17-GITHUB-COPILOT-TODO.md` | 35 | 0 | 35 |
+| `18-ACCESSIBILITY-TODO.md` | 40 | 0 | 40 |
+| `19-PERFORMANCE-TODO.md` | 40 | 0 | 40 |
+| `2-TODO.md` | 27 | 0 | 27 |
+| `20-FUTURE-FEATURES-TODO.md` | 45 | 0 | 45 |
+| `21-SDLC-TODO.md` | 127 | 2 | 129 |
+| `3-TODO.md` | 16 | 0 | 16 |
+| `4-DBAL-TODO.md` | 30 | 2 | 32 |
+| `5-FRONTEND-TODO.md` | 37 | 1 | 38 |
+| `6-PACKAGES-TODO.md` | 49 | 1 | 50 |
+| `7-DATABASE-TODO.md` | 30 | 0 | 30 |
+| `8-TESTING-TODO.md` | 30 | 4 | 34 |
+| `9-LUA-SCRIPTING-TODO.md` | 35 | 1 | 36 |
+| `README.md` | 0 | 0 | 0 |
diff --git a/docs/todo/scan-project-todos.py b/docs/todo/scan-project-todos.py
new file mode 100644
index 000000000..d87d1013d
--- /dev/null
+++ b/docs/todo/scan-project-todos.py
@@ -0,0 +1,205 @@
+#!/usr/bin/env python3
+
+from __future__ import annotations
+
+import re
+import subprocess
+from collections import Counter, defaultdict
+from dataclasses import dataclass
+from datetime import datetime, timezone
+from pathlib import Path
+
+
+@dataclass(frozen=True)
+class TodoMatch:
+ path: str
+ line: int
+ text: str
+
+
+PATTERN = r"\b(TODO|FIXME|HACK|XXX)\b"
+RG_GLOBS = [
+ "!docs/todo/**",
+ "!**/node_modules/**",
+ "!**/.next/**",
+ "!**/coverage/**",
+ "!**/dist/**",
+ "!**/build/**",
+ "!**/.git/**",
+]
+
+
+def _repo_root(script_dir: Path) -> Path:
+ # docs/todo -> docs -> repo root
+ return script_dir.parent.parent
+
+
+def _run_rg(repo_root: Path) -> list[TodoMatch]:
+ cmd = ["rg", "-n", "-S", PATTERN]
+ for glob in RG_GLOBS:
+ cmd.extend(["--glob", glob])
+ cmd.append(".")
+
+ completed = subprocess.run(
+ cmd,
+ cwd=repo_root,
+ text=True,
+ capture_output=True,
+ check=False,
+ )
+
+ if completed.returncode not in (0, 1):
+ raise RuntimeError(
+ "ripgrep failed\n"
+ f"cmd: {' '.join(cmd)}\n"
+ f"exit: {completed.returncode}\n"
+ f"stderr:\n{completed.stderr}"
+ )
+
+ matches: list[TodoMatch] = []
+ for raw_line in completed.stdout.splitlines():
+ # Format: path:line:text (text itself may contain ':', so split max 2)
+ file_part, sep1, rest = raw_line.partition(":")
+ if not sep1:
+ continue
+ line_part, sep2, text_part = rest.partition(":")
+ if not sep2:
+ continue
+ try:
+ line_no = int(line_part)
+ except ValueError:
+ continue
+ matches.append(
+ TodoMatch(
+ path=file_part.removeprefix("./"),
+ line=line_no,
+ text=text_part.rstrip(),
+ )
+ )
+ return matches
+
+
+def _top_level_dir(path: str) -> str:
+ if not path:
+ return "(root)"
+ if path.startswith(".github/"):
+ return ".github"
+ return path.split("/", 1)[0]
+
+
+def _marker_counts(matches: list[TodoMatch]) -> Counter[str]:
+ counts: Counter[str] = Counter()
+ marker_re = re.compile(PATTERN, re.IGNORECASE)
+ for match in matches:
+ found = marker_re.search(match.text)
+ if not found:
+ continue
+ counts[found.group(1).upper()] += 1
+ return counts
+
+
+def _render_scan_report(
+ repo_root: Path, out_path: Path, matches: list[TodoMatch]
+) -> None:
+ now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%SZ")
+
+ by_dir: dict[str, list[TodoMatch]] = defaultdict(list)
+ for match in matches:
+ by_dir[_top_level_dir(match.path)].append(match)
+
+ dir_counts = Counter({k: len(v) for k, v in by_dir.items()})
+ marker_counts = _marker_counts(matches)
+
+ lines: list[str] = []
+ lines.append("# TODO Scan Report")
+ lines.append("")
+ lines.append(f"- Generated: `{now}` (UTC)")
+ lines.append(f"- Repo root: `{repo_root}`")
+ lines.append(f"- Pattern: `{PATTERN}`")
+ lines.append(
+ "- Excludes: `docs/todo/`, `**/node_modules/`, `**/.next/`, `**/coverage/`, `**/dist/`, `**/build/`, `**/.git/`"
+ )
+ lines.append("")
+ lines.append("## Summary")
+ lines.append(f"- Total matches: **{len(matches)}**")
+ if marker_counts:
+ lines.append(
+ "- By marker: "
+ + ", ".join(
+ f"`{marker}`={marker_counts[marker]}"
+ for marker in sorted(marker_counts.keys())
+ )
+ )
+ lines.append("- By top-level directory:")
+ for directory in sorted(dir_counts.keys()):
+ lines.append(f" - `{directory}`: {dir_counts[directory]}")
+
+ lines.append("")
+ lines.append("## Matches")
+ for directory in sorted(by_dir.keys()):
+ lines.append("")
+ lines.append(f"### `{directory}` ({len(by_dir[directory])})")
+ for match in sorted(by_dir[directory], key=lambda m: (m.path, m.line)):
+ snippet = re.sub(r"\s+", " ", match.text).strip()
+ if len(snippet) > 180:
+ snippet = snippet[:177] + "..."
+ lines.append(f"- `{match.path}:{match.line}` — {snippet}")
+
+ out_path.write_text("\n".join(lines).rstrip() + "\n", encoding="utf-8")
+
+
+def _render_todo_status(todo_dir: Path, out_path: Path) -> None:
+ md_files = sorted(
+ p for p in todo_dir.glob("*.md") if p.name not in {"TODO_SCAN_REPORT.md", "TODO_STATUS.md"}
+ )
+
+ checkbox_open_re = re.compile(r"^\s*-\s*\[\s*\]\s+")
+ checkbox_done_re = re.compile(r"^\s*-\s*\[\s*x\s*\]\s+", re.IGNORECASE)
+
+ rows: list[tuple[str, int, int, int]] = []
+ total_open = 0
+ total_done = 0
+ for path in md_files:
+ open_count = 0
+ done_count = 0
+ for line in path.read_text(encoding="utf-8").splitlines():
+ if checkbox_done_re.match(line):
+ done_count += 1
+ elif checkbox_open_re.match(line):
+ open_count += 1
+ total_open += open_count
+ total_done += done_count
+ rows.append((path.name, open_count, done_count, open_count + done_count))
+
+ lines: list[str] = []
+ now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%SZ")
+ lines.append("# TODO List Status")
+ lines.append("")
+ lines.append(f"- Generated: `{now}` (UTC)")
+ lines.append(f"- Directory: `{todo_dir}`")
+ lines.append(f"- Total items: **{total_open + total_done}** (`open`={total_open}, `done`={total_done})")
+ lines.append("")
+ lines.append("| File | Open | Done | Total |")
+ lines.append("|------|-----:|-----:|------:|")
+ for filename, open_count, done_count, total in rows:
+ lines.append(f"| `{filename}` | {open_count} | {done_count} | {total} |")
+
+ out_path.write_text("\n".join(lines).rstrip() + "\n", encoding="utf-8")
+
+
+def main() -> None:
+ script_path = Path(__file__).resolve()
+ todo_dir = script_path.parent
+ repo_root = _repo_root(todo_dir)
+
+ matches = _run_rg(repo_root)
+ _render_scan_report(repo_root, todo_dir / "TODO_SCAN_REPORT.md", matches)
+ _render_todo_status(todo_dir, todo_dir / "TODO_STATUS.md")
+
+ print(f"Wrote: {todo_dir / 'TODO_SCAN_REPORT.md'}")
+ print(f"Wrote: {todo_dir / 'TODO_STATUS.md'}")
+
+
+if __name__ == "__main__":
+ main()
+
diff --git a/frontends/nextjs/src/lib/rendering/page-renderer.test.ts b/frontends/nextjs/src/lib/rendering/page-renderer.test.ts
index fb7bce2ba..ae1d78d00 100644
--- a/frontends/nextjs/src/lib/rendering/page-renderer.test.ts
+++ b/frontends/nextjs/src/lib/rendering/page-renderer.test.ts
@@ -49,8 +49,7 @@ function createMockUser(role: string, id = 'user1'): User {
return {
id,
username: `User ${id}`,
- role: role as any,
- level: role === 'public' ? 1 : role === 'user' ? 2 : role === 'admin' ? 3 : role === 'god' ? 4 : 5,
+ role: role as UserRole,
email: `${id}@test.com`,
createdAt: Date.now(),
}