Type gate — measured state and the next ratchet
Measured: 2026-08-28, on claude/console-redesign-install-q7u595 at
f7aac2a, in miz-oki-command-center-ui/ after a fresh npm ci.
Finding: the type gate is already closed
The console-redesign plan called for turning the type gate on as the blocker that makes everything after it safe, on the premise that build errors were being ignored behind a delta-tsc baseline of roughly 556 errors. That premise is stale. Both gates are already enforced, and the tree is clean beneath them.
next.config.mjs (lines 125–134), landed in Phase 3H:
// Build gates enforced (Phase 3H): the build fails on ESLint errors and
// TypeScript errors. Both gates measured clean before enforcement
// (tsc --noEmit exit 0; next lint 0 errors). Do not re-add the ignore
// flags — fix the diagnostic instead.
eslint: { ignoreDuringBuilds: false },
typescript: { ignoreBuildErrors: false },
Measured against that config:
| Check | Command | Result |
|---|---|---|
| TypeScript | npx tsc --noEmit |
exit 0 — 0 errors |
| ESLint | npx next lint |
0 errors, 112 warnings |
There is no delta-tsc baseline in the repository. A search across
.github/ and miz-oki-command-center-ui/ for delta-tsc, tsc-baseline,
deltaTsc, and the literal 556 returns no baseline file, no CI step
consuming one, and no npm script referencing one. The 556 figure came from
a reading of the repo and does not correspond to anything measurable on
main today. tsconfig.json already sets strict, and npm run verify
chains lint && typecheck && test && build.
Nothing in this step needs to be flipped. The sequencing argument — make
the gate safe before scaffolding new routes — still holds; it is simply
already satisfied. New code added under app/(console)/** inherits a build
that fails on any TypeScript error or ESLint error the moment it is
introduced.
What is still open: the warning ratchet
The one gate that is not closed is warnings. 112 of them pass the build today, and nothing stops that number growing.
Histogram, by rule
| Count | Rule |
|---|---|
| 59 | react/no-unescaped-entities |
| 37 | react-hooks/exhaustive-deps |
| 9 | import/no-anonymous-default-export |
| 5 | @next/next/no-img-element |
| 1 | jsx-a11y/alt-text |
| 1 | @next/next/no-page-custom-font |
| 112 | total (0 errors) |
Attribution to the five console destinations
Counting warnings in the directories that feed each planned destination:
| Destination | Warnings | Rules |
|---|---|---|
decisions |
0 | — |
evidence |
25 | react/no-unescaped-entities ×25 (all in app/kg) |
loop |
4 | exhaustive-deps ×2, no-unescaped-entities ×2 |
governance |
0 | — |
estate |
4 | no-unescaped-entities ×2, exhaustive-deps ×2 |
| subtotal | 33 (29%) | |
| elsewhere | 79 | mostly components/ and legacy app/ surfaces |
The two destinations whose pages this branch will actually build —
decisions and governance — carry zero warnings today. The 25 in
evidence are all one rule in one directory (app/kg), and none of them are
in a file the consolidation touches first.
Proposed sequence
- Ratchet before repair. Record 112 as a non-growing ceiling in CI and fail on 113. This costs one CI step, needs no code change, and is the only move that stops the number rising while the console work lands. It is also the honest shape: a ratchet asserts "no worse", which is a claim the repo can actually keep, where "no warnings" is not.
- Make
app/(console)/**strict first, at zero warnings, enforced separately from the ceiling. It is new code with no inherited debt, anddecisionsandgovernancealready measure clean — so the strict lane starts satisfied rather than starting in violation. Extending it later tocomponents/ui/**(the primitives) is the natural second step, since those are the files every console page composes. - Then work the ceiling down by rule, not by file.
react/no-unescaped-entities(59, over half the total) is mechanical and safely auto-fixable; clearing it alone takes the ceiling to 53.react-hooks/exhaustive-deps(37) is not mechanical — each one is a real question about whether a dependency was omitted deliberately, and auto-fixing them can change render behaviour. Treat those as 37 separate reviews, never a bulk fix.
The exact CI change
The repo has no oxlint config — the plan's reference to "the existing
oxlint config" points at _ds/_adherence.oxlintrc.json, which lives inside
the vendored design-system bundle and governs design-system adherence, not
this app. The app lints through .eslintrc.json + next lint. So the ratchet
is an ESLint step:
# in the UI's CI workflow, after `npm ci`
- name: Lint ratchet (errors fail; warnings may not grow)
working-directory: miz-oki-command-center-ui
run: |
npx next lint > lint.txt 2>&1 || true
ERRORS=$(grep -c "Error:" lint.txt || true)
WARNINGS=$(grep -c "Warning:" lint.txt || true)
CEILING=112 # measured 2026-08-28 @ f7aac2a; lower this, never raise it
echo "errors=$ERRORS warnings=$WARNINGS ceiling=$CEILING"
if [ "$ERRORS" -gt 0 ]; then echo "::error::$ERRORS lint errors"; exit 1; fi
if [ "$WARNINGS" -gt "$CEILING" ]; then
echo "::error::warnings rose $CEILING -> $WARNINGS; fix or justify"
exit 1
fi
Two properties this deliberately keeps, per
.claude/rules/01-verification-discipline.md:
- The ceiling is a baseline, not a weakening. Lowering it as warnings are
fixed is the intended motion; raising it to admit a new warning is the
thing the check exists to catch. Any change to
CEILINGcarries the reason in the same commit. - The error branch is unconditional.
ERRORS -gt 0fails regardless of the warning count, so the ratchet can never become a way to let an error through.
Not implemented in this branch — this step is the plan, and the owner chose to record it rather than build it now.
Verification commands
cd miz-oki-command-center-ui && npm ci
npx tsc --noEmit ; echo "tsc exit=$?" # -> 0
npx next lint 2>&1 | grep -c "Error:" # -> 0
npx next lint 2>&1 | grep -c "Warning:" # -> 112
grep -n "ignoreBuildErrors\|ignoreDuringBuilds" next.config.mjs