发布于 更新于 约 6 分钟读完 · 2,366 字

本文目录 11 节
  1. 事实记录(不可修改区)
  2. 缺口长什么样
  3. 为什么跨了两轮迭代没被发现
  4. 四条可复用 insight
  5. 1. 写入方不等于展示方(同族第三形态)
  6. 2. 手工兜底会掩盖缺失的系统路径
  7. 3. upsert 两条分支的字段集要对齐
  8. 4. 运行时字段的验收要造出「真相源与库不一致」的那一格
  9. 本机验证姿势(可直接复用)
  10. 关联文档
  11. 方法论验证等级

成稿链接存进库没人读 · 写入方不等于展示方

入档:2026-07-30 一句话:字段有人写、有库存、接口也返回了,但没有任何页面读它——发布方在管理台填完成稿链接,任务书详情页一个字都不变;缺口跨了两轮迭代没被发现,因为在此之前人一直在正文里手写「交付情况」替系统把这件事做了。

事实记录(不可修改区)

  • 项目:蛛网之上(tiaozhuxiansheng.com)任务书系统,2026-07-30 单会话
  • 起点:发布方把 OJO 测评任务在管理台切成「完工待打款」并填了成稿链接,详情页上什么都没变
  • 提交(above-the-web):5571bad 修复渲染缺口 + 同步回填 → 6eb6023 OJO 收录为评测类标杆
  • 改动规模:8 文件,+118 / -5 行;后端回归 37/37 通过(含本轮新增 2 条)
  • 定位证据(git log -S):deliverable_url 列、管理台输入框、接口 deliverableUrl 全部诞生于 a179002(账号系统那轮);前端渲染 deliverableUrl 的第一次提交就是本轮的 5571bad
  • 交付:详情页正文前加一行「成稿」callout(两张详情页共用,md 那份构建期就位、运行时以库为准);syncTasks 的 UPDATE 分支补上「库里空才回填」
  • 数据来源:本地 chrome-headless-shell 造三种库内状态实测 + 线上两条流水线绿后对生产页面与 /api/tasks/ojo-review 复验
  • 已知尾巴:库里那条链接带着飞书 ?from=from_copylink 尾巴(能打开,未清);OJO 仍是 done,打款后需人工切 closed

缺口长什么样

任务系统的成稿链接有三条写入路径:承接人在站内点「提交交付」、发布方在管理台手填、md 里的 deliverable 首次入库时当初值。

读路径零条。详情页上那条「交付:链接」读的是 deliveries 流水表——只有第一条路径才会产生行。线下交付、发布方代填这两类,成稿链接就只躺在 tasks.deliverable_url 里,页面上不存在。

顺带查出同族第二个洞:syncTasks 的 INSERT 分支写 deliverable_url,UPDATE 分支不写。md 里补了链接、而库里那行已存在时,链接永远进不来——表现得像「同步不工作」,实际是两条分支的字段集不对称。

为什么跨了两轮迭代没被发现

  1. 之前三份任务书的成稿链接,是手写进正文的「## 交付情况」小节的——页面看起来一直是对的;
  2. frontmatter 的 deliverable 确实有一条读路径(任务书首页的「教程类标杆」板块),所以「这字段没人用」的直觉不成立;
  3. 直到发布方第一次真的走「只在管理台填、正文一个字不改」这条从没跑过的路径,缺口才现形。

四条可复用 insight

1. 写入方不等于展示方(同族第三形态)

一个字段有人写、有库存、接口也返回,不等于有人读它。链路上任一环没接,功能就等于不存在。

  • 加运行时字段时对照三件套:存库 → 接口返回 → 页面渲染,缺一条就是隐形字段;
  • 读路径要挂在收敛后的那个字段(tasks.deliverable_url)上,不是某一条写入口的流水表(deliveries)——流水表只记「谁在什么时候提交过」,不回答「现在的成稿是哪一份」;
  • 验收问句从「数据存进去了吗」改成「页面上哪一行会因此变化」。

边界:只写不读也可能是对的(审计、留痕字段本就不上页面)。判据是「这个字段存在的目的是给人看吗」。

2. 手工兜底会掩盖缺失的系统路径

人用手写内容替系统把事做了,缺口就永远暴露不出来。发现某个功能「靠人手写补上」时要记一笔——那是系统缺口的坐标,不是工作习惯。

新增字段后至少走一遍不靠手写的路径。本轮的判据就是「只在管理台填,正文一个字不改,页面变不变」。

边界:手工兜底本身没错,它是上线速度的合理代价;错的是把兜底当常态而不记账。

3. upsert 两条分支的字段集要对齐

INSERT 写了某列、UPDATE 没写,表现为「只有第一次同步生效」,最像「同步不工作」。

  • 改 upsert 时把两条分支的列清单并排列出来比对,不逐条读代码;
  • 真相源字段与运行时字段混在一张表时,「只在空时回填」写成 deliverable_url = CASE WHEN deliverable_url = '' THEN ? ELSE deliverable_url END,比在应用层判断更难写错——库里已有的(承接人提交的、管理台填的)都比 md 新,不许覆盖;md 补了而库里空着,也不该让链接永远进不来。

4. 运行时字段的验收要造出「真相源与库不一致」的那一格

md 与库一致时,看不出到底是谁在生效。本轮自测造了三格:

场景md库期望
用户踩到的那个无链接管理台手填显示(修复前:不显示)
证明「库为准」有链接空隐藏
常态有链接同一条显示

只有第二格能证明「数据库为准」这条设计真的成立。双源字段的验收矩阵至少这三格,造状态用临时库 + 管理台接口,不许动生产数据。

本机验证姿势(可直接复用)

要验的是浏览器侧注水逻辑,构建产物证明不了。本机没装 playwright 包,但 playwright 的浏览器二进制在 %LOCALAPPDATA%\ms-playwright\:

  • 用 chromium_headless_shell-*/chrome-headless-shell-win64/chrome-headless-shell.exe,参数 --disable-gpu --no-sandbox --no-first-run --user-data-dir=<临时目录> --no-proxy-server --virtual-time-budget=5000 --dump-dom <url>;
  • 别用 chromium-*/chrome-win64/chrome.exe --headless=new --dump-dom:同参数会挂住不返回(实测超 3 分钟)。目录名是 chrome-win64 不是 chrome-win;
  • --no-proxy-server 与 curl 的 --noproxy 127.0.0.1,localhost 都是必须的,本机代理会把回环请求也劫走;
  • 本地环境:平台服务指向临时 sqlite + dist/tasks/index.json 当清单,另起 astro preview(本地 base 是 /above-the-web/,CORS 白名单已含 127.0.0.1:4321),用管理员令牌 PATCH /api/admin/tasks/<slug> 造各种库内状态;
  • 测动效路径还要加 --blink-settings=prefersReducedMotion=false,见 reduced-motion本机陷阱_动效降级纯淡入而非跳过_v1。

关联文档

方法论验证等级

本轮 insight等级
写入方不等于展示方(存进库不等于看得见)⭐⭐ 二次验证(同族第三形态)
手工兜底会掩盖缺失的系统路径⭐⭐ 二次验证
upsert 两条分支的字段集要对齐⚠️ 首次发现
双源字段验收要造出「只有库里有」那一格⚠️ 首次发现

没有三次验证的方法论,不用于指导下一次决策——只作为「值得继续观察的假设」。前两条已二次成立,下次做同类迭代时按它们先自查再动手。

类型/平台工程主题/版块工程主题/踩坑