Pages and the Workbench¶
Pages are ordinary Textual widgets owned by the application using Groundskeeping. The shell mounts them once, activates and deactivates them as the operator moves between tabs, and preserves page-local state.
A page receives a narrow PageContext. It does
not receive the whole app.
The workbench surface¶
The default page surface is the workbench:
- flat section navigation or a hierarchical catalogue on the left;
- rows or tree content on the upper right; and
- selected detail on the lower right.
Use SectionNavigation for peer areas such as Database, Embeddings, and Runtime. Use
CatalogueNavigation when hierarchy is the point, such as a provider with nested models or an
evaluation run with nested artefacts.
Translate domain objects before they reach the workbench. The workbench understands
SectionItem, CatalogueItem, TableView, SelectionTableView, TreeView, and friends;
it does not need to know what a Groundworkers resource or cava-nlp-shard evaluation object
is.
Use OperatorAppSpec.workbench_labels to rename shared pane chrome such as the result and
detail panel labels. Page-owned titles still live on navigation and view contracts: for
example, SectionNavigation.title names the left pane for a specific page, and
TableView.title names the current result content.
Routing¶
PageRoute is the navigation identity for one page: a key, an operator-facing label, and
a purpose line rendered beside the heading. PageRegistry validates that keys are unique
and that at least one route exists, and OperatorAppSpec.validate additionally rejects
duplicate page factories and unknown default_page keys.
Validation happens at construction, not at first navigation, so a misconfigured app fails at startup rather than when an operator clicks a tab.
Page lifecycle¶
| Method | When the shell calls it |
|---|---|
activate |
The page becomes the visible tab |
deactivate |
The operator moves to a different tab |
build_navigation |
Each time the page is rendered, to populate the left pane |
landing_view |
Each time the page is rendered, to populate the upper-right pane |
navigation_selected |
A section or catalogue item is selected |
action_selected |
A command button in the current view is pressed |
row_highlighted |
A result-table row is highlighted |
row_selected |
A result-table row is selected |
Row events return to the active page. The workbench renders generic models; the page decides what a highlighted row means.
Use SelectionTableView when rows are controls, not just data. It renders a
Groundskeeping-owned selection list with stable row keys, disabled row handling, and
selection modes for single, multiple, or all-vs-specific selection. Pages that need the full
selected-key state can implement the optional SelectionAwareOperatorPage.selection_changed
hook; older pages still receive row_selected for compatibility.
Detail panes can render TextView, KeyValueView, or TableView. Use a detail TableView
when the selected item has its own repeated data, such as available LLM models for a provider.
If landing_view raises, the shell catches the exception and renders an EmptyView
explaining that the page could not be rendered. A page that cannot build its landing content
degrades to a message instead of taking down the app.
Setup pages¶
A setup page should answer a practical operator question: "is this environment ready for the work I am about to run?"
The page normally lives in the application using Groundskeeping. It can call whatever services that application already has for config, credentials, model providers, database checks, or runtime health.
A good setup page usually has:
- a flat list of setup areas, such as config, database, runtime, model server, paths, or credentials;
- a landing
TreeViewsummarising overall readiness; - a
TableViewfor repeated checks where scanning matters; KeyValueViewdetail for the selected check;- one or two safe verification actions; and
- an operation policy that describes effects in the application's own vocabulary.
Start read-only. A Test connection or Refresh status button is often enough to prove the page shape before you add durable writes.
When setup requires guided edits, expose a ViewAction from the current view and open a
wizard with PageContext.open_wizard. The page still owns what the wizard means;
Groundskeeping only renders the navigation, fields, review state, and final result.