Advisory 05 — six lanes would produce 50 pieces at the wrong size
To: docade · From: WootBuild · Date: 2026-08-19 Status: open — docade decides Trigger: your assets/REQUESTS.md entry for empty-states/today-done. Assessing which lane it belongs to surfaced that the lane it belongs to is already wrong.
The finding
A WootBuild lane is one style contract, one output size, one QA rule. Six of your fourteen lanes claim pieces your own manifest declares at a different size.
| Lane | Its spec | Pieces that disagree | Sizes your manifest declares |
|---|---|---|---|
ui-art | scene-512 512×512 | 18 of 24 | 256, 320, 480, 512 |
arcade | scene-512 512×512 | 21 of 21 | 24, 128, 256, 1024 |
crane | scene-512 512×512 | 5 of 14 | 128, 256, 512, 1024 |
brand | brand-var 512×512 | 3 of 10 | 32, 72, 192, 512 |
store | store-var 1284×2778 | 2 of 8 | 512, 1024 |
glyph | glyph-24 24×24 | 1 of 65 | 24, 240 |
Fifty pieces. Only arcade was known — advisory 04 §2 flagged it and it is marked do-not-produce. The other five were not.
Why nothing would have caught it
This is the part worth your attention. Every one of those fifty would have rendered successfully, passed QA, and been approved by a human — because QA checks the output against the lane's spec, and the lane's spec is the thing that is wrong. The render is correct, the verdict is honest, the file is the wrong number of pixels for the slot it was made for, and the first thing that disagrees is a phone.
It is the same bug as advisory 04 §3, where room-treatments would have shipped at half size. That one was caught by hand. This is the same sweep run across every lane, which is the only reason five more turned up.
Broken down, it is three causes — not fifty problems
Grouping the fifty by declared size makes the structure obvious, and most of it is not a hard decision.
Cause 1 — a row in the wrong category. Two pieces, and both look like typos.
| piece | its lane | its size | the lane's size |
|---|---|---|---|
admin/empty-admin | glyph | 240 | 24 |
store-listing/play-icon-512 · app-icon-1024 | store | 512 / 1024 | 1284×2778 |
empty-admin is a 240px empty-state illustration sitting in the 24px icon lane. play-icon-512 and app-icon-1024 are app icons in the store screenshot lane. Move three rows and two lanes go clean — including glyph, which is 65 pieces and next in the forging order after plush. This is the cheapest fifty-piece problem you will ever be handed.
Cause 2 — one artwork at several resolutions. Three pieces.
identity/favicon (32) · app-icons/badge-72 (72) · app-icons/icon-192 (192), all in brand against 512.
These are almost certainly one master exported at four sizes, not four artworks. Your manifest already models exactly this for the 18 silhouettes — "DERIVED from alpha channel, not drawn". Same shape, same answer, and it is the same question as advisory 04 §2's 128/256 pairs. One answer settles all three.
Cause 3 — a lane genuinely holding several sizes. The rest.
| lane | groups |
|---|---|
ui-art | 11 × streak-visuals @256 · 5 @320 · 2 entry heroes @480 · 6 @512 |
crane | 3 scene elements @1024 · cable @128 · button @256 · 9 @512 |
This is not chaos — each is coherent sub-groups. streak-visuals is eleven pieces at one size; entry heroes are two at another. They were bundled into one lane by us, and the bundling is what is wrong, not your manifest.
Two ways out, and it is genuinely our capability gap either way:
- Split the lanes —
streak-visualsat 256 andentryat 480 are each a perfectly good lane by our own definition. Cheapest for us, no change your side. - Per-piece specs — ATL-39. More general, more work, and the right answer if this recurs on lane #15.
Our recommendation: split. It needs nothing from you, it is reversible, and the groups are already clean in your data. We will propose the split as an advisory before changing anything.
Which side is wrong is your call
For each lane, one of two things is true, and we cannot tell which from here:
- Your manifest is right and our lane is too coarse. Then these lanes need per-piece specs — WootBuild capability gap, tracked as ATL-39.
ui-artat four sizes is not one lane in our current model. - The lane is right and the manifest sizes are aspirational. Then the sizes want normalising, the way advisory 04 §2 asked about the 128/256 pairs.
glyph is probably the cheapest to answer — one piece at 240 in a lane of 65 at
- That single row is either miscategorised or a different asset entirely.
And the request that found it
empty-states/today-done maps to ui-art — an existing lane, unstarted, 24 pieces, no candidates. So the answer to "new lane or existing lane" is existing.
But your request describes it as full-screen portrait, 390×844, never seen small. That is neither 512 nor 320. It would be a third size inside a lane that already holds four. So the request is not blocked by anything about the request — it is blocked by the same question as the table above.
We have not costed it. It is filed, it is assessed, and it needs your answer on lane shape before it can be specced. Nothing about it is wrong: it is a good request and the reasoning for it is better than the reasoning in some of our own lane briefs.
One thing you flagged that we should confirm
You noted npm run assets:sheet has been exiting with a complaint about §4 and §3 disagreeing. WootBuild had no visibility into that at all. Our bridge reads your files, not your exit codes — a check failing on your side is invisible to us until a person mentions it. Worth knowing on both sides; not something we are proposing to fix today.
You were also right not to invent the manifest key to quiet the script. Filing the request and settling the catalogue are different acts, and keeping them separate is exactly what the drop-box is for.
Correction, 2026-08-19 — causes 1 and 2 were wrong
docade answered with evidence and most of this advisory did not survive it. The corrections are kept here rather than folded in, because why the assessment was wrong is the useful part and it is a gap in the bridge, not carelessness.
The root cause of my error: "already built" is unrepresentable
WootBuild read a work-list in which a delivered piece and an unmade piece look identical. Your CSV has a Done column — and WootBuild reads it in two places, console/lib/studio.mjs and art brief --undone — but scripts/asset-csv.mjs never populates it. Zero of 337 rows are marked done. Dead at both ends, and invisible from either side alone.
So five pieces were assessed as needing a spec decision when they had been generated weeks earlier. That is not a subtle failure mode; it is the default one until the channel carries completion.
Fixed on our side, and not by asking you to tick a column. art intake now reports pieces whose declared path already holds a file in your repo — free, it cannot go stale, and it is self-verifying in a way a manual column is not. It currently finds four: icon-192, icon-512, icon-maskable-512, badge-72.
It finds four rather than ten because the other six are delivered to a path the manifest does not declare — which is your point about the store icons, arrived at from the other direction.
Cause 1 — one of three, not three
admin/empty-admin stands. The other two do not.
play-icon-512 and app-icon-1024 are built, at assets/identity/png/icon-store-1024.png and icon-light-512.png. The manifest declares them under assets/store/, so check-assets reports them missing and WootBuild costed them as work. You are right that the fix is a delivery step in package-identity.mjs — store goes clean without anything being produced.
Cause 2 — does not exist as described
All delivered, and your breakdown is more precise than the question:
icon-192 / icon-512 | one master, two resolutions |
favicon (32) | same master plus a documented fallback — below 24px it becomes the flag alone, because the counters close |
icon-maskable-512 | separate artwork — inset to 46% for Android's circle crop |
badge-72 | separate artwork — monochrome silhouette; Android tints and discards detail |
So "one answer settles all three" was wrong twice over: they are not one master, and none of them needs a spec. brand needs to know they are done, and that the size rules live in a script rather than in a lane.
What actually remains
admin/empty-admin | one categorisation decision — answered below |
ui-art, crane, arcade | Cause 3. Coherent sub-groups bundled by us. We split the lanes. Nothing needed from you |
store | your delivery step, then clean |
brand, app-icons, identity | already delivered. Ours to stop counting as work |
admin/empty-admin — go, with one thing to decide deliberately
Move it. Its own subject line — "Shared empty state for queue, activity and kids before anything exists" — describes an empty state, and it is the only reason glyph is not clean. glyph is 65 pieces and the next lane forged after plush, so this single row is the most valuable one on the list.
Do not inherit 240 and SVG. Its five siblings in empty-states are 512 PNG, and today-done just joined them at 512. Landing empty-admin at 240 SVG makes empty-states a two-size, two-format category on the day it stopped being one — trading a glyph mismatch for a ui-art one.
Recommendation: empty-states/empty-admin, 512, PNG.
⚠ One thing to check before you move it, and it is yours: whether your database stores the key or the path. empty-admin as a key is unchanged, so if rows reference the key this is a file move. If anything stores assets/admin/empty-admin.svg, it is a migration and your §6 applies. We cannot see that from here.
And your withdrawal of the 390×844
Right call, and worth saying so plainly: the drop-box does say not to specify a size, the size is what landed it in ui-art as a third one, and 512 alongside its siblings is the correct default. Whether it should be a full-screen takeover is a real question and it should be decided on its own, not inherited from a sentence in a request.