feat: 添加 projectRelease/projectAlternative/projectDescription 定时任务 - #894
Conversation
Jiannan-dev
left a comment
There was a problem hiding this comment.
有 3 个会让新增的无人值守任务在生产中静默失败的问题需要先修,具体见行内评论。建议同时补充配置门控、AI 非 2xx/重试耗尽、GitHub rate-limit reset 的测试。
本地验证:构建 workspace util 后,现有 39 个测试全部通过;5 个变更文件无 ESLint error(projectRelease.js:320 有 1 条 Prettier warning)。
Jiannan-dev
left a comment
There was a problem hiding this comment.
复验了 74a42f9:AI 配置缺失时已能在全量查询前跳过,对应旧线程已标记 resolved;状态计数和 GitHub 限流等待也有改进。仍有 3 个会造成无人值守任务静默遗漏或重复付费调用的问题,见最新行内评论。其余旧线程先保留,待这些缺口修复后再 resolve。
本地验证:workspace util 构建成功,39 个测试全部通过;变更文件无 ESLint error(5 条 Prettier warning)。另用本地 HTTP mock 复现:外部 AI 返回 429 时当前只发起 1 次请求,没有重试。
Jiannan-dev
left a comment
There was a problem hiding this comment.
补充复验发现 2 个修复代码自身的边界问题,均已用最小状态模拟确认,见行内评论。
Jiannan-dev
left a comment
There was a problem hiding this comment.
复验 99c665d:AI 429/5xx 退避重试、Alternative 空结果完成标记、Release 请求失败分类、连续 403 有界退出都已修复,对应旧线程已全部 resolve。当前仍有 4 个阻塞项,均定位在最新修复代码,见行内评论。
本地验证:util 构建成功,39 个测试全部通过;6 个变更文件 ESLint、语法和 diff check 全部通过。行为 mock 验证 429 可重试成功;另复现 HTML 400 被错误重试 6 次。
|
|
||
| export const projectAlternativeTimer = addMonitoringToTask( | ||
| async function () { | ||
| if (!process.env.EXT_AI_SERVICE_URL || !process.env.EXT_ALTERNATIVE_BOT) { |
There was a problem hiding this comment.
[P1] 请恢复 Coze-only alternative 配置的兼容性
这次为解决 provider 门控不一致,直接把 timer 限定为 external URL+bot,并在 syncSingleProjectAlternative() 删除了整个 Coze fallback。但 README 仍明确承诺:未配置 EXT_AI_SERVICE_URL 时,/sync/alternative 走 COZE_API_TOKEN;.env.example 也仍将 Coze 作为有效配置。结果是现有 Coze-only production 部署会在这里静默 skip,手工 /sync/alternative 则会调用 fetch(undefined) 并失败。请恢复同一 provider 选择函数:external 配置完整则用 extChat,否则有 COZE_API_TOKEN 就走原有 Coze 分支;如果确实要下线 Coze,则需作为明确 breaking change 同步 API、配置文档和迁移方案,而不能由本 PR 隐式移除。
| retryCount = -1; | ||
|
|
||
| logger.error( | ||
| `AI chat response failed! status=${response.status} ${JSON.stringify(await response.json())}`, |
There was a problem hiding this comment.
[P1] 记录错误体不能改变重试分类并丢失原始 Response
这里直接 await response.json()。当永久 400/401 返回 HTML 或空 body 时,JSON 解析异常会落入下面的网络异常 catch,于是本来不应重试的 4xx 被指数退避重试 6 次;最后返回合成 { ok:false },真实 status 也丢失。我用本地 HTML 400 复现了 6 次请求和 31 秒延迟。请在状态分类之外 best-effort 读取日志正文(例如 response.clone().text() + 独立 catch),并始终返回原始 Response;只有 fetch 本身 reject 才走网络异常重试。也请加非 JSON 400/401 与空 body 503 的测试。
| logger.info( | ||
| `[Integration][ProjectAlternative] Integration Job end: updated=${result.updated}, failed=${result.failed}, total=${result.total}`, | ||
| ); | ||
| if (result.total > 0 && result.updated === 0) { |
There was a problem hiding this comment.
[P1] 任一成功仍会掩盖大面积 AI 失败
状态汇总已经有 failed,但这里仅在 updated === 0 时让监控失败。因此 provider 在第一个项目成功后持续 429/5xx,结果如 updated=1, failed=9999 仍被 addMonitoringToTask 写为 SUCCESS,无人值守任务的大面积缺失不可见。请定义并实现可观测的部分失败策略:至少 failed > 0 时标记任务失败并保留汇总,或采用明确、可配置且有告警的失败比例阈值和失败队列,不能以“有一次成功”代表整批成功。projectDescriptionTimer 有同样条件,应一起修复。
| `[Integration][ProjectRelease] Integration Job end: updated=${totalOk}, skipped=${totalSkip}, failed=${totalFail}, total=${totalProcessed}`, | ||
| ); | ||
|
|
||
| if (totalProcessed > 0 && totalOk === 0 && totalFail > 0) { |
There was a problem hiding this comment.
[P1] Release 大面积部分失败仍会被监控记为成功
请求错误现在能正确进入 totalFail,但最终条件仍要求 totalOk === 0。如果一次全量运行只有 1 个项目更新成功、其余 9,999 个因平台故障失败,这里不会抛错,任务仍记录 SUCCESS。请按失败数/比例决定任务状态,并保存失败项目供续跑;在当前监控只有 SUCCESS/FAILED 的前提下,存在未处理失败时应至少标记 FAILED,而不是让单个成功掩盖系统性故障。
#893