№ · 活跃设计 CH.07.02
主页 · 📐 活跃设计 · 剧情追踪卡片 · 字段净化与输出规则 v1

剧情追踪卡片 · 字段净化与输出规则 v1

📅 2026-07-02 · 📦 11.0 KB · 📐 活跃设计 · 📝 MD 文档 🏷️ 装备🏷️ Buff🏷️ 主链🏷️ 路线图🏷️ 设计🏷️ Year1

状态:v1 草案 适用:src/components/StoryTrackingCard.tsx 及其下游(DesktopWorkspacePanel / PortraitWorkspacePanel / SystemHubPanel / App.tsx 的 storyTrackingSnapshot 构造) 触发原因:截图显示剧情追踪面板里把开发者内部 ID、tag 拼接、阶段平铺当玩家文案直接渲染(tax_reportedbrief=risk-tax_reported、6 段验收阶段用 -> 平铺等)


1. 现状(截图所见)

StoryTrackingCard 的玩家可见区域被分成 4 块,每块都出现了"开发者内容当成玩家文案"的现象:

| 区块 | 截图所见(原文照抄) | 玩家应该看到什么 | | --- | --- | --- | | 主标题 + 下一步 | 【终局危机】税吏翻账・偷税漏税被举报 + 下一步:危机源头:官府稽税黑名单 | 当前主线剧情标题 + 一句话剧情化下一步提示 | | 主链条(紫色小条) | 主语:【终局危机】税吏翻账...・压首稽税黑名单 / tax_reported・种术激活:预警...・引 brief=risk-tax_reported / tax_reported・终局优先 | 不应该存在——这块本身是技术桥接日志 | | YEAR1 规划 phaseLine | 月 1 开张适应期・开业教学 / 基础经营闭环 | 当前月阶段 + 一句剧情叙事(这个相对可接受) | | YEAR1 规划 attributeLine | 属性:服务 / 厨艺 / 账房 / 巡防 | 不应该存在——4 个属性平铺,没有上下文,是开发者切片 | | YEAR1 规划 itemLine | 投放:生存恢复 / 轻量侦查 / 古钱币 / 幸运制钱 | 半污染——focus 是剧情化但 storyItems 是内部物品 ID,混着显示 | | YEAR1 规划 checklistLine | 验收:开张教学闭环 -> 街访导入与首轮风险闭环 -> 扩建与半年经营闭环 -> 两转升温与年度预热闭环 -> 擂台高潮闭环 -> 岁末收束闭环 | 不应该存在——6 个阶段全量平铺,没有"当前所处阶段"高亮,-> 是开发者分隔符 |


2. 字段分类标准(输出规则的核心)

2.1 三类字段

| 类别 | 定义 | 处理方式 | | --- | --- | --- | | 玩家文案(Player-Facing) | 描述剧情 / 经营 / 状态,没有任何内部 ID、tag、阶段 key、技术路径 | 可直接显示,但要走 §2.3 的措辞审查 | | 开发者桥接(Developer Bridge) | 跨模块传递用的内部 ID / tag / 状态机 key(如 tax_reportedrisk-tax_reportedmainchainLinebriefingRefs) | 不直接渲染;要么不显示,要么只在开发者调试面板(?dev=1)显示 | | 元数据(Metadata) | 玩家可见但应"低调"展示(如 第 8 日・深 06:00 时间戳、章节名) | 可显示,但要有视觉降噪(灰底、字号 ≤10px) |

2.2 4 条快速判断标准

给一个新字段,往下走一遍,任意一条命中 → 不能直接渲染

  1. 包含下划线 / 蛇形 key
    tax_reportedrisk-tax_reportedfail-chain-tax_reportedbriefing-refs-... —— 这些都是开发者 ID,必须走映射表(如 FAILURE_CHAIN_TITLE_LABELSFAILURE_SEED_TAG_LABELS)后才能显示。
  2. 包含路径分隔符或 URL 参数?
    brief=risk-tax_reported / tax_reporteda / b / c 这种 /->=> 平铺 4 个以上 token 的,都是技术拼接。
  3. 超过 3 个同类 token 平铺?
    服务 / 厨艺 / 账房 / 巡防开张教学闭环 -> 街访导入... -> 扩建... —— 玩家不会读 6 段平铺;超过 3 个同类 token 平铺一律降级。
  4. 字段名暗示是技术桥接?
    *Bridge*Refs*Tags*Source*Seed*mainchainLinepressureSourceTagsfailureSeedTagsbriefingRefs —— 名字里有 Bridge / Refs / Tags / Source / Seed / mainchain 的,默认是开发者字段。

2.3 玩家文案的措辞审查

哪怕走通了 §2.2 不算开发者字段,渲染前还要过一遍:

  • 不能有「·」或「/」分号超过 2 个连用
  • 不能以 : / 结尾(玩家应该看到完整句子)
  • 不能用全大写 + 下划线(YEAR1_PLAN 这种 snake_case)
  • 不能有英文单词混在中文里(除非是专有名词,如 ID
  • 数字前不能直接堆单位符号(16 文 ✅,铜钱:16 文 ❌)

3. 受污染字段清单(按代码位置)

3.1 App.tsx 第 2997-3128 行 storyTrackingSnapshot 构造

| 字段 | 当前来源 | 污染等级 | 处理 | | --- | --- | --- | --- | | title | primaryBond?.title ?? briefingLead?.title ?? yearTracking?.currentChapterTitle ?? '同福剧情总览' | 🟢 良 | 保留 | | nextHint | primaryBond?.unlockEffects[0] ?? briefingLead?.detail ?? briefingLead?.summary ?? '下一章:...' | 🟡 半 | detail 经常是 危机源头:xxx,语义偏技术。需要给 briefing 在生成 detail 时限制最大长度 + 强制叙事化(中文短句,不带 之后超过 20 字的列表) | | progressLabel | primaryBond.stageLabel + value/100 / 今日线索 N 条 / 年线 N% | 🟢 良 | 保留 | | pressureSourceTags | 把 entry.title、entry.summary、entry.detail、entry.sourceId、entry.id、chapterId、phaseLine 等塞进数组 | 🔴 严重 | 不应进入玩家视图。只用于内部 bridge 构建(已经走 useTasklineFailureBridge),渲染层不要碰 | | failureSeedTags | 同上,加上 failureRecord.seedTagsendgameEntry.failureSeedTags | 🔴 严重 | 同上 | | briefingRefs | entry.identry.sourceIdchapterIdslotIdfailureId | 🔴 严重 | 同上 | | trackingJumpTarget | 'endgame' \| 'briefing' 枚举 | 🟢 元数据 | 渲染层只用它决定跳哪个按钮,不要把它当文案渲染 | | year1Plan | 整个 Year1PlanSnapshot | 🟡 半 | 详情见 §3.3 |

3.2 StoryTrackingCard.tsx 第 501-505 行 mainchainLine 渲染

// 当前(污染):
{bridge?.mainchainLine && (
  <div className="mt-2 rounded-lg ...">
    主链:{bridge.mainchainLine}
  </div>
)}

mainchainLinebuildMainchainLine()pressureSourceTags + failureSeedTags + briefingRefs 拼成的 主语:xxx・压 xxx・种 xxx・引 xxx・终 xxx 格式 —— 这是开发者调试日志,不是玩家文案

建议:直接删除这段渲染。bridge 只服务于跳转按钮(TasklineFailureJumpButton)和跨模块状态共享,不应在玩家界面露出。如果想保留"主链"概念让玩家理解当前危机上下文,应该从 bridge 里抽 riskLevel + currentTitle + nextAction 三个字段单独构造玩家文案,而不是复用 mainchainLine

3.3 year1Plan.phaseLine / attributeLine / itemLine / checklistLine 渲染

getYear1PlanSnapshot()src/data/year1Plan.ts:348-371 构造这 4 个字段,它本身就是开发者快照(命名带 Snapshot,就是为了排查状态用的)。但被 StoryTrackingCard.tsx:507-523 直接渲染了。

| 字段 | 当前拼法 | 处理 | | --- | --- | --- | | phaseLine | ${monthPhase.label} ・ ${monthPhase.headline} | 🟢 可保留(剧情叙事句) | | attributeLine | YEAR1_PLAN_ATTRIBUTE_LINKS.slice(0, 4).map(label).join(' / ') | 🔴 删除渲染。属性不应该在追踪卡出现,玩家在 STAFF 面板和 HUD 里看 | | itemLine | ${itemBand.focus} ・ ${itemBand.storyItems.slice(0, 2).join(' / ')} | 🟡 改为只显示 itemBand.focus,丢掉 storyItems 切片 | | checklistLine | YEAR1_PLAN_CHECKLIST_STAGES.map(stage => stage.label).join(' -> ') | 🔴 删除渲染。改为显示当前阶段checklistStage.label + checklistStage.passSignal),用 ✓ / ○ 高亮 mustNodes 中已完成 / 未完成 |

3.4 bridge.trackingJumpTarget 文案

StoryTrackingCard.tsx:494bridge.trackingJumpTarget === 'endgame' ? '打开终局解释层' : '回看江湖简报' —— 这个 OK,按钮文案是叙事化中文,不是开发者字段。但 target 本身('endgame' \| 'briefing')作为开发者枚举,在按钮 label 上没暴露,没问题。


4. 输出规则(落到 AGENTS.md 的段落建议)

把以下段落贴到 AGENTS.md 第 7 节「不要做的事」后面,作为新一节:

4.1 玩家文案 vs 开发者字段

禁止把开发者字段当玩家文案渲染

当一段文本包含以下任意一项时,禁止直接 {{text}} 进 JSX:

  • 内部 ID(snake_case、带下划线的,如 tax_reportedbrief-refs-...
  • 技术 tag(pressureSourceTagsfailureSeedTagsbriefingRefssourceId 等数组元素)
  • 路径 / URL 参数(brief=risk-xxx / xxx
  • 3 个以上同类 token 用 /->=> 平铺的拼接串
  • 字段名带 Bridge / Refs / Tags / Source / Seed / mainchain

正确做法:

  • 走映射表(如 FAILURE_CHAIN_TITLE_LABELSFAILURE_SEED_TAG_LABELS)翻译成中文剧情文案
  • 只在 ?dev=1 调试面板渲染
  • 或者干脆不显示(让玩家在剧情面板 / 简报面板里通过正常交互触发信息)

审查工具:src/components/StoryTrackingCard.tsx 任何 JSX 里的 {xxx} 都得过这一关。

4.2 阶段平铺规则

禁止在玩家界面把 4 段以上的"阶段名"平铺展示(如验收阶段 6 段、年度规划 12 段)。

正确做法:

  • 只显示当前所处阶段 + 1-2 句剧情叙事
  • 完整 12 阶段 / 6 验收放玩家设置 / 说明面板
  • 如果一定要显示进度,用 当前:月 3 初遇挑战期 + 进度条,而不是 12 段平铺

5. 整改清单(按优先级)

| # | 改动 | 位置 | 工作量 | | --- | --- | --- | --- | | 1 | 删除 bridge.mainchainLine 渲染 | StoryTrackingCard.tsx:501-505 | 5 行 | | 2 | 删除 year1Plan.attributeLine 渲染 | StoryTrackingCard.tsx:516 | 1 行 | | 3 | 改写 year1Plan.checklistLine 为当前阶段高亮 + mustNodes 进度 | StoryTrackingCard.tsx:519-521 + getYear1PlanSnapshot | 30 行 | | 4 | 简化 year1Plan.itemLine 只保留 focus | year1Plan.ts:357 + StoryTrackingCard.tsx:517 | 5 行 | | 5 | 给 nextHint 加长度截断(≤24 字)和叙事化过滤 | App.tsx:3044-3050 | 15 行 | | 6 | 把「开发者字段不直接渲染」写入 AGENTS.md 第 4 节输出规则 | AGENTS.md | 段落级 | | 7 | (可选)新增 ?dev=1 调试入口,把 pressureSourceTags 等桥接数据放进单独抽屉 | StoryTrackingCard.tsx | 50 行 |

预期:前 5 项落地后,截图所示的污染全部消失。?dev=1 是 nice-to-have,给后续排错留口子。


6. 自检脚本(可选)

写一个小工具扫所有 src/components/**/*.tsx,找:

  • {bridge.mainchainLine} 这种直接渲染 mainchainLine
  • .tags?.map 直接渲染 tag 数组的
  • .join(' / ') / .join(' -> ') 在 JSX 文本节点里出现的

跑一遍就能发现下一个 StoryTrackingCard 类型的污染源。


7. 关联文档

  • docs/design/active/道具系统新核心UI视觉规范-v1.md —— 视觉降噪的同类问题
  • AGENTS.md 第 1 节三层架构 —— 玩家文案(UI 层)和开发者字段(逻辑层)的边界问题本质就是跨层污染