fix: enforce render-safe detail copy without screen meta labels
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2026-02-24 15:24:09 +08:00
parent 90d8b5e128
commit 0a693fdb7d

View File

@ -244,6 +244,19 @@ const TITLE_META_TERMS = [
"证据背书",
];
const RENDER_META_PATTERNS: RegExp[] = [
/第\s*\d+\s*屏/gi,
/\bscreen\s*\d+\b/gi,
/\bframe\s*\d+\b/gi,
/核心机制拆解/gi,
/机制拆解/gi,
/差异强化/gi,
/证据收口/gi,
/核心收益可证/gi,
/方案拆解/gi,
/信任收口/gi,
];
function hasPlanningTitleMeta(value: string): boolean {
const text = String(value || "").trim();
if (!text) return false;
@ -259,6 +272,14 @@ function stripAgentPlanningPhrases(value: string): string {
return result.replace(/\s+/g, " ").trim();
}
function stripRenderMetaTerms(value: string): string {
let result = String(value || "");
for (const pattern of RENDER_META_PATTERNS) {
result = result.replace(pattern, " ");
}
return result.replace(/\s+/g, " ").trim();
}
function desensitizeAudienceCopy(value: string, language: "zh" | "en"): string {
let result = String(value || "");
if (!result) return result;
@ -1245,6 +1266,45 @@ function sanitizePlannedScreens(
return fallbackScreens.map((fallback, index) => sanitizeScreen(raw[index], fallback, language));
}
function sanitizeScreensForRender(params: {
screens: DetailPlanScreen[];
coreFocus: DetailCoreFocusContext;
language: "zh" | "en";
}): DetailPlanScreen[] {
const { screens, coreFocus, language } = params;
return screens.map((screen, index) => {
const cleanedTitle = stripRenderMetaTerms(screen.title);
const title =
cleanedTitle && !hasPlanningTitleMeta(cleanedTitle)
? cleanedTitle
: index === 0
? coreFocus.firstScreenTitle
: language === "zh"
? "舒适升级"
: "Comfort Upgrade";
const subtitleRaw = stripRenderMetaTerms(screen.subtitle);
const subtitle =
subtitleRaw || (index === 0 ? coreFocus.firstScreenSubtitle : screen.subtitle);
return {
...screen,
title,
subtitle,
objective: stripRenderMetaTerms(screen.objective),
copy: stripRenderMetaTerms(screen.copy),
layoutNote: stripRenderMetaTerms(screen.layoutNote),
modelReason: stripRenderMetaTerms(screen.modelReason),
bundleReason: stripRenderMetaTerms(screen.bundleReason),
visualPrompt: stripRenderMetaTerms(screen.visualPrompt),
fabe: {
b: stripRenderMetaTerms(screen.fabe.b),
f: stripRenderMetaTerms(screen.fabe.f),
a: stripRenderMetaTerms(screen.fabe.a),
e: stripRenderMetaTerms(screen.fabe.e),
},
};
});
}
function sanitizeGlobalStyle(
raw: unknown,
fallback: DetailPlanResponse["globalStyle"],
@ -1875,7 +1935,12 @@ export async function POST(request: Request) {
const shouldUsePreflightPlan = qualityChecksAfterPreflight.passed && preflightPassed;
const shouldFallback = !shouldUsePreflightPlan;
const screens = shouldFallback ? fallback.screens : preflightScreens;
const screensRaw = shouldFallback ? fallback.screens : preflightScreens;
const screens = sanitizeScreensForRender({
screens: screensRaw,
coreFocus,
language,
});
const globalStyle = shouldFallback ? fallback.globalStyle : preflight.candidateGlobalStyle;
const qualityIssuesMerged = [
...new Set([