A spreadsheet’s formula functions, contributed by add-ins
A spreadsheet evaluates formulas such as =SUM(1, 2) by finding the function a name stands for. The built-ins ship with it; add-ins the user enables bring more (a units add-in, an inventory add-in), and the formula engine must find them without knowing any of them, just as the lesson’s renderer finds a plugin’s shortcode. Each level starts from the previous level’s finished code, so you can begin at any of them.
Support tickets show users confused by add-ins that half work: the inventory add-in’s Max was refused, so its REORDER is there but the sheet it shipped with, which relies on both, gives wrong totals. Product has decided an add-in is installed whole or not at all. Users also ask where a function comes from, so the picker shows each function with its owner: built-in, or the add-in’s id.
Today a user gets part of an add-in with no visible sign that the rest is missing, and when two add-ins compete for a name, nobody can tell from the picker which one won.
Reproduce new Workbook([inventory with REORDER and Max]): today REORDER evaluates to 3 and the picker shows “MAX, REORDER, SUM”. It should refuse the whole add-in (“inventory refused: MAX is taken by built-in”), so REORDER is #NAME?, and the picker should show “MAX (built-in), SUM (built-in)”.
Acceptance criteria An add-in is installed whole or not at all: if any of its names is taken, none of its functions is registered, and setup records one line, “add-in refused: NAME is taken by OWNER”, naming the first taken name in the add-in’s order. An add-in that names one function twice, in any spelling, is refused the same way, as taken by itself. The picker lists every function as “NAME (owner)”, in name order; the owner is built-in or the add-in’s id. Everything REGISTRY-101 promised still holds: names ignore case, the engine knows no function, a refused add-in leaves the others in place, and an unknown name is #NAME?. Notes Cases print the same steps as REGISTRY-101: values, #NAME?, the picker’s list joined by “, ”, or what setup refused joined by “; ” (none if nothing). Keep the AddIn and Formula types, the Workbook’s name, how it is opened, and the names of evaluate, functionNames and refused (Go: OpenWorkbook, Evaluate, FunctionNames, Refused; Rust: Workbook::open, evaluate, function_names, refused); the checker calls them.