diff --git a/link/v8.md b/link/v8.md index 355ef48..aafcfbc 100644 --- a/link/v8.md +++ b/link/v8.md @@ -1492,6 +1492,67 @@ would put a second, weaker parser of §5's key scheme in the schema. winning spelling of the spawn type token rather than inventing a display label — so the import has something `ScriptCompiler.FindTypeByName` will resolve without storing it twice. +### 12.2 What phase 8 built, and the two deletions the panel exposed + +The admin surface is one page, **Admin → Client Files**, covering all three things that come off the +operator's UO client: the creature catalogue, item and land art, and the cliloc table. One page +rather than three because they are one job — they live in the same client install, cross the same +bridge, and all change at the same moment, when the operator patches that client (org lead, +2026-09-14). The cliloc pair had had no UI at all since phase 2, which meant that on an install +where boot deliberately never calls the shard, the only way to import 67,496 names was `curl`. + +Four decisions were put before any code: + +| | Decided | +|---|---| +| Scope | **One page**, three sections, one nav row | +| The "activity view" (§14) | **The last import's own summary**, not a filtered feed. Rejected: a `prefix` filter on core's activity list plus a `ctx.activity.list` read helper — it would have made a `module-uo`-only phase a three-repo one with a `MODULE_API` bump, to re-read rows the panel can keep itself | +| A vanished key | **Held in the page, not a table** — see below | +| Import vs Update | **Two named buttons**, §6's own vocabulary, rather than one button and a `force` checkbox | + +**The review is in-session by construction, and that is why it needs no table.** The atlas persists +its equivalent (`shard_atlas_pending`) because *boot* re-parses the tree and would otherwise +re-prompt on every restart forever. An asset import only ever happens because somebody pressed a +button on this page, so the review is already in front of the person who caused it; declining is +not a decision to remember, it is not pressing the other button. What the panel adds is the +**pictures**: `body/820/a23` names nothing a human recognises, and "is it right that these +disappear?" cannot be answered from a list of keys. So `needsReview` now returns +`[{ key, file }]` and the page renders each one. + +`shard_asset_meta` gained a `last` block — what the import did, and who pressed it. It is written +**after** the rows commit, because the interesting half (how many atlas creatures resolved) does not +exist until another shard round trip has happened, and widening the rows-and-meta transaction to +cover a network call is how an import holds a write lock for the length of a timeout. It is +cosmetic, so a failure to write it is logged and swallowed rather than failing an import that has +already applied. + +#### The two defects, and why only this phase could find them + +Both are **deletions**, both predate phase 8, and both were invisible until a screen displayed the +numbers side by side on a stock rig. + +**One: every item picture reported as vanished.** The import diffs what the site holds against the +manifest, and a manifest is of one family by construction (§14 — one reply, one catalogue id). The +held side read the **whole table**. That was correct when phase 3 wrote it and stopped being correct +in phase 5, which put `static` and `land` rows in the same table: the body manifest never mentions +item art, so on the walk's rig **313 item pictures were staged for deletion** with a sentence saying +the shard had stopped offering them. The gate hid it for a while — an Update whose client files are +unchanged never reads a manifest — so the state that exposes it is the ordinary one: import, let the +warm pass run for a day, then patch the client. + +**Two: an approved vanish deleted the picture and kept the row.** `removeSprite` unlinked the file; +nothing deleted the row. So the catalogue still counted a portrait that was gone, the atlas +derivation could still point a creature page at a missing file, and the next forced import staged +the same key for review **again** — reporting "nothing was changed" about a file it had already +deleted. The removals now go into `saveAssets`'s own transaction, beside the rows and the meta. + +The third finding is smaller and the same shape: `countAssets()` was whole-table too, so the panel's +first render of a working install announced a **1,408-row creature catalogue** (1,095 portraits plus +313 item pictures) on the one screen an operator checks to see whether the import worked. + +**The rule worth keeping: after phase 5, `shard_assets` is three collections in one table, and every +read of it that feeds a decision has to say which.** Two of the three that did not were deletions. + --- ## 13. Visibility @@ -1590,11 +1651,18 @@ change every consumer *does* see, exactly as §7 intends. command explicitly and has no generic `/assets/*` forwarder, so `link` is in the phase too. The table now says so. -**Website admin** (`Admin → Shard`, admin-only): status, **Import**, **Update**, approve/reject for -a vanished key, and the existing path settings. Every action to the admin activity log, as -`shard.assets.*`. Phase 3 shipped the two that make it reachable — `GET /admin/shard/assets` and -`POST /admin/shard/assets/import`, mirroring the cliloc pair — so the phase could be accepted on a -real rig; the full panel is phase 8 (org lead, 2026-09-10). +**Website admin** — `Admin → Client Files`, admin-only, **built in phase 8** (§12.2). Status, +**Update**, **Re-import everything**, approve/decline for a vanished key, the item-art warm pass and +the cliloc path setting, in three sections on one page. Every action goes to the admin activity log +as `shard.assets.*` / `shard.clilocs.*`, and the panel additionally keeps the **last import's own +summary** beside the catalogue, because that log is one unfiltered list of every admin action on the +site and an import from three client patches ago is not findable in it. + +Phase 3 shipped the two routes that make it reachable — `GET /admin/shard/assets` and +`POST /admin/shard/assets/import`, mirroring the cliloc pair — so that phase could be accepted on a +real rig. The route surface did not grow in phase 8; what changed is that `needsReview` returns each +vanished key **with the picture the site currently serves for it**, and the status carries `linked` +and the `last` block. --- @@ -1629,7 +1697,7 @@ disagree, so a split bump means the next bundle silently fails to compose. | 5 | **DONE 2026-09-11.** Item statics and land on demand (§11.1): the `static` and `land` families, hue applied on the shard from `tiledata.mul`, the byte-bounded art cache, `assets.fetch` made family-aware, `families` on `assets.sources`. Website side: the warm pass, per-row `catalog` staleness, and pictures on the marketplace and the character sheet. **39,189 statics and 4,244 land tiles served; the only refusals are the 9,963 + 12,140 empty index slots §4.5 predicted.** Two traps found — the library's bitmap cache poisons a hued sprite, and `PartialHue` decides the picture from a file only the shard has. Protocol stays 8; `EXTRACTOR_VERSION` stays 2 | servuo-plugins, module-uo | | 6 | **DONE 2026-09-11, and not what this row said.** The measurement came first and changed the phase: a complete one-direction animation set is **174,453 frames / 281.5 MB**, not the ~119,000 estimated, and **the site displays still pictures** — so the deep keys and the bulk-fill switch were **not built** (§11.2, org lead 2026-09-11). What shipped is what the still-picture site was missing: the **73 bodies with no art at action 0 and real art deeper** (a horse at `body/820/a23`), the catalogue key carrying its action, the atlas join that reads it, and §4.10's per-body **action ceiling** — without which the fallback walk itself would serve **452 validated pictures of the next body**. Catalogue **1,022 → 1,095**; `EXTRACTOR_VERSION` 2 → 3; protocol stays 8 | servuo-plugins, module-uo | | 7 | **DONE 2026-09-14.** The atlas over the sidecar (§10); shared-filesystem requirement retired. The measurement came first again and changed the shape: `tree/