Harden Druks UI and make it the scaffold default - #350
Merged
czpython merged 1 commit intoAug 30, 2026
Merged
Conversation
czpython
force-pushed
the
commonzenpython/eng-906-harden-druks-ui-and-make-it-the-scaffold-default
branch
from
August 29, 2026 22:55
a41a35a to
512d723
Compare
czpython
force-pushed
the
commonzenpython/eng-905-add-forms-fields-and-operation-backed-actions
branch
9 times, most recently
from
August 30, 2026 12:18
9ef3f0c to
530b373
Compare
Base automatically changed from
commonzenpython/eng-905-add-forms-fields-and-operation-backed-actions
to
main
August 30, 2026 12:21
czpython
force-pushed
the
commonzenpython/eng-906-harden-druks-ui-and-make-it-the-scaffold-default
branch
from
August 30, 2026 12:24
512d723 to
f3ff32d
Compare
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
czpython
force-pushed
the
commonzenpython/eng-906-harden-druks-ui-and-make-it-the-scaffold-default
branch
from
August 30, 2026 12:28
f3ff32d to
f94ce07
Compare
czpython
force-pushed
the
commonzenpython/eng-906-harden-druks-ui-and-make-it-the-scaffold-default
branch
from
August 30, 2026 12:35
f94ce07 to
1f3edfe
Compare
A scaffolded app now gets a landing page in Python, a navigation entry, and no JavaScript: no Node, no React, no dist/. Its AGENTS.md states the page-purity contract and points at the UI contract. An app that needs full control of its interface still ships an ESM frontend, which the author guide describes as the escape hatch it is. A page that fails now says which page. A page function that raises, or a page that names an operation the app does not declare, answers PAGE_FAILED with the app and the page named, and so does a page whose shape its own contract cannot carry. The traceback stays in the process log, and the shell keeps the failure inside the app surface with a retry. The proof app gained a catalog page carrying one of every block, value, and field. backend/tests/test_ui_contract.py reads it through HTTP and compares what it renders against the unions themselves, so a name added to druks.ui with no example fails the suite. frontend/src/druksui/accessibility.test.tsx renders the same catalog and holds every renderer to alternative text, labelled inputs, named keyboard-reachable controls, a spoken progress state, the chart's own table, and real column headers. The author guide states what V1 leaves out and what is demand-pulled, and the development guide and the overview point at the one canonical explanation.
czpython
force-pushed
the
commonzenpython/eng-906-harden-druks-ui-and-make-it-the-scaffold-default
branch
from
August 30, 2026 12:42
1f3edfe to
b88e3ec
Compare
czpython
deleted the
commonzenpython/eng-906-harden-druks-ui-and-make-it-the-scaffold-default
branch
August 30, 2026 12:48
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ENG-906
Server-driven pages are now the path a new app takes by default, and the
surface is held to its own contract by tests that read the same wire snapshot
from both sides.
The scaffold
druks create appwritespages.pywith a landing page,navigationnamingit, and no JavaScript: no Node, no React, no
dist/. The generatedAGENTS.mdstates the page-purity contract and points at the UI contract, and the
generated suite renders the landing page. An app that needs full control of its
interface still ships an ESM frontend, which the author guide now describes as
the escape hatch it is — and says the author creates that
dist/themselves.When a page fails
A page read that goes wrong answers
PAGE_FAILEDand names the app and thepage. What the app's own code said never reaches the browser — not its message
and not its type, either of which can carry a query, a URL, a credential, or
the name of a library. That stays in the process log. Druks says which of three
things happened: the page raised, it answered with something that is not a
Page, or it named an operation the app does not declare. The shell keeps allthree inside the app surface with a retry.
An
Actionexists only once a page function has run, so its reference ischecked when Druks builds the page — the earliest moment it exists. Everything
knowable earlier still fails the load: a repeated
operation_id, one routeanswering two methods, a page table no request could resolve, a navigation
entry that is not a static top-level page, a nested child, and a router on a
reserved segment.
Held to the contract
The proof app gained a catalog page carrying one of every block, value, and
field, and the exact wire it serializes is checked in as
frontend/src/druksui/catalog.json. The backend suite compares the live HTTPresponse against that file and against the unions themselves, so a name added
to
druks.uiwith no example fails. The renderer's own suite renders that samefile. A field renamed on one side and not the other fails a suite instead of a
browser, and the backend workflow now runs when the renderer or the wire types
change.
That suite also holds every V1 renderer to alternative text, labelled inputs,
controls that are named and take focus, a spoken progress state, the chart's
numbers in a table, real column headers, and form-scoped control ids so two
forms sharing a field name keep their labels and radio groups apart.
How it is verified
Gates run:
uv run ruff check backend,uv run ruff format --check backend,npm --prefix frontend run lint,npm --prefix frontend test(209 testspass),
npm --prefix frontend run build.uv run pytest backend/did not run:this machine has no Postgres or Redis. The scaffold, page-table, block, action,
and page-failure tests that need no database were run directly, and all pass.
Two limits worth naming. The accessibility suite runs in jsdom, so it holds
focus order, names, and roles, but a browser is still what would prove visible
focus, the dark theme, and a narrow viewport. And the proof app is the
standalone package fixture the contract tests read: this repository still ships
it, so a second one would only duplicate it.