feat(ui): add /api/audit/summary aggregator for production audit dashboard - #749
Conversation
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
📝 WalkthroughWalkthroughIntroduces a new Next.js API route that aggregates production audit data from multiple markdown and JSON sources, including audit dashboards, release gates, roadmaps, and optional runtime health checks, returning a unified JSON response with structured sections and warnings. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant APIRoute as Audit Summary Route
participant FileSystem as File System
participant Parser as Data Parser
participant Response as Response Handler
Client->>APIRoute: GET /api/audit/summary
APIRoute->>FileSystem: Find docs root
alt docs root found
FileSystem-->>APIRoute: docs path
APIRoute->>FileSystem: Read PRODUCTION_AUDIT_DASHBOARD.md
APIRoute->>FileSystem: Read release gate markdown
APIRoute->>FileSystem: Read ROADMAP.md
APIRoute->>FileSystem: Read NEXT_STEPS.md
APIRoute->>FileSystem: Read PR monitor artifacts
APIRoute->>FileSystem: Read Graphiti artifacts
FileSystem-->>APIRoute: file contents + errors/warnings
APIRoute->>Parser: Parse markdown tables (metrics, blockers, gates)
APIRoute->>Parser: Parse last-updated timestamps
APIRoute->>Parser: Aggregate JSON artifacts
Parser-->>APIRoute: structured data
alt includeHealth query param set
APIRoute->>FileSystem: Fetch runtime health data
FileSystem-->>APIRoute: health metrics
APIRoute->>Parser: Compute health summary
Parser-->>APIRoute: health aggregation
end
APIRoute->>Response: Construct JSON payload with all sections
Response-->>Client: 200 OK (structured audit summary + warnings)
else docs root not found
FileSystem-->>APIRoute: not found
APIRoute->>Response: Construct error response
Response-->>Client: 500 Internal Server Error
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@pmoves/ui/app/api/audit/summary/route.ts`:
- Around line 319-323: The code currently reads ROADMAP.md and NEXT_STEPS.md
silently failing; wrap the reads for roadmapPath and nextStepsPath in existence
checks or try/catch and when a read fails push a descriptive warning into the
existing warnings array (include the path variable like
roadmapPath/nextStepsPath and context such as "ROADMAP.md missing" or the caught
error message). Update the logic around roadmapMarkdown and nextStepsMarkdown to
only use their values if the read succeeded. Ensure you reference the variables
roadmapPath, nextStepsPath, roadmapMarkdown, nextStepsMarkdown and the warnings
array so missing files are reported rather than ignored.
- Around line 120-134: The parseMetricTable function currently processes every
markdown table row and can pick up rows from unrelated tables; change it to
first locate the metrics table header (a row where the first column lowercased
is "metric" and there is a second column like "value"/"metric value"), then only
parse subsequent table rows until the table ends (stop when a non-table line or
a blank line occurs). Update parseMetricTable to: scan lines to find the header
row, begin parsing only after that header, skip the header/separator (e.g.,
lines starting with "---"), and use the existing cols[0]/cols[1] assignment to
populate ExecutiveMetrics so unrelated table rows cannot leak in.
- Around line 353-379: Remove absolute filesystem exposure by not returning
docsRoot and by converting every source path to a docs-relative identifier:
compute a relative path using path.relative(docsRoot,
dashboardPath|releaseGatePath|roadmapPath|nextStepsPath) (or null if the
markdown var is falsy or the path is outside docsRoot), and set
productionAudit.source, releaseGates.source, planning.roadmap.source and
planning.nextSteps.source to that relative identifier instead of the raw
absolute path; ensure docsRoot itself is omitted from the returned payload.
- Around line 188-196: latestReleaseGatePath (and the other spot using
fs.readdir) calls fs.readdir without try/catch which can throw despite the prior
exists() check; wrap each fs.readdir invocation in a try/catch that logs a
warning (following the readText/exists pattern) and returns null (or an empty
result) to preserve fail-soft behavior. Locate latestReleaseGatePath and the
other function using fs.readdir, add a try/catch around the await
fs.readdir(...) call, on catch call the module's logger/warning mechanism with
contextual details and return null so the route degrades gracefully.
| function parseMetricTable(markdown: string): ExecutiveMetrics { | ||
| const metrics: ExecutiveMetrics = {}; | ||
| for (const line of markdown.split(/\r?\n/)) { | ||
| if (!line.startsWith("|")) continue; | ||
| const cols = line | ||
| .split("|") | ||
| .map((c) => c.trim()) | ||
| .filter(Boolean); | ||
| if (cols.length < 2) continue; | ||
| if (cols[0].toLowerCase() === "metric") continue; | ||
| if (cols[0].startsWith("---")) continue; | ||
| if (!cols[0] || !cols[1]) continue; | ||
| metrics[cols[0]] = cols[1]; | ||
| } | ||
| return metrics; |
There was a problem hiding this comment.
Scope parseMetricTable to the metrics table only.
Line 122 currently parses all markdown table rows, so rows from other tables can leak into executiveMetrics and corrupt the summary.
Proposed fix
function parseMetricTable(markdown: string): ExecutiveMetrics {
const metrics: ExecutiveMetrics = {};
+ let inMetricsTable = false;
+
for (const line of markdown.split(/\r?\n/)) {
- if (!line.startsWith("|")) continue;
+ if (!inMetricsTable) {
+ if (/^\|\s*Metric\s*\|\s*Value\s*\|/i.test(line)) inMetricsTable = true;
+ continue;
+ }
+ if (!line.startsWith("|")) break;
+
const cols = line
.split("|")
.map((c) => c.trim())
.filter(Boolean);
if (cols.length < 2) continue;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@pmoves/ui/app/api/audit/summary/route.ts` around lines 120 - 134, The
parseMetricTable function currently processes every markdown table row and can
pick up rows from unrelated tables; change it to first locate the metrics table
header (a row where the first column lowercased is "metric" and there is a
second column like "value"/"metric value"), then only parse subsequent table
rows until the table ends (stop when a non-table line or a blank line occurs).
Update parseMetricTable to: scan lines to find the header row, begin parsing
only after that header, skip the header/separator (e.g., lines starting with
"---"), and use the existing cols[0]/cols[1] assignment to populate
ExecutiveMetrics so unrelated table rows cannot leak in.
| async function latestReleaseGatePath(docsRoot: string): Promise<string | null> { | ||
| const auditDir = path.join(docsRoot, "audit"); | ||
| if (!(await exists(auditDir))) return null; | ||
| const files = await fs.readdir(auditDir); | ||
| const matches = files.filter((name) => /^RELEASE_GATE_CHECK_\d{4}-\d{2}-\d{2}\.md$/i.test(name)); | ||
| if (!matches.length) return null; | ||
| matches.sort((a, b) => b.localeCompare(a)); | ||
| return path.join(auditDir, matches[0]); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
rg -n -C3 'fs\.readdir\(' pmoves/ui/app/api/audit/summary/route.tsRepository: POWERFULMOVES/PMOVES.AI
Length of output: 870
🏁 Script executed:
cat -n pmoves/ui/app/api/audit/summary/route.ts | sed -n '180,220p'Repository: POWERFULMOVES/PMOVES.AI
Length of output: 1699
🏁 Script executed:
cat -n pmoves/ui/app/api/audit/summary/route.ts | sed -n '1,100p'Repository: POWERFULMOVES/PMOVES.AI
Length of output: 2977
🏁 Script executed:
rg -n 'export.*GET|async.*GET' pmoves/ui/app/api/audit/summary/route.ts -A 50Repository: POWERFULMOVES/PMOVES.AI
Length of output: 2199
🏁 Script executed:
cat -n pmoves/ui/app/api/audit/summary/route.ts | sed -n '290,360p'Repository: POWERFULMOVES/PMOVES.AI
Length of output: 3324
Guard fs.readdir calls to preserve fail-soft behavior.
Lines 191 and 209 use fs.readdir without error handling. The existence checks do not prevent all failures—permission errors, concurrent deletions, and other I/O issues can still occur and crash the route. The codebase pattern (see readText at lines 95-100 and exists at lines 70-77) consistently wraps file operations in try/catch to degrade gracefully by adding warnings. These two calls should follow the same pattern.
Proposed fix
async function latestReleaseGatePath(docsRoot: string): Promise<string | null> {
const auditDir = path.join(docsRoot, "audit");
if (!(await exists(auditDir))) return null;
- const files = await fs.readdir(auditDir);
+ let files: string[] = [];
+ try {
+ files = await fs.readdir(auditDir);
+ } catch {
+ return null;
+ }
const matches = files.filter((name) => /^RELEASE_GATE_CHECK_\d{4}-\d{2}-\d{2}\.md$/i.test(name));
if (!matches.length) return null;
matches.sort((a, b) => b.localeCompare(a));
return path.join(auditDir, matches[0]);
}
@@
const fallbackDir = path.join(docsRoot, "evidence", "pr_monitor");
if (await exists(fallbackDir)) {
- const files = (await fs.readdir(fallbackDir)).filter((f) => f.endsWith("-latest.json"));
- files.sort((a, b) => b.localeCompare(a));
- if (files.length > 0) {
- source = path.join(fallbackDir, files[0]);
+ let files: string[] = [];
+ try {
+ files = await fs.readdir(fallbackDir);
+ } catch {
+ files = [];
+ }
+ const latest = files.filter((f) => f.endsWith("-latest.json"));
+ latest.sort((a, b) => b.localeCompare(a));
+ if (latest.length > 0) {
+ source = path.join(fallbackDir, latest[0]);
payload = await readJson<unknown>(source);
}
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@pmoves/ui/app/api/audit/summary/route.ts` around lines 188 - 196,
latestReleaseGatePath (and the other spot using fs.readdir) calls fs.readdir
without try/catch which can throw despite the prior exists() check; wrap each
fs.readdir invocation in a try/catch that logs a warning (following the
readText/exists pattern) and returns null (or an empty result) to preserve
fail-soft behavior. Locate latestReleaseGatePath and the other function using
fs.readdir, add a try/catch around the await fs.readdir(...) call, on catch call
the module's logger/warning mechanism with contextual details and return null so
the route degrades gracefully.
| const roadmapPath = path.join(docsRoot, "PMOVES.AI PLANS", "ROADMAP.md"); | ||
| const roadmapMarkdown = await readText(roadmapPath); | ||
| const nextStepsPath = path.join(docsRoot, "NEXT_STEPS.md"); | ||
| const nextStepsMarkdown = await readText(nextStepsPath); | ||
|
|
There was a problem hiding this comment.
Emit warnings when planning files are missing.
ROADMAP/NEXT_STEPS reads currently fail silently, so warnings[] under-reports missing artifacts.
Proposed fix
const roadmapPath = path.join(docsRoot, "PMOVES.AI PLANS", "ROADMAP.md");
const roadmapMarkdown = await readText(roadmapPath);
const nextStepsPath = path.join(docsRoot, "NEXT_STEPS.md");
const nextStepsMarkdown = await readText(nextStepsPath);
+ if (!roadmapMarkdown) warnings.push("missing ROADMAP.md");
+ if (!nextStepsMarkdown) warnings.push("missing NEXT_STEPS.md");🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@pmoves/ui/app/api/audit/summary/route.ts` around lines 319 - 323, The code
currently reads ROADMAP.md and NEXT_STEPS.md silently failing; wrap the reads
for roadmapPath and nextStepsPath in existence checks or try/catch and when a
read fails push a descriptive warning into the existing warnings array (include
the path variable like roadmapPath/nextStepsPath and context such as "ROADMAP.md
missing" or the caught error message). Update the logic around roadmapMarkdown
and nextStepsMarkdown to only use their values if the read succeeded. Ensure you
reference the variables roadmapPath, nextStepsPath, roadmapMarkdown,
nextStepsMarkdown and the warnings array so missing files are reported rather
than ignored.
| docsRoot, | ||
| warnings, | ||
| productionAudit: { | ||
| source: dashboardMarkdown ? dashboardPath : null, | ||
| lastUpdated: dashboardMarkdown ? parseLabelValue(dashboardMarkdown, "Last Updated") : null, | ||
| branch: dashboardMarkdown ? parseLabelValue(dashboardMarkdown, "Branch") : null, | ||
| commit: dashboardMarkdown ? parseLabelValue(dashboardMarkdown, "Commit") : null, | ||
| executiveMetrics: dashboardMarkdown ? parseMetricTable(dashboardMarkdown) : {}, | ||
| activeBlockers: dashboardMarkdown ? parseActiveBlockers(dashboardMarkdown) : [], | ||
| }, | ||
| releaseGates: { | ||
| source: releaseGateMarkdown ? releaseGatePath : null, | ||
| items: releaseGateMarkdown ? parseReleaseGateRows(releaseGateMarkdown) : [], | ||
| }, | ||
| planning: { | ||
| roadmap: { | ||
| source: roadmapMarkdown ? roadmapPath : null, | ||
| lastUpdated: roadmapMarkdown ? parsePlanLastUpdated(roadmapMarkdown) : null, | ||
| }, | ||
| nextSteps: { | ||
| source: nextStepsMarkdown ? nextStepsPath : null, | ||
| lastUpdated: nextStepsMarkdown ? parsePlanLastUpdated(nextStepsMarkdown) : null, | ||
| }, | ||
| }, | ||
| prMonitor, | ||
| graphiti, | ||
| runtimeHealth, |
There was a problem hiding this comment.
Do not return absolute filesystem paths in API payload.
docsRoot and several source fields can expose host path structure to clients. Return docs-relative identifiers instead.
Proposed fix
+function toPublicPath(docsRoot: string, absolutePath: string | null): string | null {
+ if (!absolutePath) return null;
+ const rel = path.relative(docsRoot, absolutePath).replace(/\\/g, "/");
+ return rel.startsWith("..") ? null : `docs/${rel}`;
+}
+
export async function GET(request: NextRequest) {
@@
return NextResponse.json(
{
generatedAt: new Date().toISOString(),
- docsRoot,
+ docsRoot: "docs",
warnings,
productionAudit: {
- source: dashboardMarkdown ? dashboardPath : null,
+ source: toPublicPath(docsRoot, dashboardMarkdown ? dashboardPath : null),
@@
releaseGates: {
- source: releaseGateMarkdown ? releaseGatePath : null,
+ source: toPublicPath(docsRoot, releaseGateMarkdown ? releaseGatePath : null),
@@
roadmap: {
- source: roadmapMarkdown ? roadmapPath : null,
+ source: toPublicPath(docsRoot, roadmapMarkdown ? roadmapPath : null),
@@
nextSteps: {
- source: nextStepsMarkdown ? nextStepsPath : null,
+ source: toPublicPath(docsRoot, nextStepsMarkdown ? nextStepsPath : null),
@@
- prMonitor,
- graphiti,
+ prMonitor: { ...prMonitor, source: toPublicPath(docsRoot, prMonitor.source) },
+ graphiti: { ...graphiti, source: toPublicPath(docsRoot, graphiti.source) },
runtimeHealth,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@pmoves/ui/app/api/audit/summary/route.ts` around lines 353 - 379, Remove
absolute filesystem exposure by not returning docsRoot and by converting every
source path to a docs-relative identifier: compute a relative path using
path.relative(docsRoot, dashboardPath|releaseGatePath|roadmapPath|nextStepsPath)
(or null if the markdown var is falsy or the path is outside docsRoot), and set
productionAudit.source, releaseGates.source, planning.roadmap.source and
planning.nextSteps.source to that relative identifier instead of the raw
absolute path; ensure docsRoot itself is omitted from the returned payload.
Summary
pmoves/ui/app/api/audit/summary/route.tspmoves/docs/PRODUCTION_AUDIT_DASHBOARD.mdpmoves/docs/audit/RELEASE_GATE_CHECK_*.mddocs/logsprimary,docs/evidence/pr_monitorfallback)docs/logs/graphiti_signed_latest.json)checkAllServiceswarnings[]for absent artifactsNotes
Validation
npx eslint app/api/audit/summary/route.ts(frompmoves/ui)tokenismandlib/serviceDiscovery.tsSummary by CodeRabbit
Release Notes