信号维度纪律 — 页级信号不能升级到站级状态¶
核心:抓取/状态/缓存的每个事件都有"作用维度"(行 / URL / 域名 / 全局)。 把小维度的失败信号升级到大维度的状态降级 → 必然过度跳过 + 用户误以为产品坏。
ISSUE-0075(row vs URL)+ ISSUE-0076(URL vs domain)是同一类家族 bug。
维度层级(从小到大)¶
规则:信号默认在原维度生效;要升级到更高维度需要明确的累积证据。
反模式(红信号)¶
反模式 1:单次失败直接升级(无历史感知)¶
// ❌ domain-state.ts v0.10.114 之前
if (outcome === 'dead') {
setState(stat, 'dead'); // 一次失败 → 整域名 30 天 TTL
}
jbjcortho.com 案例:首页 fetch ok-contact → 子页 HEAD dead → 整站打死,首页和其他子页全跳过。
反模式 2:页级信号污染域名状态¶
// ❌ contact 没找到(页级)升级到域名 cold(站级)
if (outcome === 'ok-empty') {
// 5 次 ok-empty → cold
setState(stat, 'cold'); // 整域名 90 天跳过
}
moleskidental.com 案例:5 个不同子页都没找到 contact → 整域名 cold → 包括首页都被跳过。
反模式 3:单点 404 不试 root 直接 dead¶
子页 404 不代表整站死。用户给的 website URL 可能是失效医生页,root 仍正常。
正模式(绿信号)¶
模式 1:历史感知降级¶
// ✅ 已 ok 过的需累积证据才降级
if (outcome === 'dead') {
const hasOkHistory = stat.recent.some(e => e.outcome === 'ok-*');
if (hasOkHistory) {
const recentDead = stat.recent.slice(-DEAD_CONSECUTIVE_AFTER_OK);
if (recentDead.length >= 3 && recentDead.every(e => e.outcome === 'dead')) {
setState(stat, 'dead');
}
return; // 没满足条件不降级
}
setState(stat, 'dead');
}
模式 2:页级信号留在页级缓存¶
模式 3:fallback 链 — 404 试 root URL¶
// ✅ probe.ts v0.10.116
if (firstResult.kind === 'dead' && reason === '404') {
const rootResult = await probeSingleUrl(getRootUrl(url));
if (rootResult.kind !== 'dead') {
return { ...rootResult, finalUrl: rootUrl }; // root 活就用 root
}
}
决策表¶
| 信号 | 维度 | 该影响什么 |
|---|---|---|
| HEAD/fetch 网络层失败 | 可能站级 | 累积 N 次才升 dead |
| 404/410(明确状态码) | 页级 | 仅当 root 也失败才升站级 |
| 5xx / 429 | 通常站级(瞬时) | retry-later,不持久化 |
| antibot 拦截 (cf-mitigated 等) | 站级 | 升 antibot-soft/hard |
| ok-contact | 站级正向 | 累积升 friendly |
| ok-empty (contact 找不到) | 页级 | 只更新 ContactPool 缓存(url-hash),不升站级 |
| ContactPool hit | URL 级 | 短路 fetch,不影响域名 |
必问清单¶
写新状态机规则 / 缓存层 / 黑名单时:
[ ] 这个事件的作用维度是什么?(行 / URL / 域名 / 全局)
[ ] 我要把它影响到哪个维度?跨维度的话有充分证据吗?
[ ] 单次事件够吗?还是需要累积 N 次?
[ ] 有历史正向证据时,要不要"豁免"单次失败?
[ ] 升级有 TTL 吗?TTL 期间能不能手动复活?
当前合规处(已审)¶
| 模块 | 实践 | 备注 |
|---|---|---|
domain-state.ts |
dead 加历史感知 / 砍 ok-empty 升 cold / 加复活迁移 | v0.10.116 修 |
probe.ts |
404 试 root URL fallback | v0.10.116 加 |
contact-pool.ts |
URL 级缓存(url-hash),不跨维度污染 | ✅ |
历史复发(同家族 bug)¶
| ISSUE | 描述 | 修复版本 |
|---|---|---|
| ISSUE-0075 | row 维度 status 与 URL 维度 page-log 矛盾 | v0.10.114 |
| ISSUE-0076 | 页级 ok-empty 升域名 cold / 子页 dead 升站级 / 404 不试 root | v0.10.116 |
反复 2 次就需要写规则 — 不能等第 3 次。
教训¶
- 状态机的复杂度在维度,不在状态数 — 6 个状态 × 错位升级 = 600 种失控场景
- "5 次失败 = 该跳"听起来合理,但要问"5 次是什么维度的失败"
- 历史感知 ≥ 一刀切:已成功过的不该轻易降级
- fallback 链要完整:404 不该是终点;root 试一次成本极低
- 用户问"5 次是同 URL 还是不同子页?"是黄金洞察 — 设计者陷入"次数信号",用户从"维度"问出来