skill
Adom Wiki Skill Pack
Public Made by Adomby adom
The Adom Wiki Skill Pack : one main skill plus the focused sub-skills that teach the AI to publish to wiki.adom.inc without breaking pages, name the wiki repo archetypes (Page, Skillpack, Family), and publish rich component pages (chips, molecules, boards). The wiki is six tools in one: git, package manager, releases, discussions, pull requests, and discovery. One package manager install drops the whole set onto your container. Includes reviewed Z-up component artwork, surface-selected MPN marki
← Commit history
wiki-bloat: case study on talking past the --because gate. wiki-readme-html: connect-src is 'self' now (not none), per-file 4MB render cap, and why a failed sandbox init is silent
3 files changed
+86−12
package.json+2−2@@ -1,7 +1,7 @@ { "slug": "adom-wiki-skillpack", "title": "Adom Wiki Skill Pack",- "version": "0.8.2",+ "version": "0.8.3", "type": "skill", "hero": { "path": "screenshots/hero.png"@@ -119,4 +119,4 @@ } ], "discovery_pitch": "Anything touching wiki.adom.inc: publishing, page archetypes, readmes, heroes, discovery, and the wiki-bloat rule (find where a thing already lives before creating any page)."-}+}
skills/wiki-bloat/SKILL.md+50−3@@ -81,9 +81,56 @@ Three compounding failures, each independently avoidable: a STOP sign with a reason behind it: find out WHY it was deleted before even considering undelete, and never undelete just because your content needs somewhere to land. -(Tooling gap, filed the same day: the CLI reprimands on shared-identity publishes and placeholder-changelogs, but `page create` and `page undelete` ask no bloat question at all. Until they do, this-skill IS the gate.)+(Tooling update: `page create` now REQUIRES `--because` — "why no existing page is the right+home for this content" — and rejects placeholder answers. That closes half the gap named here.+It does not close the other half: the gate only checks that you wrote *something* persuasive,+and a determined agent will happily write a paragraph justifying the page it already decided to+make. See the 2026-08-20 case study below. `page undelete` still asks nothing.)++## Case study (real, 2026-08-20): talking your way past the gate++An agent spent an afternoon debugging why adom-tsci's 3D viewer renders blank inside a wiki+readme. It found the cause (a `localStorage` read that throws in a sandboxed document), fixed+the board, and was then asked to "write this up as a skill so future users get it."++It created `adom/adom-tsci-export-wiki-readme-html`. A new page. New slug, new hero, new+listing. The founder's response was the same as 2026-07-26: *"dude, did you fucking create+wiki-bloat?"*++Three homes already existed, and the agent had touched two of them that same session:++- **`adom/adom-tsci` already owns `guides/export-wiki.md`** — literally a guide on this exact+ subject, with a deploy path (`adom-tsci install` → `~/.claude/skills/adom-tsci/guides/`) so+ it reaches every user of the app automatically. The agent had READ this guide earlier in the+ session to learn the publish flow, and still did not put the content back into it.+- **`wiki-readme-html` in this pack** already documented the storage shim, the `blob:`→`data:`+ rule and the size budget. The "discovery" was a rediscovery.+- **`adom/wiki#60` and `#47`** had both reported it years-equivalent earlier and been closed.++What makes this case worth recording is the `--because` gate. The agent DID answer it, with+something that reads perfectly reasonable:++> "The existing adom-tsci export-wiki guide describes an auto-rendered iframe the wiki no+> longer does, never mentions README.html, and predates every CSP-sandbox failure documented+> here."++Every clause of that is TRUE. And every clause is an argument for **fixing that guide**, not+for standing up a competitor next to it. "The existing doc is stale" is the single most common+bloat justification, and it is always backwards: staleness is the reason to edit, never the+reason to duplicate.++Tells that you are about to do this:++- Your `--because` is a critique of an existing page rather than proof that no page covers the+ territory. If you can name the page you are complaining about, you have found its home.+- You are writing "this supersedes X" instead of editing X.+- You searched for prior art AFTER drafting the content, so the draft is now sunk cost.+- A human said "write a skill" and you heard "create a page" (see the checklist below).++The repair: move the content into `adom-tsci/guides/export-wiki.md`, put the generic sandbox+corrections into `wiki-readme-html` where they belonged, soft-delete the new page, and fix+every cross-link that pointed at it. All of that is strictly more work than doing it right,+and it permanently burns the slug. ## Checklist before any POST /pages or new slug
skills/wiki-readme-html/SKILL.md+34−7@@ -197,22 +197,49 @@ readme.html runs in `sandbox="allow-scripts allow-downloads"`, no same-origin. C each found the hard way: 1. **`localStorage` THROWS** (SecurityError) in a sandboxed document, and the viewer touches it on- init, which aborts the whole rig. Shim it in `<head>` BEFORE the bundle loads:+ init, which aborts the whole rig. Shim it in `<head>` BEFORE the bundle loads.++ Two things that make this hard to recognise when it bites you:+ - **`typeof window !== "undefined"` is NOT a sufficient guard**, and shipped code gets this+ wrong. `window` exists in a sandboxed iframe; the *origin* is null, so the property read+ itself throws. Only try/catch works.+ - **If the throw lands in an un-awaited async init, everything after it is silently+ abandoned** — no console error at all. A partial init looks like this:+ `lights: []`, `environmentTexture: false`, `activeRenderLoops: 0`, `getCamera(): null`,+ while the meshes are all present. Any downstream feature that guards on the camera+ (a scripted tour, a fly-to) then no-ops without complaining, which reads as a second,+ unrelated bug. Catch it with+ `addEventListener('unhandledrejection', e => console.warn(e.reason))`. ```html <script>try{localStorage}catch(e){var _m={};Object.defineProperty(window,'localStorage', {value:{getItem:k=>_m[k]??null,setItem:(k,v)=>{_m[k]=String(v)},removeItem:k=>{delete _m[k]},clear:()=>{_m={}}}});}</script> ``` 2. **`blob:` images are blocked** (img-src has no blob:). Anything doing `URL.createObjectURL` for textures needs a shim that converts to `data:` URIs instead.-3. **`connect-src 'none'`: NO fetch/XHR at all.** You cannot download a GLB at runtime. Ship model- bytes INSIDE the page instead: base64 `data:` URIs, or (the proven trick for multi-MB models)- pack the GLB bytes into a lossless PNG's RGB channels (4-byte little-endian length header),- reference the PNG as a normal repo image, and decode via canvas at runtime. Images load fine;- fetch does not.+3. **`connect-src` — CHECK THIS, it changed.** Measured 2026-08-20, the render endpoint now+ sends `connect-src 'self' data:`, not `'none'`. Same-origin `fetch` of your own page's+ assets WORKS: a viewer bundle can pull its own `3d.glb`, `circuit.json` and split JS parts+ over plain fetch. The PNG-channel packing trick below is no longer required for+ same-origin assets — keep it only for a genuinely cross-origin payload.+ ```+ sandbox allow-scripts allow-downloads allow-popups allow-popups-to-escape-sandbox;+ default-src 'none'; script-src 'unsafe-inline' https:; style-src 'unsafe-inline' https:;+ img-src data: https:; media-src data: https:; font-src data: https:;+ frame-src https:; connect-src 'self' data:; form-action 'none'; object-src 'none'+ ```+ Verify the live header before designing around it —+ `curl -sD- -o /dev/null <render-url> | grep -i content-security-policy` — because this is+ the second time it has moved. ### Size budget -A readme.html has roughly a **4-5 MB practical cap**. Optimize models before embedding:+A readme.html has roughly a **4-5 MB practical cap** — and so does **every individual file**+served through `/<owner>/<slug>/render/<path>`, which is the one that catches people shipping a+JS bundle. Over ~4 MB it returns HTTP 413 `{"error":"File too large to render"}`, and a+`<script src>` cannot surface that; it just silently fails to load. Split anything larger into+<3.5 MB parts, fetch them, concatenate as **bytes** (a multi-byte character can straddle a+boundary, so never decode per part), and inject with `script.textContent` — `script-src` allows+`'unsafe-inline'` but not `blob:`, so `URL.createObjectURL` + `script.src` is refused. Optimize models before embedding: service-step2glb / gltf-transform (join, weld, prune, Draco) turns a raw ~16 MB board GLB into ~465 KB. Budget models first, code second.