feat(POSTV1-ASSET-ALL-16): 发布完整素材注册清单
Dada P0-A isolated Windows CI / validate-and-package (push) Waiting to run

This commit is contained in:
suyx
2026-08-06 11:30:09 +08:00
parent 910e32917f
commit fd4cd277cc
5 changed files with 87 additions and 66 deletions
+14 -2
View File
@@ -4,8 +4,8 @@ export interface ColorCardDefinition {
cardId: typeof P0A_COLOR_CARD_IDS[number]; cardId: typeof P0A_COLOR_CARD_IDS[number];
displayName: string; displayName: string;
mappingStatus: "confirmed_native_mapping" | "stable_web_style_native_mapping_provisional"; mappingStatus: "confirmed_native_mapping" | "stable_web_style_native_mapping_provisional";
rendererName: "horizontal_line" | "ticket_strip" | "vertical_stack" | "vertical_ticket"; rendererName: string;
styleId: "style_01" | "style_02" | "style_08" | "style_16"; styleId: string;
} }
export type FiveColorPalette = readonly [string, string, string, string, string]; export type FiveColorPalette = readonly [string, string, string, string, string];
@@ -17,7 +17,19 @@ export interface ColorCardRenderPlan extends ColorCardDefinition {
export const P0A_COLOR_CARD_DEFINITIONS: readonly ColorCardDefinition[] = [ export const P0A_COLOR_CARD_DEFINITIONS: readonly ColorCardDefinition[] = [
{ cardId: "COLOR001", displayName: "纵向票据", mappingStatus: "confirmed_native_mapping", rendererName: "vertical_ticket", styleId: "style_01" }, { cardId: "COLOR001", displayName: "纵向票据", mappingStatus: "confirmed_native_mapping", rendererName: "vertical_ticket", styleId: "style_01" },
{ cardId: "COLOR002", displayName: "纵向色阶", mappingStatus: "stable_web_style_native_mapping_provisional", rendererName: "vertical_stack", styleId: "style_02" }, { cardId: "COLOR002", displayName: "纵向色阶", mappingStatus: "stable_web_style_native_mapping_provisional", rendererName: "vertical_stack", styleId: "style_02" },
{ cardId: "COLOR003", displayName: "纵向色条", mappingStatus: "stable_web_style_native_mapping_provisional", rendererName: "vertical_strip", styleId: "style_03" },
{ cardId: "COLOR004", displayName: "纵向标线", mappingStatus: "stable_web_style_native_mapping_provisional", rendererName: "vertical_line", styleId: "style_04" },
{ cardId: "COLOR005", displayName: "横向标签", mappingStatus: "stable_web_style_native_mapping_provisional", rendererName: "horizontal_label_strip", styleId: "style_05" },
{ cardId: "COLOR006", displayName: "指示色条", mappingStatus: "stable_web_style_native_mapping_provisional", rendererName: "indicator_strip", styleId: "style_06" },
{ cardId: "COLOR007", displayName: "图钉色条", mappingStatus: "stable_web_style_native_mapping_provisional", rendererName: "pinned_strip", styleId: "style_07" },
{ cardId: "COLOR008", displayName: "横向标尺", mappingStatus: "stable_web_style_native_mapping_provisional", rendererName: "horizontal_line", styleId: "style_08" }, { cardId: "COLOR008", displayName: "横向标尺", mappingStatus: "stable_web_style_native_mapping_provisional", rendererName: "horizontal_line", styleId: "style_08" },
{ cardId: "COLOR009", displayName: "色彩海报", mappingStatus: "stable_web_style_native_mapping_provisional", rendererName: "color_poster", styleId: "style_09" },
{ cardId: "COLOR010", displayName: "标题海报", mappingStatus: "stable_web_style_native_mapping_provisional", rendererName: "caption_poster", styleId: "style_10" },
{ cardId: "COLOR011", displayName: "边框色条", mappingStatus: "stable_web_style_native_mapping_provisional", rendererName: "framed_strip", styleId: "style_11" },
{ cardId: "COLOR012", displayName: "OTTO 色块", mappingStatus: "stable_web_style_native_mapping_provisional", rendererName: "otto_blocks", styleId: "style_12" },
{ cardId: "COLOR013", displayName: "纵向圆点", mappingStatus: "stable_web_style_native_mapping_provisional", rendererName: "vertical_dots", styleId: "style_13" },
{ cardId: "COLOR014", displayName: "三色圆环", mappingStatus: "stable_web_style_native_mapping_provisional", rendererName: "three_circles", styleId: "style_14" },
{ cardId: "COLOR015", displayName: "描边圆点", mappingStatus: "stable_web_style_native_mapping_provisional", rendererName: "outlined_dots", styleId: "style_15" },
{ cardId: "COLOR016", displayName: "横向票条", mappingStatus: "stable_web_style_native_mapping_provisional", rendererName: "ticket_strip", styleId: "style_16" }, { cardId: "COLOR016", displayName: "横向票条", mappingStatus: "stable_web_style_native_mapping_provisional", rendererName: "ticket_strip", styleId: "style_16" },
] as const; ] as const;
+21 -39
View File
@@ -3,25 +3,20 @@ import type { StaticStickerCatalog, StaticStickerCatalogItem } from "@dada/stati
export const P0A_COMPLEX_RELEASE_VERSION = "p0a-complex-v1"; export const P0A_COMPLEX_RELEASE_VERSION = "p0a-complex-v1";
export const P0A_STATIC_STICKER_RELEASE_VERSION = "p0a-static-v1"; export const P0A_STATIC_STICKER_RELEASE_VERSION = "p0a-static-v1";
export const P0A_TEXT_TEMPLATE_IDS = [ function numberedIds(prefix: string, count: number) {
"FLOWER001", "FLOWER002", "FLOWER003", "FLOWER004", "FLOWER005", "FLOWER006", "FLOWER007", "FLOWER008", return Object.freeze(Array.from({ length: count }, (_, index) => `${prefix}${String(index + 1).padStart(3, "0")}`));
"H001", "H002", "H003", "H004", "H005", "H006", "H007", "H008", }
"TAG001", "TAG002", "TAG003", "TAG004", "TAG005", "TAG006", "TAG007", "TAG051",
"SIMPLE001", "SIMPLE002", "SIMPLE003", "SIMPLE004", "SIMPLE005", "SIMPLE006", "SIMPLE007", "SIMPLE008",
] as const;
// Derived from exact package-hash matches between the 32 frozen templates and the 86-item font panel. export const P0A_TEXT_TEMPLATE_IDS = Object.freeze([
export const P0A_REQUIRED_FONT_PANEL_IDS = [ ...numberedIds("FLOWER", 145),
"FONT005", "FONT008", "FONT011", "FONT021", "FONT022", "FONT027", ...numberedIds("H", 119),
"FONT039", "FONT043", "FONT046", "FONT052", "FONT081", ...numberedIds("TAG", 51),
] as const; ...numberedIds("SIMPLE", 17),
]);
export const P0A_COLOR_CARD_IDS = ["COLOR001", "COLOR002", "COLOR008", "COLOR016"] as const; export const P0A_REQUIRED_FONT_PANEL_IDS = numberedIds("FONT", 86);
export const P0A_COLOR_CARD_IDS = numberedIds("COLOR", 16);
export const P0A_DYNAMIC_STICKER_IDS = [ export const P0A_DYNAMIC_STICKER_IDS = numberedIds("DYN", 35);
"DYN001", "DYN002", "DYN003", "DYN004", "DYN007",
"DYN008", "DYN011", "DYN012", "DYN015", "DYN016",
] as const;
export const P0A_DYNAMIC_RUNTIME_FONT_SOURCES = [ export const P0A_DYNAMIC_RUNTIME_FONT_SOURCES = [
{ assetId: "15974853bc3294ef68e7e6d58fe74fd7", sourceReference: "fonts/15974853bc3294ef68e7e6d58fe74fd7", templateId: "DYN002" }, { assetId: "15974853bc3294ef68e7e6d58fe74fd7", sourceReference: "fonts/15974853bc3294ef68e7e6d58fe74fd7", templateId: "DYN002" },
@@ -76,12 +71,12 @@ export interface P0aPublicManifest {
text_templates: PublicComplexAsset[]; text_templates: PublicComplexAsset[];
}; };
counts: { counts: {
color_cards: 4; color_cards: 16;
dynamic_stickers: 10; dynamic_stickers: 35;
font_panel_items: 11; font_panel_items: 86;
static_parts: 25; static_parts: 25;
static_stickers: 1407; static_stickers: 1407;
text_templates: 32; text_templates: 332;
}; };
release_tier: "alpha_whitelist"; release_tier: "alpha_whitelist";
release_version: string; release_version: string;
@@ -140,15 +135,6 @@ function validateStaticCatalog(catalog: StaticStickerCatalog) {
if (Object.keys(catalog.part_counts).length !== 25) throw new Error("static sticker part counts must contain 25 parts"); if (Object.keys(catalog.part_counts).length !== 25) throw new Error("static sticker part counts must contain 25 parts");
} }
function fontIdsForTemplates(templates: readonly RegisteredComplexAsset[]) {
const referenced = new Set<string>();
for (const template of templates) {
for (const fontId of template.font_panel_references ?? []) referenced.add(fontId);
}
referenced.add("FONT081");
return [...referenced].sort((left, right) => Number(left.slice(4)) - Number(right.slice(4)));
}
export function createP0aPublicManifest(input: { export function createP0aPublicManifest(input: {
complexManifest: ComplexRegistryManifest; complexManifest: ComplexRegistryManifest;
staticCatalog: StaticStickerCatalog; staticCatalog: StaticStickerCatalog;
@@ -157,11 +143,7 @@ export function createP0aPublicManifest(input: {
validateStaticCatalog(input.staticCatalog); validateStaticCatalog(input.staticCatalog);
const textTemplates = orderedItems(input.complexManifest.items, P0A_TEXT_TEMPLATE_IDS, "text_template"); const textTemplates = orderedItems(input.complexManifest.items, P0A_TEXT_TEMPLATE_IDS, "text_template");
const derivedFontIds = fontIdsForTemplates(textTemplates); const fontPanelItems = orderedItems(input.complexManifest.items, P0A_REQUIRED_FONT_PANEL_IDS, "font_panel");
if (JSON.stringify(derivedFontIds) !== JSON.stringify(P0A_REQUIRED_FONT_PANEL_IDS)) {
throw new Error(`P0-A referenced font panel mismatch: received ${derivedFontIds.join(",")}`);
}
const fontPanelItems = orderedItems(input.complexManifest.items, derivedFontIds, "font_panel");
const colorCards = orderedItems(input.complexManifest.items, P0A_COLOR_CARD_IDS, "color_card"); const colorCards = orderedItems(input.complexManifest.items, P0A_COLOR_CARD_IDS, "color_card");
const dynamicStickers = orderedItems(input.complexManifest.items, P0A_DYNAMIC_STICKER_IDS, "interactive_sticker"); const dynamicStickers = orderedItems(input.complexManifest.items, P0A_DYNAMIC_STICKER_IDS, "interactive_sticker");
@@ -174,12 +156,12 @@ export function createP0aPublicManifest(input: {
text_templates: textTemplates.map(publicItem), text_templates: textTemplates.map(publicItem),
}, },
counts: { counts: {
color_cards: 4, color_cards: 16,
dynamic_stickers: 10, dynamic_stickers: 35,
font_panel_items: 11, font_panel_items: 86,
static_parts: 25, static_parts: 25,
static_stickers: 1_407, static_stickers: 1_407,
text_templates: 32, text_templates: 332,
}, },
release_tier: "alpha_whitelist", release_tier: "alpha_whitelist",
release_version: input.complexManifest.release_version, release_version: input.complexManifest.release_version,
+4 -4
View File
@@ -90,17 +90,17 @@ const familyCounts = Object.fromEntries(["text_template", "font_panel", "color_c
])); ]));
const fullP0Enabled = complex.manifest.items.filter((item) => item.release_tier === "full_p0" && item.release_status === "enabled").length; const fullP0Enabled = complex.manifest.items.filter((item) => item.release_tier === "full_p0" && item.release_status === "enabled").length;
const publicJson = JSON.stringify(manifest); const publicJson = JSON.stringify(manifest);
const hiddenIds = ["FLOWER009", "H009", "TAG008", "SIMPLE009", "COLOR003", "DYN005"]; const completionIds = ["FLOWER145", "H119", "TAG051", "SIMPLE017", "FONT086", "COLOR016", "DYN035"];
const response = { const response = {
counts: manifest.counts, counts: manifest.counts,
full_p0_enabled: fullP0Enabled, full_p0_enabled: fullP0Enabled,
hidden_ids_absent: hiddenIds.every((id) => !publicJson.includes(id)), complete_catalog_present: completionIds.every((id) => publicJson.includes(id)),
no_absolute_paths: !/[A-Za-z]:[\\/]/.test(publicJson), no_absolute_paths: !/[A-Za-z]:[\\/]/.test(publicJson),
release_tier: manifest.release_tier, release_tier: manifest.release_tier,
status: "passed", status: "passed",
}; };
const registrationValidation = { const registrationValidation = {
allowlist: { public_catalog: {
color_cards: P0A_COLOR_CARD_IDS, color_cards: P0A_COLOR_CARD_IDS,
dynamic_stickers: P0A_DYNAMIC_STICKER_IDS, dynamic_stickers: P0A_DYNAMIC_STICKER_IDS,
font_panel_items: P0A_REQUIRED_FONT_PANEL_IDS, font_panel_items: P0A_REQUIRED_FONT_PANEL_IDS,
@@ -112,7 +112,7 @@ const registrationValidation = {
source_mutations: complex.report.source_mutations + staticResult.report.source_mutations, source_mutations: complex.report.source_mutations + staticResult.report.source_mutations,
static_parts: Object.keys(staticResult.catalog.part_counts).length, static_parts: Object.keys(staticResult.catalog.part_counts).length,
static_stickers: staticResult.catalog.count, static_stickers: staticResult.catalog.count,
status: response.hidden_ids_absent && response.no_absolute_paths && fullP0Enabled === 0 ? "passed" : "failed", status: response.complete_catalog_present && response.no_absolute_paths && fullP0Enabled === 0 ? "passed" : "failed",
}; };
if (registrationValidation.status !== "passed") throw new Error("P0-A registration validation failed"); if (registrationValidation.status !== "passed") throw new Error("P0-A registration validation failed");
+28
View File
@@ -0,0 +1,28 @@
import { describe, expect, it } from "vitest";
import {
P0A_COLOR_CARD_IDS,
P0A_DYNAMIC_STICKER_IDS,
P0A_REQUIRED_FONT_PANEL_IDS,
P0A_TEXT_TEMPLATE_IDS,
} from "../../packages/template-registry/src/index.js";
describe("POSTV1-ASSET-ALL-16 complete asset catalog", () => {
it("publishes every normalized complex asset instead of the original alpha subset", () => {
expect(P0A_TEXT_TEMPLATE_IDS).toHaveLength(332);
expect(P0A_TEXT_TEMPLATE_IDS[0]).toBe("FLOWER001");
expect(P0A_TEXT_TEMPLATE_IDS.at(-1)).toBe("SIMPLE017");
expect(P0A_REQUIRED_FONT_PANEL_IDS).toHaveLength(86);
expect(P0A_REQUIRED_FONT_PANEL_IDS[0]).toBe("FONT001");
expect(P0A_REQUIRED_FONT_PANEL_IDS.at(-1)).toBe("FONT086");
expect(P0A_COLOR_CARD_IDS).toHaveLength(16);
expect(P0A_COLOR_CARD_IDS[0]).toBe("COLOR001");
expect(P0A_COLOR_CARD_IDS.at(-1)).toBe("COLOR016");
expect(P0A_DYNAMIC_STICKER_IDS).toHaveLength(35);
expect(P0A_DYNAMIC_STICKER_IDS[0]).toBe("DYN001");
expect(P0A_DYNAMIC_STICKER_IDS.at(-1)).toBe("DYN035");
});
});
+20 -21
View File
@@ -7,6 +7,7 @@ import type { StaticStickerCatalog } from "../../packages/static-sticker-catalog
import { import {
P0A_COLOR_CARD_IDS, P0A_COLOR_CARD_IDS,
P0A_DYNAMIC_STICKER_IDS, P0A_DYNAMIC_STICKER_IDS,
P0A_REQUIRED_FONT_PANEL_IDS,
P0A_TEXT_TEMPLATE_IDS, P0A_TEXT_TEMPLATE_IDS,
createP0aPublicManifest, createP0aPublicManifest,
} from "../../packages/template-registry/src/index.js"; } from "../../packages/template-registry/src/index.js";
@@ -116,29 +117,27 @@ function evidence(name: string, value: unknown) {
writeFileSync(resolve(root, name), `${JSON.stringify(value, null, 2)}\n`); writeFileSync(resolve(root, name), `${JSON.stringify(value, null, 2)}\n`);
} }
describe("TDD-WP5-WHITE-001 P0-A public allowlist", () => { describe("TDD-WP5-WHITE-001 complete public catalog", () => {
it("registers the complete archive while publishing only the exact P0-A allowlist", () => { it("registers and publishes the complete normalized archive", () => {
const source = fullComplexManifest(); const source = fullComplexManifest();
const before = structuredClone(source); const before = structuredClone(source);
const manifest = createP0aPublicManifest({ complexManifest: source, staticCatalog: staticCatalog() }); const manifest = createP0aPublicManifest({ complexManifest: source, staticCatalog: staticCatalog() });
expect(source).toEqual(before); expect(source).toEqual(before);
expect(P0A_TEXT_TEMPLATE_IDS).toHaveLength(32); expect(P0A_TEXT_TEMPLATE_IDS).toHaveLength(332);
expect(P0A_COLOR_CARD_IDS).toEqual(["COLOR001", "COLOR002", "COLOR008", "COLOR016"]); expect(P0A_REQUIRED_FONT_PANEL_IDS).toHaveLength(86);
expect(P0A_DYNAMIC_STICKER_IDS).toEqual([ expect(P0A_COLOR_CARD_IDS).toHaveLength(16);
"DYN001", "DYN002", "DYN003", "DYN004", "DYN007", expect(P0A_DYNAMIC_STICKER_IDS).toHaveLength(35);
"DYN008", "DYN011", "DYN012", "DYN015", "DYN016",
]);
expect(manifest.counts).toEqual({ expect(manifest.counts).toEqual({
color_cards: 4, color_cards: 16,
dynamic_stickers: 10, dynamic_stickers: 35,
font_panel_items: 11, font_panel_items: 86,
static_parts: 25, static_parts: 25,
static_stickers: 1_407, static_stickers: 1_407,
text_templates: 32, text_templates: 332,
}); });
expect(manifest.assets.text_templates.map((item) => item.canonical_id)).toEqual(P0A_TEXT_TEMPLATE_IDS); expect(manifest.assets.text_templates.map((item) => item.canonical_id)).toEqual(P0A_TEXT_TEMPLATE_IDS);
expect(manifest.assets.font_panel_items.map((item) => item.canonical_id)).toEqual([...referencedFontIds, "FONT081"]); expect(manifest.assets.font_panel_items.map((item) => item.canonical_id)).toEqual(P0A_REQUIRED_FONT_PANEL_IDS);
expect(manifest.assets.color_cards.map((item) => item.canonical_id)).toEqual(P0A_COLOR_CARD_IDS); expect(manifest.assets.color_cards.map((item) => item.canonical_id)).toEqual(P0A_COLOR_CARD_IDS);
expect(manifest.assets.dynamic_stickers.map((item) => item.canonical_id)).toEqual(P0A_DYNAMIC_STICKER_IDS); expect(manifest.assets.dynamic_stickers.map((item) => item.canonical_id)).toEqual(P0A_DYNAMIC_STICKER_IDS);
expect(manifest.assets.static_stickers).toHaveLength(1_407); expect(manifest.assets.static_stickers).toHaveLength(1_407);
@@ -154,10 +153,10 @@ describe("TDD-WP5-WHITE-001 P0-A public allowlist", () => {
&& item.release_tier === "alpha_whitelist" && item.release_tier === "alpha_whitelist"
&& item.validation_status === "passed")).toBe(true); && item.validation_status === "passed")).toBe(true);
const serialized = JSON.stringify(manifest); const serialized = JSON.stringify(manifest);
expect(serialized).not.toContain("FLOWER009"); expect(serialized).toContain("FLOWER145");
expect(serialized).not.toContain("COLOR003"); expect(serialized).toContain("COLOR016");
expect(serialized).not.toContain("DYN005"); expect(serialized).toContain("DYN035");
evidence("unit-allowlist.json", { counts: manifest.counts, hidden: ["FLOWER009", "COLOR003", "DYN005"], status: "passed" }); evidence("unit-full-catalog.json", { counts: manifest.counts, status: "passed" });
}); });
it("rejects incomplete registration and any early full_p0 enablement", () => { it("rejects incomplete registration and any early full_p0 enablement", () => {
@@ -172,12 +171,12 @@ describe("TDD-WP5-WHITE-001 P0-A public allowlist", () => {
}); });
describe("TDD-WP5-COL-001 shared five-color renderer input", () => { describe("TDD-WP5-COL-001 shared five-color renderer input", () => {
it("binds the four enabled layouts to one immutable palette snapshot", () => { it("binds all sixteen layouts to one immutable palette snapshot", () => {
const palette = ["#102030", "#405060", "#708090", "#A0B0C0", "#D0E0F0"] as const; const palette = ["#102030", "#405060", "#708090", "#A0B0C0", "#D0E0F0"] as const;
const plans = createP0aColorCardRenderPlans(palette); const plans = createP0aColorCardRenderPlans(palette);
expect(P0A_COLOR_CARD_DEFINITIONS.map((item) => [item.cardId, item.styleId])).toEqual([ expect(P0A_COLOR_CARD_DEFINITIONS.map((item) => [item.cardId, item.styleId])).toEqual(
["COLOR001", "style_01"], ["COLOR002", "style_02"], ["COLOR008", "style_08"], ["COLOR016", "style_16"], P0A_COLOR_CARD_IDS.map((cardId, index) => [cardId, `style_${String(index + 1).padStart(2, "0")}`]),
]); );
expect(plans.map((plan) => plan.cardId)).toEqual(P0A_COLOR_CARD_IDS); expect(plans.map((plan) => plan.cardId)).toEqual(P0A_COLOR_CARD_IDS);
expect(plans.every((plan) => plan.palette === plans[0]!.palette)).toBe(true); expect(plans.every((plan) => plan.palette === plans[0]!.palette)).toBe(true);
expect(Object.isFrozen(plans[0]!.palette)).toBe(true); expect(Object.isFrozen(plans[0]!.palette)).toBe(true);