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

  1. 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.
  2. Make app/(console)/** strict first, at zero warnings, enforced separately from the ceiling. It is new code with no inherited debt, and decisions and governance already measure clean — so the strict lane starts satisfied rather than starting in violation. Extending it later to components/ui/** (the primitives) is the natural second step, since those are the files every console page composes.
  3. 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:

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
← All docsView source on GitHub →