feat(assets): a creature's picture is whichever action has one (Phase 6)
The shard's catalogue can now answer for 73 bodies it used to report absent —
they have no art at action 0 and real art at a later one, and their key says
which (`body/820/a23` is a horse). This side stores that action and stops
assuming `a0` anywhere.
The atlas join is the part that mattered. It read
a.asset_key = CONCAT('body/', b.body, '/a0')
which would have silently dropped exactly the creatures this phase adds. It now
reads the row's own action, with COALESCE for rows written before the column
existed — a NULL inside CONCAT makes the whole comparison NULL, which would have
taken every portrait off the site on upgrade with the database perfectly correct
and nothing in any log. It still matches at most one row per slug: a deeper key
(`body/820/a23/f4`) does not equal the catalogue key.
Verified against a real MariaDB with the live shard's own 1,095-row manifest: the
ALTER applies to an installed-shape table and is idempotent, the horse joins to
its a23 picture, a pre-phase-6 NULL-action row keeps its portrait, and a stored
frame key does not become a second candidate.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
This commit is contained in:
@@ -332,6 +332,11 @@ async function readManifest({ family = FAMILY } = {}) {
|
||||
width: Number(row?.width) || 0,
|
||||
height: Number(row?.height) || 0,
|
||||
body: Number.isFinite(Number(row?.body)) ? Number(row.body) : null,
|
||||
// Which action the thumbnail came from (§11.2, phase 6). All but 73 of
|
||||
// this client's bodies answer 0; the rest have no art there and are
|
||||
// catalogued deeper, with the key naming the action. An overlay older
|
||||
// than phase 6 omits it, and 0 is the right reading of that.
|
||||
action: Number.isFinite(Number(row?.action)) ? Number(row.action) : 0,
|
||||
direction: Number.isFinite(Number(row?.direction)) ? Number(row.direction) : null,
|
||||
})
|
||||
}
|
||||
@@ -374,7 +379,7 @@ async function readManifest({ family = FAMILY } = {}) {
|
||||
/**
|
||||
* The bytes for an explicit list of keys.
|
||||
*
|
||||
* Returns a Map of key → `{ sha256, bytes, width, height, body, direction, png }`
|
||||
* Returns a Map of key → `{ sha256, bytes, width, height, body, action, direction, png }`
|
||||
* where `png` is a Buffer. A key the shard could not serve is **absent from the
|
||||
* map** rather than present with a null — the caller then decides what that means
|
||||
* for its own row, and the two ways it happens (`absent`, `unsupported`) are
|
||||
@@ -449,6 +454,7 @@ async function fetchAssets({ keys, catalog } = {}) {
|
||||
width: Number(row.width) || 0,
|
||||
height: Number(row.height) || 0,
|
||||
body: Number.isFinite(Number(row.body)) ? Number(row.body) : null,
|
||||
action: Number.isFinite(Number(row.action)) ? Number(row.action) : null,
|
||||
direction: Number.isFinite(Number(row.direction)) ? Number(row.direction) : null,
|
||||
// Phase 5's art families carry these; the body catalogue does not, and a
|
||||
// consumer that wants neither is unaffected by either.
|
||||
|
||||
Reference in New Issue
Block a user