它解决什么问题
内容站的腐化通常不来自代码,而来自数据:缺字段的 frontmatter、改 slug 后留下的死引用、草稿混进公开索引、发布后页面出现内部术语。这套体系把这些问题挡在“写作的下一分钟”,而不是“读者的下一次点击”。
四层结构
第 1 层 结构校验 zod schema(内容集合定义):类型、枚举、默认值、日期
第 2 层 语义校验 scripts/check-content.mjs:引用完整性、slug 唯一、
日期逻辑、字段覆盖率、各集合统计 → content-health.json
第 3 层 生成隔离 prebuild:search:index / graph:data 只读非草稿;
公开产物(索引/图谱/RSS/sitemap/dist)逐一断言无草稿泄漏
第 4 层 运行时门禁 release-gate 脚本对真实响应检查:
全路由 200、站内死链、公开文案禁用词、状态页语义正确
关键决策:
- 校验先于生成:prebuild 顺序固定
content:check → search:index → graph:data → build,坏数据在最上游失败,下游永远消费干净输入。 - 草稿隔离用“产物断言”而不是“信任生成器”:生成器已过滤草稿,但门禁仍独立扫描每个公开产物是否含任一草稿 ID——双保险,因为这里的失误不可逆(搜索引擎缓存)。
- 内容健康可视化:校验结果落 JSON,后台渲染成清单(缺什么字段、差多少内容达标),作者看到的是可执行条目而非日志墙。
- 人工判断保留给人:门禁只查可机械验证的规则(存在性、格式、泄漏、死链);文案质量、真实性审核是独立人工环节,草稿必须经它才能转正。
运行方式
# CI 与本地同一入口
npm ci && npm run check && npm run lint && npm run test \
&& npm run content:check && npm run build
# 发布前对运行中的站点:
npm run test:release-gate:runtime
历史上这套门禁真实拦下过:新文章标题命中公开页禁用词(改文章,不降规则)、生成索引残留已删路由、define:vars 脚本在客户端路由下崩溃(由此新增全站交互回归)。
当前状态
四层全部在跑:CI 工作流含全部质量命令;运行时检查接入发布流程;内容后台展示健康度与草稿审核清单。已知边界:禁用词扫描跳过代码块(正文内技术词合法),像素级视觉验收仍需人工(见路线图遗留记录)。
下一步
把“订阅频道承诺 ⊆ Feed 覆盖”这类跨产物一致性规则补进第 3 层;为运行时检查增加性能断言(复用 LHCI 产物而非另起浏览器会话)。