[!IMPORTANT] 终审与修复状态更新(2026-10-13):
本复审报告的所有独有发现、与初审报告的分歧裁定已全部整合进唯一权威终审报告:ARCHITECTURE_REVIEW_UNIFIED.md。
全仓所有 P0 / P1 / P2 问题均已全部修复完毕 ✅,并在tests/目录下建立了端到端自动化测试套件(18 项用例 100% 通过)。最新修复状态与代码台账请以终审报告为准。
审查日期:2026-10-10
代码基线:main@cedbe6c
对照文档:ARCHITECTURE_REVIEW.md
本文是独立复审,不覆盖原报告、不修改业务代码。原报告含有凭据和部署信息;本报告不重复凭据原文,也不上传外部服务。原报告中声称的历史复现,不自动视为本轮已经验证。
原报告的主线方向正确,但不能原样作为实施清单。 它抓到了错误成功、状态串用、push 重试和消费端降级问题;同时遗漏了危险清理路径、首次发布分叉、冷启动依赖及 fallback 绕过当前排除规则,并把若干可选重构或条件性现象写成了确定缺陷。
当前最重要的架构问题不是文件太长,而是没有把以下事实分开:
本次拿到了合法观测 ≠ 该地址符合当前策略 ≠ 本地快照写完 ≠ 远端发布成功 ≠ 下游拿到新鲜且可用的值。
建议优先处理:
| 优先级 | 问题 | 本文位置 |
|---|---|---|
| P1 | 卸载空服务名、打包外部临时目录都可扩大递归删除范围 | S-01、R-01 |
| 凭据处置 | 配置历史和原报告含完整 token;需轮换并脱敏 | §2.2 |
| P1 | API 业务错误文本可作为有效候选;两份 Python 配置共享 state/vars | §3.5 |
| P1 | fallback 可重新启用当前策略明确禁用的地址 | P-01 |
| P1 | push 失败不重试,以及首次远端探测失败形成不兼容历史 | S-02、§3.2 |
| P1 | Router 原地覆盖持久 state 和 HTTP 输出 | §3.7 |
| P1(特定权限部署) | root 重装跟随服务用户可替换的 token 文件路径 | S-06 |
| P2 / 跨链路 | 数据新鲜度、稳定值心跳发布与消费 TTL 必须一起设计 | §3.2、§4.1、§5.3 |
| P2 | 配置/指标边界可绕过排名、禁用规则或 JSON 契约 | P-02~P-07 |
| P2 | 冷启动恢复、服务用户权限及调度实际间隔缺口 | S-03~S-05、§3.3、R-02 |
“静态确认”不代表问题较轻;危险删除路径无需通过实际删除才能确认。条件性问题也不代表默认部署已触发,本文在各条中写明了前提。
范围包含 Python 更新器、Server Git 发布与 systemd 生命周期、BusyBox Router 更新与 HTTP 服务、打包、三份配置、Sub-Store 和配套文档。
证据分级:
未进行真实 API 请求、cfst 测速、HTTP 服务启动、systemd 安装卸载、Git fetch/push。未执行 run_update_and_commit.sh,包括离线方式也未执行整个调度脚本。
原报告 §10.4 用本地 runtime-state 的 HEAD 日期推断线上停更,但本轮读取已有引用发现:
| 引用 | 本地可见最新提交时间 |
|---|---|
refs/heads/runtime-state |
2026-09-15T01:40:44Z |
refs/remotes/vmess-domain-rotator/runtime-state |
2026-10-06T07:04:59Z |
远端跟踪引用最新三条记录为 56404a9、1b89e6b、9e96c09,提交说明分别呈现正常、fallback、正常发布,且选中值相同。因此:
本轮脱敏验证确认:配置含 token、对应历史提交存在且属于 main 祖先、原报告本身重复了完整 token。
建议作废并轮换、改用环境变量或受限文件注入;原 review 文档也要脱敏后才考虑提交或分享。凭据是否仍有效、API 是否有写权限、是否已被第三方使用,本轮均未验证,不能把“可能篡改候选”写成确定影响。
domain_updater.py:919-925 使用 subprocess.run(..., shell=True, check=False)。
以下事项必须与真实故障分开:
if ... set -- 参数拼接虽长,但不天然比表驱动差;避免为减少行数引入 eval、不受控分词或新的配置语言。server_only 下 Router 分支未执行,不等于死代码;它是可配置模式的正常实现。top_candidates 有诊断、人工选择和未来消费价值,暂未全部被当前 JS 使用并不构成错误。不应直接增加“第二候选自动 failover”:仓库没有可靠的目标连通性/隧道兼容性验证机制。vmess.tar.gz 当前不在 Git 跟踪文件中,原报告“该归档已入库”的论据不适用于当前基线。下列均为静态确认;没有执行整个调度器、安装器或卸载器。
位置:uninstall_debian.sh:40-68、uninstall_debian.sh:100-103。
SERVICE_NAME 没有非空、字符集、路径分隔符和删除目标归属校验。传入空服务名时,STATE_DIR 变为 /var/lib/;默认删除认证文件的分支会对该目录递归删除。空值可能来自部署变量或误用脚本参数。这不只是卸载失败,而是 root 权限下损坏其他服务状态。
不要实际运行此输入来验证。 应在任何 systemd/文件操作前拒绝无效服务名,限制为单一合法服务名称;删除前校验规范化路径必须位于预期的专属目录、且存在本安装器写入的归属标记。绝不能仅靠加引号防止此类目标错误。
位置:run_update_and_commit.sh:222-231、run_update_and_commit.sh:355-373。
首次没有本地 runtime 分支时,认证失败、网络失败与“远端分支不存在”共用 orphan 初始化路径。如果远端实际已有发布历史,本地会建立另一套历史,push 非快进失败。之后因为本地分支已存在,不再 fetch;即使候选持续变化或者使用 --force-commit,也不能解决分叉。
区别于原报告 P1-3:旧问题是已提交但未推送的版本被跳过;此处是历史拓扑已错误,单纯增加重试仍不能恢复。
建议区分探测结果与错误,未知远端状态必须失败而非初始化;每轮发布前核对远端进度,明确单发布者约束和远端领先/分叉恢复政策。不要用无条件 force push 作为修复。
位置:run_update_and_commit.sh:161、run_update_and_commit.sh:225-226、domain_updater.py:1023-1024。
fresh clone 或本地 state 丢失后,updater 先运行,发布分支随后才获取。首次 API 失败时没有本地 last-good,流程直接失败;即使远端有可用 state,也不会读取它。
这是灾备能力缺口,不等于现有本地 fallback 完全失效。建议明确恢复流程:受控地从可信发布分支恢复属于本实例、版本兼容且未过期的状态,再开始采集。远端 state 不能未经校验就当成当前健康观测。
位置:install_debian.sh:373-375、install_debian.sh:428、install_debian.sh:466-473。
root 所有的仓库指定非 root 服务用户,或者重装时更换用户,安装器只修正 runtime/服务 state 的归属。safe.directory 只是 Git 信任配置,不赋予 .git、refs、worktree 元数据写权限,配置文件的读取权限也未按服务用户验证。可能出现采集成功、发布阶段持续失败。
建议以实际服务用户检查配置、来源文件、输出、.git 与 worktree 操作权限;发现问题应拒绝安装或明确指导,不要默认递归 chown 整个用户仓库。
位置:install_debian.sh:430-453、install_debian.sh:512-516、run_update_and_commit.sh:204-209。
安装器输出的手工强制提交命令没有加载服务的 EnvironmentFile。自定义 remote、header-token 认证、禁用推送等配置会丢失,回落到脚本默认值。指定 remote 不存在时,脚本还会静默选第一个 remote,可能把输出发向非预期仓库。
建议提供遵循同一身份和配置的运维入口;明确 remote 配置错误应报错。禁用推送的部署不能因为改用手动入口就意外开启推送。
位置:install_debian.sh:387-389、install_debian.sh:419-423。
header-token 模式下,token 所在目录归服务用户所有,root 重装使用普通重定向写入固定文件。若该非 root 用户控制此目录内的链接,重定向以及随后的 chown/chmod 会跟随链接影响目录之外的目标。此结论依赖“非 root 服务用户可操作目录,随后 root 执行带 token 的重装”,不等于默认单用户部署已经被利用。
建议将认证文件目录保留为 root 管理;凭据按服务读取需要设置属组/权限。写入使用安全临时文件与受控替换,拒绝不符合预期的链接和文件类型。本轮仅检查权限边界,未执行提权或破坏实验。
位置:install_debian.sh:398-411、uninstall_debian.sh:95-103。
credential-store 模式把凭据写入服务用户家目录或指定文件,卸载却只清理环境文件、服务 state 目录和 safe.directory。已保存的凭据条目、credential helper 设置可能保留。因此不能承诺默认卸载清除所有关联 token。
修复应记录本安装器创建的凭据条目与配置,针对性移除或明确提示。不要删除服务用户共享的整个 Git 凭据文件。
位置:run_update_and_commit.sh:216-217、run_update_and_commit.sh:301-304。
当当前检出分支等于 runtime 分支,work_dir=APP_DIR;进入同步时源和目标是同一个文件,cp 报错后 set -e 终止。该分支正常运行还需具备脚本与配置等文件,所以不是默认 main 部署必现,但现有“直接复用”分支并不正确。可跳过同文件复制,或者明确拒绝不支持的运行位置。
证据:run_update_and_commit.sh:36-75、install_debian.sh:356-360、install_debian.sh:480-485。以下是算术推导,不是目标 systemd 运行实验:
| 参数与 tick 相位 | 门禁实际放行示例 | 问题 |
|---|---|---|
| peak=10,offpeak=30;分钟 05/15/25/35/45/55 | 05、25、35、55 | 默认间隔也可能在边界两侧各运行一次 |
| peak=10,offpeak=45;分钟 00/10/20/30/40/50 | 00、40、50 | 不是每 45 分钟 |
| peak=10,offpeak=120;分钟 00/10/20/30/40/50 | 每小时 00 | 不是每 2 小时 |
| peak=10,offpeak=120;分钟 07/17/27/37/47/57 | 非高峰全部跳过 | 容差完全错过边界 |
| peak=30,offpeak=10 | timer 每 10 分钟;高峰全部放行 | 高峰并非设置的 30 分钟 |
OnUnitActiveSec 不保证整点对齐;Persistent=true 也不能为纯 monotonic timer 提供 OnCalendar 式停机补跑。初次手动启动 service 仍经过门禁,不能把安装完成等同于已生成首份快照。
原报告还有两项需要纠正:
start == end 在代码中明确注释为“无高峰窗口”,没有相反业务约定时不能判为错误,应补文档或显式校验政策。应先确定“日历时刻运行”还是“距上次成功运行的间隔”语义,再生成正确 timer 或持久化调度状态。首次初始化、手工诊断和常规节流也应分开。
位置:domain_updater.py:1044-1075、domain_updater.py:1124-1144。静态确认。
state 保存 blocked.example,随后配置加入对该域名的明确排除;本轮没有其他合格候选时,流程直接重新选择 state 中的值。fallback 不再执行地址校验、硬性域名排除或实例归属检查。
需要区分:record_filter 全淘汰后并不会重新调用正则解析,但会取未经校验的 state。last-good 只说明过去被选中过,不能自动获得当前策略豁免。
建议把资格分为不可放宽的硬约束(合法地址、明确禁用、实例归属)和可按降级政策放宽的软约束(新鲜度、性能),所有 fallback 也必须满足硬约束。
top_n=0 不只隐藏候选,还会改变实际选择位置:domain_updater.py:816-820、domain_updater.py:928-936、domain_updater.py:1028-1042。纯函数离线复现:
原始顺序:slow.example, fast.example
评分顺序:fast.example, slow.example
top_n=0
实际结果:('slow.example', [])
评分后的切片为空,代码回退到原始顺序第一项。Local 模式同样允许有选中值、空候选列表;下游速度门禁因而没有指标可读。足够小的负数也有类似问题,不能笼统声称每个负数都得到空列表。
应校验正整数,拒绝布尔值和被 int() 截断的浮点数。正常正整数条件下,本轮没有发现 Python 把不同候选的指标错配给选中值;不要虚构 random/round-robin 路径,当前代码没有实现它们。
位置:domain_updater.py:660-686、domain_updater.py:704-738、domain_updater.py:72-74。离线复现:
1e308、指标各为 1,配置校验通过,但总分和总权重溢出,得到 NaN;经实际 build_top_candidates() 生成的候选 JSON 仍含裸 NaN。2 < 10、10 < "1x"、"1x" < 2 都成立,不满足排序所需的一致关系。另经静态检查,lexicographic 路径数值解析失败后保留原值,非有限数或错误类型可能转为文本参与排序。
极端权重不是默认配置故障,但数据/配置进入边界后不能产生宣称成功却无法被 JS JSON.parse() 读取的文件。建议明确字段类型,先淘汰无效指标;计算后再次验证有限性;序列化设置严格 JSON,并在写入前验证整个快照。
位置:domain_updater.py:649-677、domain_updater.py:753-764。静态确认;需要启用 within_hours,当前 Server 值为 0。
一条昨天的有效观测、一条刚生成但缺指标的观测,开启一小时窗口后,只保留后者并可能输出 ok。所以问题不止原报告的“全部记录都缺指标”,也可能是存在完整观测却选中不合格新观测。
未来时间没有上界检查;没有任何近期记录时又会主动放宽到全部记录。放宽本身可以是业务策略,但必须显式表现为降级,并与“硬性有效期”区分。先建立候选资格,再应用新鲜度和排序。
位置:domain_updater.py:271-295、config.server.json:60-64。解析纯函数离线复现:999.999.999.999 被保留。
原报告只强调 cfst 地址未校验;API 路径的域名正则也能接受这种数字串。当前 Server 排除的是“合法 IPv4”形式,并不能替代对非法数字地址的拒绝。应先区分地址类别并严格解析,不能因为非法 IPv4 不匹配合法 IP 正则,就把它当合法域名。
位置:domain_updater.py:444-460、domain_updater.py:510-523、domain_updater.py:562-570。
离线复现:
[:配置校验通过,运行时返回不匹配,规则静默失效。scoring.tie_breakers=null:校验通过,解析时迭代 None 抛出 TypeError。静态确认的输出键冲突:domain_updater.py:973-975、domain_updater.py:1101-1108。若将 state_last_good_key 设为保留键 status,地址被状态字符串覆盖;下一次异常可以把 ok 当作地址发布。
应在运行前编译规则、统一可空语义,并校验保留字段与输出路径冲突。不需要为此引入大型配置框架。
位置:domain_updater.py:25-34、domain_updater.py:1023-1027、domain_updater.py:1113-1144。静态确认。
语法有效但类型为 [] 或 null 的 state 不会回到默认对象,随后在主 try 外调用 .get() 直接退出。state 的 last-good 类型与地址也未校验。
另外,current/vars 已更新而 state 写入失败时,异常分支首先再次写同一 state;如果仍失败,回退写出流程不会完成。Python 的单文件原子写已有效降低截断风险,但不能替代 state schema 校验或多文件提交边界。
位置:domain_updater.py:144-166。静态确认。
skip_run=false 时,Python 也只检查 cfst 退出码和结果文件是否存在。旧 CSV 已存在、命令返回 0 却未刷新结果时,会读取旧文件并给本次时间及 ok。默认 cfst 是否会在什么情景下这样退出仍需版本确认,但运行器没有建立文件属于本次采集的保证。
显式 skip_run=true 主动复用文件是另一种模式,不应一律认定为错误。正常测速应使用本轮唯一结果路径,成功后验证并发布。
status.example.com 被正则兜底提取。准确前提是 HTTP 成功返回了业务错误;真实 HTTP 401/403 通常由 urllib 抛异常,而非照常交给解析器。--print-output-settings 确认:都使用相同的 runtime/state.json、runtime/substore_vars.json。约定与 README 不能替代当前真实配置。位置:build_router_package.sh:9、build_router_package.sh:46-49、build_router_package.sh:87-91。静态确认。
环境变量 TMP_DIR 可指向已经存在、包含其他数据的目录。cleanup 无条件删除整个目录,并在输入验证前就注册退出 trap;因此即使依赖缺失、根本没有开始打包,也会删除该目录。
不能靠提醒“只传空目录”消除这个清理边界问题。应在指定临时根下用 mktemp -d 创建本轮独占子目录,仅清理自己创建的目录,并避免清理未经验证的外部路径。不应实际执行危险目录输入验证此问题。
位置:router_local_update.sh:312-324、README.md:388-403。静态确认。
更新器已读到持久化 last-good,但必须等待没有外层 deadline 的 cfst 返回,才会写 fallback。README 启动流程又等待更新器后才启动 HTTP。重启清空 /tmp 后,一次迟迟不返回的测速即可让 HTTP 长期没有服务;“最多重试 5 次”不能约束单次耗时。
建议先按可接受的降级策略恢复一份完整快照、启动可用的读取服务,再进行有总超时的采集。正常“cfst 非零返回 + 可用持久 state”本来就能恢复,不能笼统说 Router 重启一定失去 fallback。
位置:router_local_update.sh:63-80、router_local_update.sh:134-173、router_local_http.sh:41、router_local_http.sh:118-124。静态确认,包含三个具体输入:
VALUE_TEXT_FILE 为绝对路径:更新器按绝对路径写,HTTP 却拼成 runtime 加该路径,找不到源文件或保留旧 index。VALUE_TEXT_FILE=index.html:先写当前值,随后把同一文件当派生 index 删除,再复制失败;成功与回退发布都可被中断。建议统一相对路径基准;逐一预检目录、可写性和规范化路径是否相互冲突。位于 docroot 外的文件不会因为配置了绝对路径就自动获得 HTTP 地址,必须显式区分持久化路径和公开路径。
位置:router_local_update.sh:103-123。审查子任务离线复现:从源码提取 awk 程序,用合成 CSV 输入;普通 region 可解析,region 内部含 tab 时输出带未转义控制字符,JSON 解析失败。
脚本没有输出解析/契约检查,因而可以将不可消费的 current 文件标为 ok。这不是正常 cfst 数据必现故障,而是输入边界缺失。不要仅修 tab;应采用覆盖 JSON 控制字符的可靠转义,并在发布前校验。
位置:router_local_update.sh:82-88。静态确认。
外部工具把状态保存为 "last_good_ip" : "...",冒号前有合法 JSON 空白,正则即无法提取值。测速失败时会被当作没有 last-good。自身生成的格式满足该正则,所以这是手工恢复、格式化和跨生产者兼容性问题,而非默认自写自读失败。
位置:README.md:405-406。若设备 ps 展示完整 cron shell 命令,启动分支里未隐藏的 router_local_http.sh 字符串可能使检测匹配自身,即使服务不在也不重启。[r] 技巧只能避免 grep 的搜索参数自匹配,不能解决同一命令另一处出现完整进程名。
应使用可靠的 PID/进程归属或服务监督机制。实际触发取决于目标 BusyBox 的 ps 输出和截断,不能宣布现有设备已经受影响。
位置:router_local_http.sh:130-131、config_router.conf:25-36。数字端口配置未限定监听接口,默认暴露 docroot 中可读文件;可达性与认证还受设备防火墙/BusyBox 配置影响。
不能直接宣称公网泄露;默认持久 state 位于 docroot 外,也不能声称它被默认 HTTP 暴露。建议明确 LAN 绑定、允许访问方以及只放可公开输出的 docroot,避免部署时把凭据或其他文件放入同目录。
| 原报告主题 | 本轮结论 |
|---|---|
| P1-5 原地写 state/HTTP 输出 | 确认。文本/index、current、state、vars 顺序覆盖,可能截断或混批。 |
| P2-2 非法地址进入 state | 确认非空即接受。但“第二行空、第三行有效”实际进入 fallback/失败,不是成功发布空地址;原报告的例子需修正。 |
| P2-3 旧 CSV 误作本轮结果 | 静态确认,具体 cfst 无结果行为待目标版本验证;显式 skip-run 不算错误。 |
| P2-4 cd 失败仍执行 | 源码确认;审查子任务在条件调用的最小 sh 逻辑中复现 set -e 不会可靠中止。 |
| P2-5 无互斥 | 确认。CSV 的首选、计数、候选分次读取,重叠更新会错配。watchdog 自身只启动 HTTP,不是直接启动更新器。 |
| P2-9 配置不含斜杠 | 可移植性问题成立,点命令的 PATH 查找不等于前置 -r 检查;具体 shell 行为待设备确认。 |
| P2-10 相对路径基准 | 确认 CWD 与配置目录语义混用;当前默认绝对路径和 README 的 cd 掩盖部分触发条件。 |
| P2-11 CRLF | 确认处理不统一。source 期间的回车空行可能先触发失败,不能指望之后修剪变量补救。 |
| P2-12 BusyBox fallback | 配置失效后会尝试其他 BusyBox,确认;但不是调用系统独立 httpd,成功日志会打印选中的二进制。 |
| P2-13 忽略 domain_filter | 撤回这一缺陷表述:shell 配置没有该选项,也已转发 cfst 原生过滤参数。新增相同功能是能力扩展。空字段输出与稀疏字段文档不一致则仍成立。 |
| P2-14 60 行参数拼接 | 可维护性讨论,不独立列 P2;现有带引号的 set -- 是正确 POSIX 做法。 |
| P2-16 cfst 可执行权限 | 确认,打包只验证文件名且未补 cfst 执行权限。 |
| P2-17 自定义安装根 | 确认,配置原样复制仍可能指向旧安装。 |
| P2-18 先删旧归档 | 确认,生成或校验失败会丢失旧可用包。 |
| P2-19 构建依赖完全未说明 | 修正:README 已列架构/组件要求和来源目录覆盖方式;缺的是获取步骤、版本与校验和。CI 可提供依赖或 mock,不能说完全无法验证。 |
| P2-20 index 只缺失才补 | 修正:HTTP 在源文本存在时每次删掉并重建 index,不是仅缺失时补建。 |
另有两处重要文档/状态语义漂移:
updated_at 不能直接当“最近一次成功测量”。这与减少闪存写入有关,应明确 checkpoint 策略,而非机械要求每个派生文件每次都写。cfip_runtime/ 维护路径,与当前 /tmp runtime 加独立持久 state 不完全一致。构建主机不能执行另一架构 BusyBox 时,也不能据此断言目标二进制缺少 httpd applet;应区分“无法在本机检查”和“目标明确不支持”。
证据:operator_template.js:171-180、operator_template.js:282-291。
离线复现:输入 {domain: "stale.example", status: "ok", updated_at: "2000-01-01T00:00:00Z"},节点被改写成 stale.example。
原报告 P1-4 只强调 Router fallback 抢占健康 Server,遗漏了默认部署:生产任务停止而静态 JSON 仍可访问时,5 分钟缓存到期只是重读同一份旧文件,不能证明数据新鲜。即使最近请求成功,也不代表最近测速成功。
建议:按来源校验 last_success_at 与有效期,明确模式内的降级政策。server_only 不应擅自切到 Router;没有合格值时可以保持原订阅节点,或者在显式配置的期限内使用降级值。
重要约束:不能孤立给 JS 加 TTL;必须同时处理发布端的心跳/新鲜度传播,否则健康且长期不换域名的 Server 会被误判过期。
证据:operator_template.js:47-58、operator_template.js:303-305。
离线复现:{domain: {bad: true}, status: "ok"} 被字符串化成 [object object],写入符合名称规则的 VMess 节点 server,同时进入缓存。
显式覆盖已有 IPv4/IPv6/域名校验,但 HTTP 获取的数据没有相同边界。错误类型、非法 IP、含协议或路径的字符串不应通过 String(...) 自动转成“地址”。生产端校验不能替代消费者校验。
建议:先验证顶层为对象、地址字段为非空字符串,再使用一致的地址解析器;坏来源进入失败分支,不污染节点或缓存。
证据:operator_template.js:118-163。
离线复现:Router 返回 ip=192.0.2.1, status=error_use_last_good 且没有测速指标,Server 返回健康值;default 仍选择 Router。状态只记入 telemetry,缺少速度不触发低速条件。
修复应按“健康且新鲜 → 有期限的降级值 → 保留原节点”的明确政策排序,而不是简单把所有 fallback 永久禁用。Router-only、Server-only、default 必须各自有明确降级边界。
证据:operator_template.js:13、operator_template.js:282-291。
条件性风险;共享缓存模型下已复现:两个同为 server_only、URL 不同的操作符使用同一 Map 模拟宿主缓存;A 写入后,B 不请求自己的来源,直接使用 A 的值。
影响取决于实际 Sub-Store 宿主的缓存命名空间;本轮未启动真实 Sub-Store,因此不宣称所有部署都跨实例串值。至少缓存键没有表达 URL、阈值和策略版本;编辑配置时同模式旧缓存可能继续生效。
建议:缓存键包括有效来源 URL、模式与影响选择的策略,缓存值保留来源、观测时间、状态和有效期,而不只保存裸地址。
证据:operator_template.js:62-76。
0 被 || "" 转为空,绕过低速判断;当前生产者通常输出字符串指标,因此这是数字化契约迁移时必须防止的回归,不列为默认部署阻断项。-10 MB/s 的正则只取 10,被视作正速度;这是损坏输入的健壮性缺陷,不证明 cfst 正常输出会产生负速度。应统一数值及单位契约,并校验完整输入,而非提取其中第一个数字。
$arguments.domain 合法时最高优先级生效,且不访问网络。domain_override=false 只关闭显式覆盖,恢复正常来源选择。ok 或 last-good 的值,都来自本次合法、通过业务筛选的观测;错误响应文本、旧 CSV 和损坏数据不能补成“成功”。last_success_at,通知失败不得撤销已完成的选择。Python Server、Python cfst 和 BusyBox cfst 是三个采集/运行路径,但运行时数据的生产实现主要是 Python 与 shell 两套,JS 是消费者。Router 无 Python、POSIX sh、闪存与内存分离等约束应保留。
建议先约定最小公共快照:版本、实例、批次、选中地址、状态、最近成功观测时间、有效期、原因。指标明确单位并绑定具体候选;不要把完整诊断数据塞进 substore_vars.json。
随后再按已有职责提取配置校验、来源适配、选择策略、状态持久化与发布边界。不要为了消除代码重复而让路由器依赖 Python,也不要把 cfst 原生排序强制转换成 Server 的业务评分规则。跨实现的一致性主要用共享 fixture 和契约测试保证。
/tmp 输出和持久 state 可能跨文件系统,不能声称一次 rename 能原子提交两者。持久 state 要单独保证完整写入和可恢复性。不要直接承诺“原子替换整个非空运行时目录”:跨平台目录替换、HTTP 正在访问和外置 state 都需要单独设计。
阶段 A:先止住错误成功和永久未发布
先封住卸载与打包的越界清理入口;修复特定权限部署下的凭据写入边界并轮换暴露凭据;隔离实例;严格校验输入并保留筛选失败语义;修复回退与输出写入;修复 pending push 重试及错误分支初始化;给当前最高风险路径补离线回归。
阶段 B:打通状态到消费者的闭环
明确成功观测时间与有效期;补健康心跳发布;统一来源资格与降级政策;锁住快照边界;补冷启动、重启和输出缺失恢复测试。
阶段 C:收敛结构与运维
修复部署参数、权限、目录和生命周期问题;补配置校验及诊断入口;提取稳定边界,而非一次性重写全部脚本。最后再讨论迟滞、并行请求与更多下游能力。
迟滞需要实际的候选排名变化、测量噪声和连接影响数据,不能仅根据 Git 提交数决定阈值。
Python py_compile
两份 Python 配置 --print-output-settings
三个 Server Bash 脚本 bash -n
两个 Router 脚本及打包脚本 sh -n
Sub-Store 脚本 node --check
路径检查实际证实 Server 与 Local 的 state/vars 重合,而不是根据旧报告推测。
纯函数/内存模拟实际观察到:
top_n=0 改选原始第一项;非法数字地址通过解析;数值/文本比较形成循环。tie_breakers=null 校验通过后触发 TypeError。[object object] 地址;接受多年以前的 Server 快照;Router fallback 优先于健康 Server。这些模拟以暴露当前缺陷为目标;复现断言通过不等于业务行为正确。
Router 子任务完成脚本语法检查,以及两个最小离线案例:条件上下文中的 set -e 不可靠中止、awk 候选字段内部 tab 生成非法 JSON。主会话另外重跑了所有规定的 shell/JS 语法检查。
Server/Python 子任务的部分命令受工具超时阻止,未把未运行的测试计入成果;主会话随后补做了上面列明的语法、路径及 Python 纯函数验证。
TypeError: Object of type datetime is not JSON serializable。这是测试夹具选错层级,不是生产输出的新增缺陷;改为调用实际候选构造函数后验证了 NaN 问题。main() mock 的实验时工具超时,因此 fallback 硬排除绕过、异常 state 在主 try 外崩溃等仍标为静态确认,不称已端到端复现。可以使用标准库 unittest、Node 内存 VM 和受控 shell fixture;本轮没有引入测试框架或改动业务代码。