Spec: docs/superpowers/specs/2026-08-23-web-ui-rewrite-design.md § What this deletes.
Depends on #537 (parity) and #539 (glossary link).
keel/web/render.py is 872 lines of server-side HTML. Once the client renders from #534's API, the server does no rendering — as in the reference architecture, its job is static files with correct headers plus JSON.
The ordering constraint
#533's API pins must exist before these HTML pins are removed. tests/commands/test_console_thinness.py currently pins keel/web/ with the same rules it applies to the console layer, and that property — "a THIRD renderer over the same reports, never a second place that computes them", as render.py's own docstring puts it — must transfer to the API layer rather than lapse in the gap.
Deleting the renderer before the replacement is pinned would leave a window where nothing enforces the invariant, which is precisely the invariant worth keeping.
Scope
keel/web/security.py is untouched. All five of its layers stay exactly as they are.
Acceptance
Spec:
docs/superpowers/specs/2026-08-23-web-ui-rewrite-design.md§ What this deletes.Depends on #537 (parity) and #539 (glossary link).
keel/web/render.pyis 872 lines of server-side HTML. Once the client renders from #534's API, the server does no rendering — as in the reference architecture, its job is static files with correct headers plus JSON.The ordering constraint
#533's API pins must exist before these HTML pins are removed.
tests/commands/test_console_thinness.pycurrently pinskeel/web/with the same rules it applies to the console layer, and that property — "a THIRD renderer over the same reports, never a second place that computes them", asrender.py's own docstring puts it — must transfer to the API layer rather than lapse in the gap.Deleting the renderer before the replacement is pinned would leave a window where nothing enforces the invariant, which is precisely the invariant worth keeping.
Scope
render.py's HTML generation and its inline stylesheet — the palette moves to a real stylesheet (Palette: profit and loss are the same brightness, and inputs have no visible border #532 owns its values).keel/web/server.py, leaving static serving (Static asset serving and the full security header set #535) and the JSON API (JSON API over the existing reports, with server-side sort and /api/config #534).keel/web/security.pyis untouched. All five of its layers stay exactly as they are.Acceptance
render.pyno longer generates HTML; the module is removed or reduced to nothing that renders.keel/web/server.pyserves static assets and JSON only.test_console_thinness.pypins the API layer, and the pin count does not drop.security.py's suite passes unmodified.keel serveopens a working browser UI with no server-rendered HTML in the response.keel/commands/gained rendering logic as a side effect of the move.