feat(asset-bridge): the 73 bodies action 0 could not see, and the ceiling that makes looking safe (Phase 6) #33

Merged
whitlocktech merged 1 commits from feat/asset-bridge-p6 into edge 2026-09-14 06:15:56 +00:00
Member

What & why

Phase 6 of the Asset Bridge (docs/link/v8.md §4.10, §11.2 — protocol 8). Not the phase §16 planned: the measurement came first and the org lead's scope answer (2026-09-11) removed most of it — the site displays still pictures, so deep frame keys and the bulk-fill switch were not built. Docs PR has the numbers.

What shipped is what the still-picture site was missing.

The catalogue looks past action 0. 73 of this client's bodies have no art there and real art deeper — body 820's first drawn action is 23, and it is a horse. They were absent from the catalogue and rendered as text on the bestiary. The catalogue now takes the first action that has art and the key names that action (body/820/a23), so it is still exactly one row per body and the row says which picture it is. 1,022 → 1,095.

The action ceiling (§4.10), without which the fallback walk is the bug. A body's actions are a contiguous band and the next body's slots begin immediately after, so one action past the ceiling is a real record of a different creature. Measured over the 795 legacy bodies:

Asking one action past the band Bodies
Refused by phase 0's validator 152
Passes every check and decodes 643
...byte-identical to body+1's action 0 452

Body 1 action 22 is an ettin, body 3's is an imp — confirmed by rendering them beside the body they belong to. Nothing about those records is defective, so the only defence is to refuse the address: the ceiling lives in BridgeAssetValidator.ResolveAnimation, where every caller already goes.

The ceiling is the banding, never Animations.GetAnimLength. That function disagrees with the index arithmetic on exactly one body of this client — a body reaching file type 5 as id 34 owns 13 actions while it answers 22 — and the gap is nine actions of somebody else's art.

A fetch serves only the key the catalogue chose. body/820/a0 and body/400/a2 come back unsupported with the chosen action alongside, never by decoding what was asked for.

EXTRACTOR_VERSION 2 → 3 (unchanged input, a different answer). Protocol stays 8action on a row is additive. link needs nothing.

How it was tested

Compiled (dotnet build Scripts.csproj -c Release — 0 warnings, 0 errors), then the real rig: sidecar, then ServUO, then REST.

  • GET /assets/sourcesextractorVersion 3, families body/land/static

  • GET /assets/manifest?family=body1,095 rows in one page, 1,991 ms (cold scan of all 2,047 bodies was 1,409 ms before this phase), every row carrying action, 73 of them non-zero

  • POST /assets/fetch

    key result
    body/820/a23 ok, 68×69, and it is a horse (looked at, per §4.5)
    body/820/a0 unsupported, action: 23
    body/400/a2 unsupported, action: 0
    body/1/a22 unsupported, action: 0
    body/400/a0/f3 unsupported
  • The ceiling directly: body 1 a21 resolves, a22 refused; body 400 a34 resolves, a35 refused; body 276 (→ 34 in anim5) a12 resolves, a13 refused.

  • All 73 fallback sprites rendered and inspected. The 7 legacy ones and ~12 UOP ones are recognisable creatures (horse, spider, dragon, phoenix, turkey); most of the remaining UOP ones are thin flat object/equipment sprites — correct by construction (a UOP entry is addressed by a name carrying body and action, and the payload re-declares the body) and never surfaced unless a spawn file names them.

Checklist

  • I have read CONTRIBUTING.md.
  • The change builds and existing tests/checks pass locally.
  • I have added or updated tests/docs where it makes sense.
  • My commits are reasonably scoped with clear messages.

AI-assisted contributions (required)

  • AI tools were used. Tool(s): Claude Code (Opus 5). I have reviewed and understand every change, and take responsibility for it. AI-authored commits are marked with a Co-Authored-By trailer.

🤖 Generated with Claude Code

https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4

## What & why Phase 6 of the Asset Bridge ([`docs/link/v8.md`](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/link/v8.md) §4.10, §11.2 — protocol 8). **Not the phase §16 planned**: the measurement came first and the org lead's scope answer (2026-09-11) removed most of it — the site displays still pictures, so deep frame keys and the bulk-fill switch were not built. Docs PR has the numbers. What shipped is what the still-picture site was missing. **The catalogue looks past action 0.** 73 of this client's bodies have no art there and real art deeper — body 820's first drawn action is 23, and it is a horse. They were absent from the catalogue and rendered as text on the bestiary. The catalogue now takes the first action that has art and **the key names that action** (`body/820/a23`), so it is still exactly one row per body and the row says which picture it is. **1,022 → 1,095.** **The action ceiling (§4.10), without which the fallback walk is the bug.** A body's actions are a contiguous band and the next body's slots begin immediately after, so one action past the ceiling is a real record of a different creature. Measured over the 795 legacy bodies: | Asking one action past the band | Bodies | |---|---| | Refused by phase 0's validator | 152 | | **Passes every check and decodes** | **643** | | ...**byte-identical to body+1's action 0** | **452** | Body 1 action 22 is an ettin, body 3's is an imp — confirmed by rendering them beside the body they belong to. Nothing about those records is defective, so the only defence is to refuse the address: the ceiling lives in `BridgeAssetValidator.ResolveAnimation`, where every caller already goes. **The ceiling is the banding, never `Animations.GetAnimLength`.** That function disagrees with the index arithmetic on exactly one body of this client — a body reaching file type 5 as id 34 owns 13 actions while it answers 22 — and the gap is nine actions of somebody else's art. **A fetch serves only the key the catalogue chose.** `body/820/a0` and `body/400/a2` come back `unsupported` with the chosen action alongside, never by decoding what was asked for. `EXTRACTOR_VERSION` **2 → 3** (unchanged input, a different answer). Protocol stays **8** — `action` on a row is additive. `link` needs nothing. ## How it was tested Compiled (`dotnet build Scripts.csproj -c Release` — 0 warnings, 0 errors), then the real rig: sidecar, then ServUO, then REST. - `GET /assets/sources` → `extractorVersion 3`, families `body/land/static` - `GET /assets/manifest?family=body` → **1,095 rows in one page, 1,991 ms** (cold scan of all 2,047 bodies was 1,409 ms before this phase), every row carrying `action`, 73 of them non-zero - `POST /assets/fetch` → | key | result | |---|---| | `body/820/a23` | ok, 68×69, and **it is a horse** (looked at, per §4.5) | | `body/820/a0` | `unsupported`, `action: 23` | | `body/400/a2` | `unsupported`, `action: 0` | | `body/1/a22` | `unsupported`, `action: 0` | | `body/400/a0/f3` | `unsupported` | - The ceiling directly: body 1 a21 resolves, a22 refused; body 400 a34 resolves, a35 refused; body 276 (→ 34 in anim5) a12 resolves, a13 refused. - All 73 fallback sprites rendered and inspected. The 7 legacy ones and ~12 UOP ones are recognisable creatures (horse, spider, dragon, phoenix, turkey); most of the remaining UOP ones are thin flat object/equipment sprites — correct by construction (a UOP entry is addressed by a name carrying body *and* action, and the payload re-declares the body) and never surfaced unless a spawn file names them. ## Checklist - [x] I have read [CONTRIBUTING.md](CONTRIBUTING.md). - [x] The change builds and existing tests/checks pass locally. - [x] I have added or updated tests/docs where it makes sense. - [x] My commits are reasonably scoped with clear messages. ## AI-assisted contributions (required) - [x] AI tools were used. Tool(s): `Claude Code (Opus 5)`. I have reviewed and understand every change, and take responsibility for it. AI-authored commits are marked with a `Co-Authored-By` trailer. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
wtclaude added 1 commit 2026-09-14 06:10:58 +00:00
The catalogue asked every body for action 0 and reported the rest absent. 73 of
this client's bodies have no art there and real art deeper — body 820's first
drawn action is 23, and it is a horse — so they rendered as text on the bestiary.
The catalogue now falls back to the first action that has art, and the key names
that action (`body/820/a23`). 1,022 -> 1,095 rows.

Walking the action axis is the one thing that can walk off the end of a body's
slots, and the slots after a body's band are the NEXT BODY'S. Measured here: one
action past the band, 643 of 795 legacy bodies return a fully validated picture
and 452 of those are byte-identical to body+1's action 0 (body 1 action 22 is an
ettin; body 3's is an imp, both confirmed by rendering them). Phase 0's validator
cannot catch that — the record is real — so the ceiling refuses the ADDRESS, in
ResolveAnimation where every caller already goes.

The ceiling is the index banding, never `Animations.GetAnimLength`: for a body
reaching file type 5 as id 34 that function answers 22 while the arithmetic gives
13, and the difference is nine actions of another creature's art.

A fetch serves only the key the catalogue chose for that body. `body/820/a0` and
`body/400/a2` come back `unsupported` with the chosen action alongside, never by
decoding what was asked for.

`EXTRACTOR_VERSION` 2 -> 3 (unchanged input, a different answer). Protocol stays
8 — `action` on a manifest/fetch row is additive.

Deep frame keys and the bulk-fill switch that §16 planned for this phase were
NOT built: the site displays still pictures, and a complete one-direction
animation set measures 174,453 frames / 281.5 MB against no consumer (docs
§11.2, org lead 2026-09-11).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
whitlocktech merged commit 577688b993 into edge 2026-09-14 06:15:56 +00:00
whitlocktech deleted branch feat/asset-bridge-p6 2026-09-14 06:15:57 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: RunicGateway/servuo-plugins#33
No description provided.