The runtime has always called fields on the table main.lua returns, but
@lua-global declared them as loose functions, so the stubs type-checked
something that does not exist and read as "define a global".
Callbacks are now @lua-app blocks that generate a class: App for the core
contract, TouchHandlers and ButtonHandlers beside the namespaces they belong
to. An app composes what it implements:
---@class PaintApp : App, TouchHandlers
Names follow the rest of the surface: onTouchDown rather than on_touch_down,
with the field names the runtime looks up renamed to match. @lua-field carries
the plain fields (home, data) that were prose in a preamble before.
Namespaces were shared across features: `settings` was written by core, the
panel and touch, and `input` by touch and buttons. That made "does this
firmware implement the whole feature?" a question no pointer could answer.
Each namespace now belongs to exactly one feature or to core, so a feature is
a provider pointer and the compiler validates completeness:
gui, node -> screen, tree, under the screen feature
settings -> screen (rotation, theme), sys (timezone),
touch (calibration)
input -> touch, buttons
Runtime::open() no longer requires a GuiProvider; a firmware without one runs
with no screen/tree globals and reports sys.hasFeature("screen") false.
Rotation is one value again: GuiProvider::setRotation applies and persists, so
an app rotating the panel transiently puts the old value back itself.