ADR-0079: Dashboard strict typecheck + JSDoc enforcement (phased)¶
- Status: Accepted
- Date: 2026-05-23
- Driver: 2026-05-23 best-practices review §2 (
noUncheckedIndexedAccessnot enabled) + §4.3 (TS comment density 4.7% vs Go 24%)
Context¶
Two gaps in the dashboard codebase quality, both flagged by the 2026-05-23 best-practices review:
- §2 — TypeScript safety:
tsconfig.jsonenablesstrict: true,noUnusedLocals,noUnusedParameters,noImplicitReturns,noFallthroughCasesInSwitch— solid. ButnoUncheckedIndexedAccessis not enabled, so:
const userById = new Map<string, User>();
const u = userById.get(id); // type: User (not User | undefined!)
u.name; // RUNTIME ERROR if id was unknown
The same shape applies to array indexing (arr[i]), record
indexing (obj[key]), and destructuring with index access.
- §4.3 — Documentation density: Go ratio is 24% (excellent); TS
ratio is 4.7%. Drilling in,
src/lib/find/registry.ts(787 LoC),src/lib/dut-catalog/loader.ts(685 LoC) are sparse on JSDoc.
The straightforward fixes (enable the flag, enforce JSDoc on lib/) would land hundreds of new errors in a single PR — unsafe to ship without staging.
Decision¶
Adopt phased rollout for both:
Part A — noUncheckedIndexedAccess¶
- New
dashboard/tsconfig.strict.jsonextendstsconfig.jsonand adds the flag. - New
dashboard/package.jsonscripttypecheck:strictrunstsc -p tsconfig.strict.json --noEmit. - New
.github/workflows/dashboard-typecheck-strict.ymlrunstypecheck:stricton every PR, warn-only (continue-on-error: true). - The workflow comments the current finding count + delta-vs-baseline on the PR so ratcheting is visible.
- Existing
dashboard — typecheck, lint, test, buildjob continues to use the non-stricttsconfig.json— no behavior change to the required checks.
Baseline at landing: 430 strict findings. Most are
Map.get() / arr[i] patterns in lib/ and app/ pages.
Part B — JSDoc enforcement¶
- New devDependency
eslint-plugin-jsdoc. - New eslint.config.mjs rule block scoped to
src/lib/**/*.{ts,tsx}:
| Rule | Phase |
|---|---|
jsdoc/check-alignment |
WARN (active now) |
jsdoc/check-syntax |
WARN (active now) |
jsdoc/require-jsdoc |
OFF (Phase 2 ratchet target) |
jsdoc/require-description |
OFF (Phase 2) |
jsdoc/require-param-description |
OFF (Phase 2) |
jsdoc/require-returns-description |
OFF (Phase 2) |
jsdoc/no-undefined-types |
OFF (TS covers it) |
jsdoc/valid-types |
OFF (confused by TS-style annotations) |
Phase 1 enforces structural correctness of existing JSDoc (alignment, syntax) without requiring new headers. This makes sloppy existing comments fail the lint while not penalizing files that have no JSDoc.
Part C — Ratchet plan¶
| Wave | When | Action |
|---|---|---|
| W0 | This PR | Land strict-typecheck workflow (warn-only) + JSDoc plugin (alignment+syntax only). |
| W1 | +4 weeks | Fix all Map.get() patterns in src/lib/ (about 80 findings). |
| W2 | +8 weeks | Fix all array-index patterns in src/app/ API routes. |
| W3 | +12 weeks | Flip jsdoc/require-jsdoc from off to warn for files with > 200 LoC. |
| W4 | +16 weeks | When strict-typecheck baseline is < 50, flip the workflow to fail-gate + promote to required_status_checks. |
Each ratchet wave is its own PR with the corresponding fixes.
Consequences¶
Positive¶
- First visibility into the strict-typecheck baseline. Without measurement we can't manage it.
- Trend tracking — the workflow's comment shows whether each PR adds or removes findings. New PRs adding strict findings will be visible (though not blocking) on the PR UI.
- JSDoc structural correctness active immediately — bad existing JSDoc now fails the lint.
Negative¶
- One more CI job (~30s on a hot cache). Mitigated by
paths:filter — only runs when TS files / config / package change. - Warn-only initially means findings can still land. Discipline required to actually look at the workflow comment. Mitigated by the W3-W4 ratchet timeline.
Alternatives considered¶
- Enable in main tsconfig + fix all 430 findings in this PR — rejected as too-large blast radius. A future ratchet wave can do this when the baseline is < 50.
tsconfig.lib.jsonstrict overlay only onsrc/lib/**— rejected initially becausesrc/lib/interacts withsrc/app/types; if a function in lib/ takesUser | undefinedthen the caller in app/ needs to handle that too, which means the strict mode needs to be consistent across files. Wave 1 may revisit this.@ts-expect-errorbaseline file — rejected: too noisy in every-day code reading. The separate strict tsconfig is cleaner.
See also¶
dashboard/tsconfig.strict.json— the strict overlay.github/workflows/dashboard-typecheck-strict.yml— the warn gatedashboard/eslint.config.mjs— the JSDoc plugin block- ADR-0074 — sibling pattern (file-size budget with baseline file)