Repository navigation
ui takes the root namespace and replaces print and fail #698
Description
Epic: #445 — The Starlark authoring surface
Plan: docs/plans/ui-at-root.md
Problem
Starlark ships two output builtins — print and fail — and devlore owns neither.
A script's print resolves to starlark.Universe, is formatted by the interpreter, and is written straight
to stderr by starlark-go's default. It never reaches status.Narrator, so it is invisible to --quiet, to
any output shaping we choose, and to the diagnostics stream that #507 will introduce. fail is the same
story on the error path.
Meanwhile ui already has methods that mean exactly these things, and five more besides.
The shape of the fix
Marking the ui provider +devlore:root=true surfaces all six methods as top-level globals. Because
Starlark's resolver checks predeclared before universal (resolve.go:437 precedes :441), and
CamelToSnake maps Print→print and Fail→fail, the builtins are replaced — with no interception
code, no starlark.Universe mutation, and no Thread.Print hook.
receiver_type.go:66 already documents this case, using note() as its example. This would be the first
real use of the RoleModule|RoleRoot branch that runtime.go:125 calls reserved.
What it buys
| Today | After | |
|---|---|---|
print |
stderr, via starlark-go | status.Narrator, like everything else |
fail |
interpreter error text | narrated, then raised |
note warn error succeed |
ui.note(...) |
top-level, which is what an author reaching for print wanted |
| #507 diagnostics stream | print bypasses it |
print rides it |
Prerequisite
ui.Print(msg string) takes one string. The builtin is variadic with a sep keyword. Replacing the
builtin with a narrower signature would break every print(a, b) in the tree — so the variadic change
lands first (see the task below).
Ruling needed
Does error belong in the global namespace? note, warn, succeed, print, and fail are safe
words to claim. error is an ordinary word for a script to want as a variable, and a predeclared name
cannot be assigned over at module scope — error = compute() becomes a resolve error, not a shadow.
Options: accept it, give the global a different name while keeping ui.Error, or leave Error off root.
Out of scope, but noted
- The other builtins —
dir,getattr,hasattr,type,repr— stay the universe's. Three are
reflection over the surface, which is a real question for a hermetic runtime, and a separate one. - Eight sites construct
starlark.Threadindependently and none setsThread.Print. Irrelevant once
printis predeclared (the builtin is never reached), but any policy needing the thread hook would
have to set it eight times.
Activity
David-Noble-at-work commentedon Aug 26, 2026
Tasks: #699 (variadic signatures), #700 (root placement + call-site sweep). Sequential — the replacement must accept what the builtin accepted before it can replace it.
David-Noble-at-work commentedon Aug 26, 2026
Both rulings settled.
sepkeyword —Print(args ...any)joined with" ". Zerosep=call sites in the tree. See ui.Provider methods take variadic arguments #699.errorkeeps the global name — script authors adopterrfor the variable, as Go authors do. See Mark ui +devlore:root=true and sweep the ui.* call sites #700.Plan
docs/plans/ui-at-root.mdis updated and approved.David-Noble-at-work commentedon Aug 26, 2026
Scope narrowed: no signature changes. #699 is closed; #700 carries the whole job.
The variadic prerequisite dissolved. Both ways of widening
ui.Printto match the builtin fail:%operator already rendered —50% offbecomes50%!o(MISSING)fftoGoInto, soTrue/None/lists render astrue/<nil>/[a b]instead ofTrue/None/["a"](msg string)is the correct signature: the script renders with Starlark's ownstr()and%, and hands over a finished string. Survey confirms the tree already works this way — 64printand 65failsites, zero multi-argument, zero non-string.Remaining work is one directive, one defect fix, and a 231-site sweep. See #700 and
docs/plans/ui-at-root.md.docs(ui): one string is the correct contract for the narration method…
Providers claim what they guarantee, and a type-checked pass holds th…