
no-mistakes CI Monitor 生命周期全解从 findings 驱动修复到 timeout 与 rerun 的完整机制【免费下载链接】no-mistakesgit push no-mistakes项目地址: https://gitcode.com/GitHub_Trending/no/no-mistakes导读本文基于 no-mistakes 仓库内部技能文档.agents/skills/ci-monitor/SKILL.md系统拆解其 CI 监控CI Monitor从观察 PR 检查状态到产出 findings、驱动自动修复、处理超时与重跑的完整生命周期。你将掌握CI 步骤如何将一次定案观察转化为带action的 findings、ci_timeout为何是空闲超时而非绝对截止时间、GitHub 检查合并rollup collapse与no_ci声明的就绪语义、以及取消型检查先重跑、绝不进修复循环的瞬态策略——每条结论都对应仓库源码与回归测试可直接指导二次开发与运维排障。CI 步骤的本质产出 findings而不是产出修复SKILL.md 开宗明义CI 步骤只负责观察并定案修复由执行器executor的共享自动修复循环驱动。这是理解整个监控器的第一原则。一次定案观察settled observation指所有检查都已结束、所有已授权的瞬态重跑都已花完、且至少存在一个问题。此时ciObservationFindingsinternal/pipeline/steps/ci_findings.go把每个问题转成一条带action的 finding普通失败检查 →ActionAutoFix进入auto_fix.ci循环合并冲突 →ActionAutoFix且修复必须重新校验注册过的 review botscm.ReviewBots以scm.Check.App识别发布失败 → 按未解决评论逐条生成ask-user警告不消耗修复轮次提供方归因、且无重跑会替换的结果如取消型检查→ask-user警告。修复轮次由执行器的repairFromFindingsinternal/pipeline/steps/ci_fix.go消费sctx.PreviousFindings完成auto_fix.ci的轮数由执行器根据轮次历史持久化统计——CI 步骤自身从不计数或限制修复轮数。修复发布后监控仍留在修复轮内继续监视必须调用sctx.MarkRunning()使状态回到running否则就绪消费者如 TUI 的活动 CI 指示会读到过时状态。该契约与回归清单由 AGENTS.md 中的 CI Step Findings Model 一节负责维护。分类器只读提供方的结构字段bucket、state、check suite 的 app 身份绝不读检查名或日志文本因此其可信度与背后的状态 API 一致空 app 身份永远不会被当作 review bot。ci_timeout 是空闲超时不是绝对截止时间SKILL.md 明确区分了两种时间基准timeoutAnchor空闲超时的测量基准仅当上游默认分支 tip 前进时重新武装re-arm。这意味着只要 base 分支持续更新一个绿色 PR 的监控器就会一直续命。started固定不变用于轮询节奏poll pacing与宽限期计算不受 re-arm 影响。re-arm 只会延长截止时间这是对 base tip 解析瞬时失败的 fail-safe 方向。取值语义集中在 internal/config/config.go0未配置→ 回落到config.DefaultCITimeout7 * 24 * time.Hour见 config.go负值CITimeoutUnlimited -1→ 无限监控哨兵直到 PR 合并/关闭或被axi abort --run中止关键字解析由parseCITimeout处理config.go支持unlimited、none、off、never等YAML 模板中的默认值为ci_timeout: 168hconfig.go并需与 Go 默认值保持同步由TestDefaultConfigYAML_MatchesGoDefaultsinternal/config/config_global_test.go强制校验。用户面语义的权威文档在 docs/src/content/docs/reference/global-config.md。GitHub 就绪状态commit rollup 与 Actions 运行记录的并集对 GitHub 而言PR 的 CI 就绪状态是两路证据的并集当前 PR head commit 的检查汇总check rollupActions API 返回的、针对同一 SHA的每一个 workflow 运行。一个在创建 jobs/check-runs 之前就被拒绝的 workflow不会出现在 commit rollup 中但仍会出现在 Actions API 里。因此运行发现错误run discovery errors与未知运行状态失败关闭fail closed绝不把一个绿色 rollup 当作认证通过对应回归TestGetChecksIncludesFailedWorkflowRunMissingFromPRRollup、TestGetChecksBindsRollupAcrossABAHeadMovement、TestCIStep_FailedHeadWorkflowRunPreventsChecksPassed。同名检查折叠collapse同一 head 上后运行覆盖先运行GitHub 原始 commitstatusCheckRollup会返回一个 commit 拥有过的每一个检查运行——包括已经被同 head 上后来的同名运行取代的旧运行例如 pipeline 自动修复推送后、被synchronize重新触发的 required-check job。Host.GetChecksinternal/scm/github/github.go负责把同一 workflow 的同名重跑折叠到最新的startedAt用 Actions 运行身份打破时间戳平局与每个保留的重复项比较保留无序记录包括有歧义的 pending 替换最后用completedAt兜底。同时独立 workflow、未识别的外部检查、同名的 commit status context一律保留从而恢复gh pr checks已经采用的 last-wins 语义本仓库 gate 的verify.py也依赖这一语义。关键顺序约束折叠必须在appendUnrepresentedWorkflowRuns之后执行绝不能提前。原因后者按运行 ID 把 Actions 运行并集与 checks 去重且去重基于完整未折叠的 rollup若先折叠被取代运行的 ID 会从 represented 集合中消失并集就会把同一个过期运行以它自己的 workflow 运行名重新加回来。不折叠的后果是一个真实事故场景监控器把synchronize放进 gate 触发集合时每次自动修复推送都会让旧 head 上的 gate 合法失败稍后同 head 重绑定又变绿但过期的 FAILURE 仍然可见并再次触发修复推送形成无限循环。回归TestGetChecksCollapsesSupersededSameNameCheckToLatestAtOneHead、TestGetChecksCollapseOrderingDoesNotLetWorkflowRunUnionResurrectSupersededCheck、TestGetChecksPreservesIndependentSameNameWorkflows、TestGetChecksPreservesIndependentSameNameExternalCheckRuns、TestGetChecksCollapseComparesNewestRunWithEverySameNameCandidate、TestGetChecksKeepsQueuedReplacementWithEqualStartTime、TestGetChecksPreservesUnorderedExternalPendingReplacement、TestGetChecksPreservesSameNameStatusContextAndCheckRun。已发布修复的 attestation 重绑定restamp一次已发布的 CI 修复会把 PR body 中已有的 pipeline attestation 重绑定到新 headrestampPublishedAttestation紧跟publishRepair之后但仅当宿主具备该 HTML attestation 与PRContentReader即 GitHub时其他宿主直接跳过并告警不得让已发布的修复失败GitHub 上重试后仍无法定案的 restamp 是未定案unsettled不是成功——对应errCIAttestationUnsettled此时 CI 修复被 park 为 ask-userci_fix.gorestamp 写回前会二次读取 PR body 确认未被并发编辑绝不覆盖他人改动只写 body、不发 titleci_fix.go。此外CI 修复提示词ciFailingCheckFixRules见 ci_fix.go允许 agent 判定过期/attestation/基础设施类红不是代码缺陷并得出code_change_neededfalse但真实的失败测试或构建仍然必须修复不得为了迎合检查而凭空捏造工作。回归TestCIStep_PublishRepairRebindsAttestationAcrossRepairPushes、TestCIStep_PublishRepairDoesNotMintAttestation、TestCIStep_PublishRepairSkipsRestampWithoutReader、TestRestampPRAttestation_MissingReaderIsSkipped、TestRebindPipelineAttestationHead_VerifyPyRoundTrip、TestCIStep_FixPromptPrefersSimplificationOverMachinery、TestCIStep_FixPromptDistinguishesCodeDefectFromExternalFailure。空检查列表永不绿色no_ci 是唯一例外CI 就绪绝不把未经证实的空 forge 检查列表当作绿色就绪要求观察到全绿检查或受信任的默认分支配置no_ci: true且注册检查数为零由 internal/pipeline/steps/ci.go 判定该声明是否适用internal/cimonitor/cimonitor.go 拥有 agent 面向的日志词汇与 Ready/DeclaredNoCI 解析延迟注册、pending、失败、错误、未知状态、过期 head 证据一律保持 not-ready注册了检查的 no-CI 仓库其检查仍会被正常处理no_ci 声明不能豁免已注册的检查。cimonitor包的三个规范日志行是全系统的单一事实源cimonitor.goChecksPassedMsgall CI checks passed - still monitoring until merged or closedNoChecksPassedMsgrepository declares no CI (no_ci: true) - treating as all checks passed - still monitoring until merged or closed只有正向声明才会输出ChecksRunningMsgCI checks running, waiting for results...TUI 与 agent 面向的axi命令都通过ParseActivity/FromAuthoritativecimonitor.go解析这些行保证两端解释一致、不会漂移。回归TestChecksPassed_PR607RealLogSequence、TestCIStep_EmptyChecksWithoutNoCIStaysNotReadyPastOldGracePeriod、TestCIStep_EmptyChecksWithTrustedNoCIBecomesReady、TestCIStep_DelayedCheckRegistrationStaysNotReadyUntilGreen、TestCIStep_DeclaredNoCIWithUnexpectedChecksHonorsThem、TestEffectiveRepoConfig_NoCITrustedOnly。持续检查读取失败6 连败即停车而不是隐形空转提供方检查读取的持续失败不会被掩盖连续 6 次GetChecks错误且 PR 仍处于 open 状态时CI 步骤停车park在一个 ask-user gate 上consecutiveCheckErrorLimit 6见 internal/pipeline/steps/ci_checks.gofinding 是 provider 中立的指明提供方 CLI/凭据支持问题并携带底层错误GitHub 特有条款要求gh 2.50才能执行gh pr checks --json任何一次成功读取都会重置连续失败计数。回归TestCIStep_PersistentCheckReadFailureParksAtAskUser。这样一个坏掉的gh认证不会把运行拖到ci_timeout耗尽。孤儿监控器的回收axi abort --run从 worktree 之外回收孤儿监控器使用no-mistakes axi abort --run id它只需要NM_HOME不会启动已停止的 daemon已知运行只有拿到持久化的终态真相durable terminal truth才算成功已记录的 nonterminal 运行按未确认失败处理只有未知 id 是幂等 no-op裸axi abort仍然保持 worktree/分支作用域。PR 生命周期是单调的merged/closed 事务性地终结运行一个 merged 或 closed 的 PR 观察会事务性地完成一次活动运行及其 CI 步骤PR 生命周期状态是单调的因此重复或延迟到达的观察不能重新激活或回退regress一个终态运行启动时在任何 parked-run 规划与通用崩溃恢复之前会先对已持有终态 PR 状态的遗留pending/running行做对账reconcile。回归TestUpdateRunPRStateFinalizesActiveTerminalOutcomes、TestUpdateRunPRStateIgnoresDuplicateAndDelayedRegressions、TestReconcileTerminalPRRunsFinalizesLegacyActiveRows、TestRecoverOnStartup_FinalizesLegacyTerminalPRRune2e 有TestTerminalPRRunDisappearsFromActiveListing。瞬态策略cancelled 先重跑绝不进修复循环提供方报告的cancelled检查永远不是对代码的裁决因此确定性重跑严格先于任何 CI 修复轮次执行。核心实现在 internal/pipeline/steps/ci_transient.go分类classifyCheckFailureci_transient.gogenuine提供方归因于 job 本身的失败自身退出码、自身超时、无法启动的 workflow→ 直接送修复 agenttransient提供方归因于自身的结论——只有 cancelled 检查CANCELLED/CANCELED以及 pre-run 基础设施失败job 在仓库任何步骤运行前就挂在 setup/action 解析上由可选的scm.PreRunFailureDetector标记Check.PreRunFailure并经markPreRunInfraFailures重归类到 cancel bucket→ 重跑且永远不会掩盖真实测试/lint 失败真实失败必然过了 setup、死在后面的步骤unknown状态未知或本版本不识别 →绝不重跑不确定状态不是瞬态的证据。两个刻意的排除项TIMED_OUT属于 genuine——GitHub 在 job 超过自身timeout-minutes时报告它这通常是分支自身代码挂起重跑只会再烧掉一个完整超时窗口STALE被normalizeCheckBucket视为 skipped 而非失败避免一处说不失败、另一处说重跑它的矛盾。预算checkRerunBudget按检查名per check name记账同一 flaky job 不能花掉别的 job 的预算同名检查共享一个 key选择时用预留reserved防止单次轮询超额预算按请求花销而非按成功花销spend在请求前先持久化预留崩溃后不会越过文档上限重发默认值DefaultCIRerunTransient 0上限MaxCIRerunTransient 5config.goci.rerun_transient配置见 docs/src/content/docs/reference/repo-config.md任一 genuine 或未识别失败、或合并冲突出现在同一次轮询中时抑制所有重跑——真实失败在首次观察到时就立即升级不被瞬态兄弟拖慢。发布 head 校验一旦已发布分支 head 不再等于runs.head_sha绝不发出重跑——否则会认证一个本运行从未交付的 commit。每个未完成的重跑都记录其校验过的 pipeline head 与请求时可见的同名提供方链接当运行 head 前进或出现新的结论性非取消链接时重跑持久化退役retirement 保留已花预算绝不改变检查 bucket。延迟到达的同名绿色兄弟可以满足同 head 的链接触发这与默认分支既有的 name-keyed 屏蔽一致。终态与 pending 的区分Terminal is not pendinghasUnresolvedChecksci_checks.go就绪必须拒绝每个非 pass/fail/skip buckethasPendingChecksci_checks.go只有还能自行完成的检查才允许监控器继续轮询cancelbucket——GitHubCANCELLED、GitLabcanceled、BitbucketSTOPPED、以及 GitHub 对 job 被自身timeout-minutes杀死的报告方式——是不会再被替换的已发布结论因此在无未完成重跑时作为ask-user瞬态 findingunresolvedTransientFindings由cancelledWithoutRerun选出上报而不是继续等待混淆两者的后果正是 #628 回归真实运行被挂满整个ci_timeout。未识别的 bucket 仍被刻意当作等待处理因为未知不是终态的证据。rollup 滞后宽限gh run rerun在提供方接受请求后立即返回而新尝试是异步替换 rollup 中的取消检查因此下一次轮询仍可能读到重跑想要替换的那个结果。rerunRollupGracePolls 2ci_transient.go给出有界宽限有界地等它发布超限则作为 unresolved 上报绝不无限挂起。回归清单TestCIStep_CancelledCheckIsRerunBeforeEscalating、TestCIStep_CancelledCheckStaysUnresolvedAfterItsBudget、TestCIStep_LaggingRerunRollupKeepsWaitingForTheRepublishedCheck、TestCIStep_SameHeadGreenRerunEmitsChecksPassed、TestCIStep_DelayedSameNameCheckRetainsLegacyNameBehavior、TestCIStep_ResolvedRerunDoesNotParkALaterGreenHead、TestRetireResolvedReruns、TestRetireResolvedRerunsRetriesAfterPersistenceFailure、TestCIStep_MovedPublishedHeadTerminatesInsteadOfRerunning、TestCIStep_MovedPublishedHeadClearsCIReadiness、TestClassifyCheckFailure、TestMarkPreRunInfraFailures_RetriesInfraButNotGenuine、TestMarkPreRunInfraFailures_OptInGated、TestPreRunFailures_FlagsSetupFailureNotGenuine、TestPreRunFailures_FailsClosedOnUnreadableRun。就绪读的是实时 head每次轮询都从提供方读取CI 就绪状态在每次轮询时都从提供方的实时 PR head 检查汇总读取它永远描述 forge 当前为该 PR 持有的 head没有任何已记录的 SHA 充当门槛一个行记录仍指向前进前 commit 的运行也必须能在 pipeline 最后推送的 head 上识别绿色。回归TestCIStep_GreenChecksAtAdvancedHeadAreRecognizedWhileRunTracksOlderHead。观察者视角一份可复现的行为清单把整份 SKILL.md 浓缩成运维与开发可逐条核对的检查表主题行为契约关键证据文件findings 模型一次定案观察 → 每个问题一条带 action 的 findingbot 评论 park 为 ask-userci_findings.go空闲超时timeoutAnchor随 base tip 前进 re-armstarted固定re-arm 只延长ci.go、config.go检查并集commit rollup ∪ Actions 运行错误/未知 fail closedgithub.go同名折叠last-wins且必须在 appendUnrepresentedWorkflowRuns 之后github.go空列表无no_ci: true永不绿色延迟注册保持 not-readyci.go、cimonitor.go持续失败连续 6 次读取错误停车 ask-usergh 2.50ci_checks.go孤儿回收no-mistakes axi abort --run id仅需 NM_HOMEaxi_abort 相关命令族生命周期merged/closed 单调终结启动对账遗留行ci.go瞬态重跑cancelled/pre-run 失败先重跑预算按名记账、按请求花销head 移动即终止ci_transient.go终态 vs pending只有能自行完成的检查才保持轮询cancel 是终态结论ci_checks.go实时就绪每轮询读实时 head rollup无 SHA 门槛ci.go结语从文档契约到源码实现的一条龙验证.agents/skills/ci-monitor/SKILL.md本质上是 no-mistakes CI 监控子系统的一份行为规格说明书而仓库内 ci.go、ci_transient.go、ci_fix.go、ci_findings.go、cimonitor.go 与大量回归测试逐条落实了这份规格。阅读本文后你可以带着findings 驱动修复、空闲超时、并集就绪、同名折叠、瞬态重跑这五个关键词直接进入对应源码把每个策略分支与测试用例对应起来为二次开发、自定义 gate 或线上排障打下坚实基础。【免费下载链接】no-mistakesgit push no-mistakes项目地址: https://gitcode.com/GitHub_Trending/no/no-mistakes创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考