Files
trade-message-center/.trellis/spec/project/missing-values.md
T

9.9 KiB
Raw Blame History

缺失值、默认值与补偿

1. Scope / Trigger

凡是配置、环境变量、协议 payload、构建标识、持久化数据或跨包边界上将缺失值替换为另一个值的代码,都必须应用本规范。value || fallbackvalue ?? fallback 属于重点;普通布尔逻辑如 enabled || hasPermission 不属于本规则。

2. Signatures / Contracts

必填值在使用边界判空并返回稳定错误,不得生成或选择替代值:

const buildHash = process.env.BUILD_HASH;
if (!buildHash) throw new Error("BUILD_HASH must be provided by the workspace command");

workspace 根命令每次执行生成一次 BUILD_HASH,通过继承环境传给所有 package;package 只能消费:

const buildHash = randomBytes(8).toString("hex");
runPackages({ env: { ...process.env, BUILD_HASH: buildHash } });

只有用户要求、PRD/设计契约、已发布外部协议、现有可执行测试或必须保持的已确认兼容行为,才足以允许补偿。?? 只适用于契约规定 null/undefined 缺失;false0、空字符串是否缺失必须明定。

3. Contracts

  • 必填配置、协议字段和跨包环境变量缺失时必须观察到错误,不得静默改写为随机值、空值或开发值。
  • BUILD_HASH 唯一生成者是 workspace 根命令;下层不得从 mode env 读取、重新生成或覆盖。
  • 允许默认值时须在代码旁或所属规范写明来源、范围和不掩盖错误的理由,并测试存在/缺失两条路径。
  • 候选值优先级无证据时不得使用 a || b || c 猜测,应显式报错或补齐契约。
  • OneTalk business_card 的客户资料缺失不是消息事实错误:消息持久化必须保留 { version: 1, kind: "business_card" } marker;读取时没有同账号同会话 profile 也返回 marker,不能 fallback 到登录人/发送者 contactprofile 单字段缺失才按 view 合同返回 null

4. Validation & Error Matrix

发现的代码形态 处理
必填值用 `value
根生成 BUILD_HASH 后向下传递 保留单一生成点,下层缺失直接报错
契约明确缺失时默认值 允许,记录证据并测试主值、缺失值和有意义 falsey 值
0/false/"" 合法却被 `
多候选值无确认优先级 不猜测,显式报错或先补契约
名片读取时没有客户 profile 保留 marker;不得用消息 item.contact、登录人资料或其他会话资料补偿
名片 profile 只有部分字段 view 仅对缺失字段返回 null;不得回写 marker 或消息事实

5. Good / Base / Bad Cases

  • Good:根命令生成一次 BUILD_HASH,各 package 只读继承值,缺失直接失败。
  • Good:必填 database URL、协议版本或设备 binding 缺失时返回稳定错误。
  • Base:契约规定空标题显示“未命名会话”,且测试覆盖空/真实标题。
  • Base:契约规定仅 undefined 表示未配置,重试次数 0 合法,因此使用 ?? 并测试 0/缺失。
  • Base:名片当前客户资料尚未采集时,读取返回 marker;资料稍后到达由下一次读取以内存投影反映,不修改消息事实。
  • Badprocess.env.BUILD_HASH || randomBytes(8)...?? randomBytes(...) 在下层自行生成 hash。
  • BadNumber(env.PORT) || 3000env.PORT ?? 3000 没有端口默认值契约。
  • Bad:在消息观察阶段等待异步 __conversationListData__,或把登录人 item.contact 当作客户资料 fallback。

6. Tests Required

  • 必填配置覆盖缺失、空值和合法值,缺失断言稳定错误,而不是只断言不抛异常。
  • workspace 构建断言根命令只生成一次且所有 package 收到相同 hashpackage 单独缺失时失败。
  • 允许默认值覆盖真实、缺失及契约中合法的 0/false/""
  • 名片读取覆盖 profile 全缺失、部分字段缺失和完整字段,断言 marker/view/null 形状以及没有登录人资料 fallback。
  • 修改前后搜索 ||??、三元默认值和 mode env 读取,确认没有第二个来源。

7. Wrong vs Correct

// Wrong:下层随机,掩盖根层传递错误。
const buildHash = process.env.BUILD_HASH ?? randomBytes(8).toString("hex");

// Correct:根层生成一次,下层只消费并显式失败。
const workspaceBuildHash = randomBytes(8).toString("hex");
runPackages({ env: { ...process.env, BUILD_HASH: workspaceBuildHash } });
const buildHash = process.env.BUILD_HASH;
if (!buildHash) throw new Error("BUILD_HASH must be provided by the workspace command");
// Correct:契约规定未设置时使用默认值,且 0 合法。
const retryLimit = options.retryLimit ?? DEFAULT_RETRY_LIMIT;

Scenario: Workspace 发布版本与扩展清单

1. Scope / Trigger

  • Trigger:根 package 版本跨 workspace package metadata、扩展构建/watch 和 release CI 传播,且这些边界不得各自补偿或推导版本。
  • Scope:根 package.json:version 是唯一可编辑事实源;apps/*/package.json 的具体版本是同步镜像;扩展最终只加载生成的 dist/manifest.json

2. Signatures

  • validatePackageVersion(value: unknown, source?: string): string:验证同时满足 package metadata 和 Chrome Manifest 的三段数字版本。
  • readRootPackageVersion(rootDir?: string): string:只读取并验证 workspace 根 package.json
  • pnpm version:sync:把根版本写入所有 apps/*/package.json 镜像。
  • pnpm version:check:只检查镜像,不自动修复。
  • pnpm version:ensure:先执行 version:sync,再执行 version:check;根级 dev/build/typecheck/test/format:check 通过它获得稳定的一致版本环境。
  • TMC_PACKAGE_VERSION:由 workspace 根命令注入给下游扩展命令;扩展直接执行时缺失必须失败。

3. Contracts

  • 版本必须是 major.minor.patch 三段纯数字;每段在 0..65535,无前导零,且不得为 0.0.0
  • 禁止 prerelease、build metadata、v 前缀和第四段;root version、package mirrors、generated manifest、release archive/path 使用同一值。
  • BUILD_HASH 仍由根 wrapper 独立生成;版本读取不能从 hash、Git tag 或任何子包 manifest 推导。
  • Vite entry 不得复制共享 dist 的 public assets;扩展 build/dev orchestrator 只初始化一次静态资源,并独占 generated manifest。
  • release quality job 输出版本,publish job 只能消费该 outputGit tag 仅负责触发、源码定位和 ancestry 校验。
  • server 部署必须等待同版本扩展 ZIP 成功上传 OSS;下载接口只按运行时版本签名,上传失败时不得切换到新服务版本。
  • 发布数据库门禁调用 server test:integration,串行执行所有 *.integration.test.ts 并设置 60 秒硬超时;普通 server test 排除集成测试,不能代替数据库门禁。

4. Validation & Error Matrix

条件 行为
根版本缺失、非字符串或 JSON 无法读取 读取命令、质量命令和 CI 在使用前显式失败
版本不是三段数字、含前导零、额外段、prerelease/build metadata validatePackageVersion 显式失败,不生成/保留可发布 manifest
版本段大于 65535 或全为零 显式失败,不生成/保留可发布 manifest
子包镜像缺失或与根版本不同 单独执行 version:check 显式失败;根级质量命令先通过 version:ensure 同步,再执行检查
TMC_PACKAGE_VERSION 缺失或非法 直接扩展 build/watch 显式失败;不回退到模板或旧产物
tag 与根版本不同 CI 仍可按 tag 触发,但发布 archive/path 使用已校验的根版本

5. Good / Base / Bad Cases

  • Good:修改根 package.json:version 后直接运行根级质量命令;命令先执行 version:sync,再由 version:check 和构建验证所有镜像与 dist/manifest.json
  • GoodCI 在 checkout tag 后读取 root versionquality output 传给 publishpublish 再校验 zip 内 manifest version。
  • Basepublic/icons 由扩展 orchestrator 复制一次,Vite watcher 使用 copyPublicDir: false
  • Bad:从 GITHUB_REF_NAME、扩展 package manifest 或 manifest.template.json 作为发布版本来源。
  • Bad:四个 watcher 各自 public-copy,或版本缺失时继续使用旧 dist/manifest.json

6. Tests Required

  • 版本 reader/validator:覆盖合法交集、缺失、非字符串、格式、范围和全零失败,并断言稳定错误信息。
  • 镜像同步:临时 workspace 中验证漂移失败、version:sync 修复、再次检查通过。
  • 根脚本顺序:断言 version:ensure 的顺序是 sync → check,且所有根级质量命令统一调用它。
  • 构建边界:验证 root wrapper 同时传递一次 BUILD_HASHTMC_PACKAGE_VERSIONVite 禁止 public copyorchestrator 复制真实 icons 并生成 root-version manifest。
  • 发布契约:读取真实 release workflow,验证 tag trigger、quality output、SHA artifact、版本 archive/OSS path 和 zip manifest version check。
  • 产物审计:构建后断言 manifest、icons、入口文件存在且 manifest version 等于 root versionwatch smoke 可在缺少外部 URL 时明确跳过。

7. Wrong vs Correct

# Wrong:发布版本由 tag 或子包值决定。
release_version="${GITHUB_REF_NAME}"

# CorrectCI 只消费 quality job 已校验的根版本 output。
PACKAGE_VERSION="${{ needs.quality.outputs.extension_version }}"
// Wrong:下层缺失时偷偷使用旧版本或本地 package 值。
const version = process.env.TMC_PACKAGE_VERSION || "0.1.0";

// Correct:必填版本缺失或非法时显式失败。
const version = validatePackageVersion(process.env.TMC_PACKAGE_VERSION, "TMC_PACKAGE_VERSION");