chore(observability): admin metrics & error logging (#5 review)#17
Conversation
Follow-up: actually surface the unknown state in the UIReviewer's finding was correct. Returning Fix in 56567db
The repo's Verification
|
JSX whitespace fixReviewer was right — splitting the word as `repositor` + `{count === 1 ? "y" : "ies"}` rendered with a space in the middle because JSX preserves whitespace between text and expressions. Fix in 33c5e95 (pushed)Move the whole word inside the conditional and use explicit `{" "}` to control the space between the count and the noun: ```tsx Renders correctly as Verification
|
The admin dashboard displayed `0%` for "Parsing Errors" with the description "Target KPI: < 1%", suggesting the metric was working when in fact no parsing-error log table exists yet — the value was a hardcoded zero. Admins reading "0%" would conclude the system has no parsing errors when the reality is the data is not collected. Change `parsingErrorRate` from `number` to `number | null` in the UsageMetrics interface, return `null` from getUsageMetrics(), and have the stats card render "N/A" with the description "Not yet tracked" when the value is null. The existing display path (number + "%") is preserved for when a real metric is wired up. Addresses finding #17 from bmad-method-ui#5 review.
When a GraphQL batch fails (transient network error, GitHub hiccup), the catch handler set every repo in that chunk to `false`, which is the same value used for "confirmed no BMAD". A caller asking the UI "does this repo have BMAD?" got "no" — when the truthful answer was "we don't know, the API call failed". Change the result map type from `Record<string, boolean>` to `Record<string, boolean | null>`: - true → BMAD detected - false → confirmed absent - null → detection failed for this repo (unknown) The single in-repo consumer (add-repo-dialog) already coerces `bmadResult.data[r.fullName] ?? false` at the boundary so existing behavior is preserved; surfacing the unknown count in the UI is a follow-up that can build on this contract. Addresses finding #16 from bmad-method-ui#5 review.
fetchFileContent wraps provider.extendBmadDirs in `try { ... } catch {}`
with a comment relying on the downstream getFileContent →
assertSafePath check to enforce the whitelist invariant. That
reasoning is still correct (assertSafePath refuses any path whose
top-level segment is not in the whitelist, so an extension that
failed to apply cannot be exploited), but discarding the error gives
zero visibility when the validation path is exercised in production.
Replace the silent catch with a console.warn that captures the
owner / name / segment / error message. No behavior change — the
authoritative refusal still happens in assertSafePath if the segment
was not added.
Addresses finding #15 from bmad-method-ui#5 review.
Commit 8090e03 had the server return `null` for repos in a failed batch, but add-repo-dialog still applied `?? false` at the boundary and rendered the repo identically to a confirmed "no BMAD". The data contract was honest but the user saw the same lie as before. Count the null values returned by detectBmadRepos and surface that count via a new `unknownDetectCount` state. When > 0 the dialog shows an amber warning ("BMAD detection failed for N repositor(y/ies)…") in the same area as the existing detection status. The repo list itself stays usable; only the misleading badge gets the disclaimer. State name is intentionally distinct from existing/planned warning state in PR #14 so the two surfaces can coexist when both PRs merge. Addresses the follow-up review on 8090e03.
The previous JSX split the word as `repositor` + `{count === 1 ? "y"
: "ies"}`. React preserves whitespace between text and expressions,
so the rendered output had a space in the middle of the word
("repositor y" / "repositor ies").
Move the whole word into the conditional ("repository" /
"repositories") and use an explicit `{" "}` to control the space
between the count and the word. The dash and trailing text are also
on their own lines, which JSX collapses to single spaces correctly.
Addresses the follow-up review on 56567db.
0742e9d to
df08f8d
Compare
Summary
Addresses 3 observability findings from the Raven's Verdict review on bmad-method-ui#5. All three were UX/visibility issues that presented fabricated or invisible information; the fixes restore honesty without changing behavior.
Findings addressed
0%for "Parsing Errors" — a hardcoded zero presented as a real KPInullfromgetUsageMetrics, render"N/A — Not yet tracked"in the stats carddetectBmadReposGraphQL batch failed, the catch handler set every repo in that chunk tofalse(indistinguishable from confirmed-absent)boolean | null, mark failed batches asnullso callers can distinguish "unknown" from "confirmed no"fetchFileContentwrappedprovider.extendBmadDirs(topSegment)in a silentcatch {}— the downstreamassertSafePathis still the authoritative invariant but logs had zero signalconsole.warncapturing owner/name/segment/error; no behavior changeCommits
fix(admin): show parsingErrorRate as N/A until tracking existsfix(actions): represent detectBmadRepos batch failures as nullchore(actions): log extendBmadDirs failures instead of silently catchingVerification
pnpm tsc --noEmit— cleanpnpm lint— 0 errors (2 pre-existing warnings unrelated)pnpm test— 151/151 passingNote on #16
The single in-repo consumer (
add-repo-dialog.tsx) doesbmadResult.data[r.fullName] ?? falseat the boundary, so thenullvalues are coerced tofalsefor the existing badge rendering — current behavior is preserved. Surfacing the unknown count to the user (e.g. "BMAD detection failed for 12 repos") was kept out of this PR because the dialog is also modified by PR #14 for the LIMIT_EXCEEDED warning, and the merge story is cleaner if the UI surface comes as a follow-up after both PRs land.Context
PR 4 of 4 addressing bmad-method-ui#5 review. This completes the upstream fix series.
Findings not addressed (intentional):
ALLOW_REGISTRATIONis read lazily in Better Auth'sbeforehook, not at module init.LocalProvider.assertSafePathalready blocks null bytes, Unicode slashes, traversal, absolute paths, and enforces the bmadDirs whitelist.revalidateTagsecond arg) — Next.js 16.1.6 acceptsrevalidateTag(tag, profile). Not spurious.Follow-up issues tracked: #12 CSP and #13 role enum.