feedback5 WU: 焰狐龙梓兰 (MH05) leaves data/assets.json; a voice slot with no line on disk is dropped, not []

She left the 自选 pool in 0.2.0 (data/backups.json diy.excluded, a collab operator) but her art stayed in the
manifest because fetch-assets refuses to shrink it. Regenerated offline in this worktree, whose public/assets and
public/fonts are the shared ones: node tools/fetch-assets.mjs --offline --add-only --allow-shrink (--add-only so no
file on disk is rewritten and the fonts entry is kept). The run dropped exactly her 163 entries.

JSON diff against the previous manifest: removed chars.char_1048_orchd2, skills.skchr_orchd2_1..3,
skillsById.skchr_orchd2_1..3, audio.sfx.units.char_1048_orchd2; changed hash and stats (files 6994 -> 6979,
bytes 462620448 -> 458334505, chars 210 -> 209, charsWithBack 207 -> 206, spineModels 673 -> 671,
skills 525 -> 522, sfxUnits 484 -> 483); nothing added, nothing else changed.

The first regeneration also added 71 entries like audio.voice.char_134_ifrit = {select: [], place: []}: the 自选
operators' voice lines are planned but not downloaded on this machine, and resolveTemplate kept an array whose lines
were all missing as []. tools/assets/manifest.mjs now drops such an array like a missing leaf (and the entry it
empties); an array that is empty in the template stays.

Tests: test/assets.test.js (the array rule), test/assets-diy.test.js (no diy.excluded operator keeps art in the
manifest). docs/ASSETS.md: 71 自选 picks, 209 operators (206 with Back), 522 skill icons, the empty-slot rule.
This commit is contained in:
sganggs committed 2026-10-06 17:51:41 +08:00
1 parent e2bb090f44
commit 9bfac54c8f
5 files changed
+42 -5

No files matched your search

+15
View File
@@ -362,6 +362,21 @@ describe('audio banks and plan id sets', () => {
assert.equal(ok.value.p, '/assets/package.json');
assert.equal(ok.fallbacks.length, 1);
});
test('an array whose lines are all missing is dropped, with the entry it empties (a voice slot never downloaded)', () => {
const line = (rel) => ({ alts: [{ rel, urls: ['u'] }] });
const tpl = {
voice: {
a: { select: [line('nope/1.mp3'), line('nope/2.mp3')], place: [line('nope/3.mp3')] },
b: { select: [line('nope/4.mp3'), line('package.json')] },
},
empty: [],
};
const r = resolveTemplate(tpl, { root: ROOT, spine: new Map() });
assert.deepEqual(r.value.voice, { b: { select: ['/assets/package.json'] } }, 'operator a has no line on disk: no entry at all');
assert.deepEqual(r.value.empty, [], 'an array that was empty in the template stays');
assert.deepEqual(r.misses.sort(), ['voice.a.place[0]', 'voice.a.select[0]', 'voice.a.select[1]', 'voice.b.select[0]']);
});
});
// ---------------------------------------------------------------------------