feat(gate): 新增 E_WAIT_LANGUAGE——交接件正文禁「让前端等我们」的措辞
changelog-filename-gate / validate (push) Failing after 2s
changelog-filename-gate / validate (push) Failing after 2s
2026-09-21 wx 第二次点名:「不要在changelog里写让前端等待部署 这不是你第一回犯错了 工作流是你部署完测试环境推送changelog」。 §2.1 的 backend_status 门禁挡得住预告式推送,挡不住这一类:20_7443 的 frontmatter 已经是 deployed、CI 全绿,但正文 status_note 结尾写着「该缺陷已在修……修好后另发 交接件」。mmg 因此一直没动工,隔天才来问「这个是有啥问题吗 还是没做到呢」——门禁 只看 frontmatter,看不见自由文本里的这句话,所以「deployed + 校验绿」并不代表这份 交接件可执行。消费方也没有能力消解这种不确定性:他查不了我们的部署状态、看不到 dev-v3、不知道「另发」是哪天,读到「等」就只能等,而且是静默地等。 - scripts/validate-changelog-frontmatter.mjs:新增 validateNoWaitLanguage,对所有 v2 文档逐行扫描(与 change_type 无关,前端条目同样适用)。三类措辞:部署状态对冲、 未来交付承诺、直接叫停对接;「等待」做共现判定而非裸词匹配,避免把「前端需轮询 等待支付回调」这类业务语义一起拦掉。前向引用(「以后续订正为准」「见后续订正」) 一并封住——它和「修好后另发」是同一件事换个说法,实测被绕过一次。 - tests:4 个用例,含 1 个阴性对照(灰度开关状态、已知缺口工单号、业务流程里的等待 必须放行),防止作者为了过门禁把该写的契约边界一起删掉。 - BACKEND_CHANGELOG_DELIVERY_GUIDE.md §2.1:写明规则、背景与那条分界线——这条影响 他「怎么写代码」,还是只影响他「什么时候开始写」?后者一律删。 阳性对照:对已推送的 HEAD 版 20_7443 跑新规则,命中 2 处(第 308、451 行「修好后另发」), 即它能抓住真实发生过的那次。既有 changelog-path-aliases 测试对 11_7510 的 2 条 E_ALIAS_STATE 红是本次改动之前就存在的,与本提交无关,pre-push 钩子也不跑该用例。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
这个提交包含在:
@@ -142,6 +142,51 @@ test('requires the template author format and write-operation database section',
|
||||
assert.ok(errors.some(({ code }) => code === 'E_API_TEMPLATE'));
|
||||
});
|
||||
|
||||
test('rejects a handoff that tells the frontend to wait for us', () => {
|
||||
const value = document().replace('## 七、不影响范围', `## 六.8、当前状态
|
||||
|
||||
- 🔴 该缺陷已在修(属 #7990 那一族),修好后另发交接件。
|
||||
|
||||
## 七、不影响范围`);
|
||||
const errors = validateV2Document(FILE, value, { requireV2: true });
|
||||
assert.ok(errors.some(({ code }) => code === 'E_WAIT_LANGUAGE'));
|
||||
});
|
||||
|
||||
test('rejects forward references that only rename the waiting', () => {
|
||||
const value = document().replace('## 七、不影响范围', `## 六.8、当前状态
|
||||
|
||||
- 该链路属 #7990 那一族,处理进展以后续订正为准,本节不复述内部处理状态。
|
||||
|
||||
## 七、不影响范围`);
|
||||
const errors = validateV2Document(FILE, value, { requireV2: true });
|
||||
assert.ok(errors.some(({ code }) => code === 'E_WAIT_LANGUAGE'));
|
||||
});
|
||||
|
||||
test('rejects hedging about whether the backend is deployed', () => {
|
||||
const value = document().replace('## 七、不影响范围', `## 六.8、当前状态
|
||||
|
||||
- 已合入 dev-v3;是否已滚动到测试服未核,取证前请自行跑 deploy-status.sh 确认。
|
||||
|
||||
## 七、不影响范围`);
|
||||
const errors = validateV2Document(FILE, value, { requireV2: true });
|
||||
assert.ok(errors.some(({ code }) => code === 'E_WAIT_LANGUAGE'));
|
||||
});
|
||||
|
||||
// 阴性对照:契约自身的覆盖边界(灰度开关状态、已知缺口的工单号、业务流程里的等待)不是「让前端等我们」。
|
||||
// 它们影响的是「怎么写代码」而不是「什么时候开始写」,必须放行——否则作者会为了过门禁把该写的限定一起删掉,
|
||||
// 那正好撞上「产出物必须把自己的覆盖范围写在脸上」那条相反的要求。
|
||||
test('accepts coverage limits that do not ask the frontend to wait', () => {
|
||||
const value = document().replace('## 七、不影响范围', `## 六.8、已知边界
|
||||
|
||||
- 灰度开关 \`hl.order.requirement.transfer-kind-submit-enabled\` 在生产环境尚未开启,开启属独立运维动作。
|
||||
- TRANSFER-only 订单在 9 个下游消费方无产出,已记 #8056,不影响本次对接。
|
||||
- 支付结果异步返回,前端需轮询等待支付回调。
|
||||
|
||||
## 七、不影响范围`);
|
||||
const errors = validateV2Document(FILE, value, { requireV2: true });
|
||||
assert.deepEqual(errors.filter(({ code }) => code === 'E_WAIT_LANGUAGE'), []);
|
||||
});
|
||||
|
||||
test('rejects backend and gateway pending at publication', () => {
|
||||
const errors = validateV2Document(
|
||||
FILE,
|
||||
|
||||
在新工单中引用
屏蔽一个用户