← Commit history

2.1.4 (doc only, module bytes unchanged): VENDOR the module, never read it from the pack path; the boot-death heuristic I published was wrong; console-origin and commit-after-mount traps. 2.1.3 shipped this note without its content.

John Lauer ·2fda5d8a65 ·1mo ago ·parent 61a5a30
2 files changed +39−5
package.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "adom-ui-design",-  "version": "2.1.3",+  "version": "2.1.4",   "type": "skill",   "description": "The Adom UI Design System plus a bundle of focused UI skills (tooltips, floating-windows, toasts, responsive-ui, app-shell, ai-driven-apps, clickable-paths, dark-default, input-feedback, settings-layout). One install drops the whole set of skill files onto your container, plus shared icon assets (the settings cog) so every Adom app draws the same glyph.",   "dependencies": {},
skills/newbie-panel/SKILL.md+38−4
@@ -55,6 +55,25 @@ column. Every one of those apps had read this skill. **A written spec is not a m </script> ``` +### ⛔ VENDOR THE FILE INTO YOUR OWN PACKAGE. NEVER READ IT FROM THE PACK PATH.++Copy `newbie-panel.js` into your app's own package and serve it from there. **Do not point a+script tag, a route, or a file read at `~/.claude/skills/adom-ui-design/assets/module/`.**++This is not tidiness. On 2026-08-25 a background fleet auto-update **downgraded the pack to+1.41.0, whose `files[]` has no module entry, so `assets/module/` ceased to exist.** nb watched+it happen between two commands: it verified 2.1.2 at the pack path, and seconds later the path+was gone. The registry still served the file and the latest published version was still 2.1.2;+only the installed copy reverted. **An app reading the module live from the pack path loses it+to an update it never ran, and the symptom is a dashboard with no panel and no error.**++nb's own dashboard was unaffected, because it vendors the module and serves it from `vendor/`.+Its served bytes stayed correct and hash-verified while the pack path underneath it+disappeared. That is the whole argument.++So the supply chain is **pack -> vendored copy in your package -> the route you serve**, and+you hash all three. The pack is where you GET the module; it is never where you READ it.+ ### SERVE IT AS A FILE. DO NOT INLINE IT.  Give it its own route (`/newbie-panel.js`) and `<script src>` it, even when your dashboard is@@ -161,9 +180,15 @@ Deleting your hand-rolled panel is the part that breaks pages, not mounting the    removed `#btnDemo`, and a surviving top-level `$('#btnDemo').onclick = ...` threw on null    and killed the ENTIRE boot script. No panel, no status pills, no surfaces, and the page    looked "empty but fine". It cost several cycles chasing the module for a fault in the-   page. **The tell that splits it in half in one query: check whether the page's OTHER boot-   steps ran.** Zero status pills means the boot died; pills but no panel means the mount-   failed. Two different bugs, and they look identical on screen.+   page. **Two different bugs look identical on screen, and the obvious tell is WRONG.** The+   first version of this rule said: zero status pills means the boot died, pills but no panel+   means the mount failed. parts-search then hit exactly this bug and its page showed **four+   pills and no panel**, which by that rule sends the investigator straight at the module when+   the boot had in fact died. Pills survive because they are scheduled by an earlier statement+   than the state poll, so a top-level throw between the two leaves a page **half-booted**+   rather than dead. **The reliable split is whether the LAST boot statement ran:** set a+   marker at the end of boot and check it, or check that your poll interval exists. Never+   infer boot health from any single rendered region. 2. **DO NOT MAKE THE PANEL WAIT ON AN API CALL.** Phrases are static config. kicad's first    wiring fetched them from its own API, which at first paint runs before the target is    resolved, returns empty, and renders no panel at all. Inject them at serve time@@ -178,7 +203,16 @@ Deleting your hand-rolled panel is the part that breaks pages, not mounting the    selectors return nothing: pup checked `.nbband` false and `table.prompts` false on the    EVALUATED template rather than in the source; project-manager checked that `say-row`,    `demo-btn`, `nbSetOpen`, `nbRender` and `sayCopy` all have zero matches left.-5. **Hash the bytes you actually SERVE**, not the ones you vendored. nb's first mount was+5. **THE PAGE'S CONSOLE BELONGS TO THE INSTANCE THAT SERVED IT.** parts-search read its own+   container copy's `/console`, saw zero messages, and concluded nothing was being logged. The+   mount error had been logged the whole time, on John's box, by the workspace install that+   actually served the page. It cost three publishes. **Read the page's console through the+   webview, never by curling your own port.**+6. **Do not commit state before the mount returns.** parts-search's `renderSay` set the phrase+   list BEFORE mounting, so when the first attempt threw, every later poll short-circuited on+   the equality check and never retried, while a bare `catch` swallowed the reason. Commit+   only after `mount()` returns.+7. **Hash the bytes you actually SERVE**, not the ones you vendored. nb's first mount was    correct only by luck: it fetched from the repo while the pack was empty, so its bytes    happened to be right while the install path was broken. `curl` your own route back and    compare its sha256 to the pack's.