Skip to content

fix: 克隆传输期时间心跳 (T13.14) 与 T13.12 两处复核修正 - #5

Merged
qiyinxi merged 7 commits into
AUTO-MAS-Project:mainfrom
qiyinxi:feat/t13-clone-heartbeat-20260907
Sep 7, 2026
Merged

fix: 克隆传输期时间心跳 (T13.14) 与 T13.12 两处复核修正#5
qiyinxi merged 7 commits into
AUTO-MAS-Project:mainfrom
qiyinxi:feat/t13-clone-heartbeat-20260907

Conversation

@qiyinxi

@qiyinxi qiyinxi commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

三件事,提交彼此独立、文件零重叠。不新增字段、stage、state、错误码或环境变量。

T13.14 克隆传输期时间心跳(35d027e e8bc217 8487442 98e9fd9

workspace.clone 的进度脉冲原本按 sideband 写入次数计数、上限 64。go-git 的 sideband 集中在握手阶段涌入,真机夹具里 64 条脉冲全部落在同一时刻(23.260),之后真正传输 packfile 的 7.9 秒一条事件都没有,而且 66 条消息文案完全一样。

改成固定 1 秒的 ticker 心跳:仍只发不带数值的 running(符合《架构设计》对 Git 阶段「只发不带数值的 running,由 Electron 展示不定进度」的要求),Write 保留 io.Writer 但丢弃 sideband 内容——那是外部自然语言,可能含 URL。stop() 在 Clone 返回后、判定结果之前调用并阻塞等 goroutine 退出,保证终态之后不会有迟到的 running。

fix(uv): 进度回调失败即结束镜像轮换(7c7fc67,T13.12 复核修正)

download()FailureProgress 分支,一律落 OutcomeSwitchSource。协议输出断掉时会把 5 个 uv 镜像源各空跑一次,attempt 报告标成 download_failed,内层错误码降级成 UV_DOWNLOAD_FAILED——与 T13.12 设计文档「协议写失败沿错误链保留 OUTPUT_WRITE_FAILED,不得降级」冲突。改为 OutcomeTargetFailure 并原样返回回调错误(不经 wrapBootstrapError,否则用户可见文案会从「协议输出失败」变掉)。

对外四元组不变:修复前后实测均为 code=OUTPUT_WRITE_FAILED stage=uv.download message=协议输出失败 exit=20,差别只在下载器调用次数 5 → 1。

这个缺陷此前测不出来的原因是 fakeRotationRunner 只调一次 attempt 且不看 Outcome.Kind,所以新增测试改用真 mirror.NewRotator()

fix(protocol): human 模式百分比保留两位小数(d4fef5a,T13.12 复核修正)

下载器给的 percent 未取整,human 模式会打印 percent=42.857142857142854%。只改渲染端,NDJSON 数值不动——设计文档要求三个数值逐字来自 mirror.DownloadProgress,而且两位取整会在 99.995% 时提前打出 100。

验证

工作目录为该分支工作树,go1.26.5 windows/amd64,全部实跑:

结果
gofmt -l . 输出为空
go vet ./... exit 0
go build ./... exit 0
go test ./... -count=1 19 个包全部 ok
git diff --check exit 0
go test -race ./internal/uv ./internal/mirror ./internal/gitrepo -count=1 exit 0

红→绿证据:TestBootstrap_DownloadProgressFailureStopsRotationTestUVDownloadProgress_OutputFailureStopsRotation 在修复前均报 downloader calls = 5, want 1;心跳的四条新测试在实现前编译失败。

未做:没有真机跑过带真实字节进度的完整 bootstrap 看 Electron 界面表现。

🤖 Generated with Claude Code

Sourcery 摘要

在保持外部进度协议不变的同时,提高 Git 克隆和 UV 下载过程中进度报告的可靠性。

错误修复:

  • 将基于 sideband 写入的克隆进度脉冲替换为每秒运行一次的心跳;即使在静默的 packfile 传输期间,心跳仍保持活动状态,并在终端事件之前停止。
  • 当下载进度报告失败时,立即停止 UV 镜像轮换,同时保留原始的输出写入错误。
  • 将人类可读进度输出中的百分比四舍五入到两位小数,同时不改变 NDJSON 中的值。

增强功能:

  • 添加有关克隆传输心跳的设计和任务文档,并更新里程碑跟踪信息。

文档:

  • 记录 T13.14 克隆传输心跳的行为,并更新当前设计索引。

测试:

  • 增加对定时克隆心跳、静默传输期间、sideband 隔离、输出失败取消、终端事件顺序以及 UV 轮换终止的测试覆盖。
Original summary in English

Summary by Sourcery

Improve progress reporting reliability across Git cloning and UV downloads while keeping the external progress protocol unchanged.

Bug Fixes:

  • Replace clone progress pulses based on sideband writes with one-second running heartbeats that remain active during silent packfile transfer and stop before terminal events.
  • Stop UV mirror rotation immediately when download progress reporting fails while preserving the original output-write error.
  • Round percentages to two decimal places in human-readable progress output without changing NDJSON values.

Enhancements:

  • Add design and task documentation for clone transfer heartbeats and update milestone tracking.

Documentation:

  • Document the T13.14 clone transfer heartbeat behavior and update the current design index.

Tests:

  • Add coverage for timed clone heartbeats, silent transfer periods, sideband isolation, output-failure cancellation, terminal-event ordering, and UV rotation termination.

qiyinxi and others added 7 commits September 7, 2026 15:58
真机初始化夹具暴露 workspace.clone 脉冲按 sideband Write 次数计数,
64 条预算在握手瞬间耗尽,packfile 传输的 7.9 秒零事件。登记 T13.14,
设计为注入 ticker 的 1 秒时间心跳;仍只发不带数值的 running,不触碰冻结契约。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
workspace.clone 的 running 脉冲原按 go-git sideband 的 Write 次数计数(上限 64),
而 sideband 文本集中在握手瞬间涌入,预算当场耗尽,之后 packfile 传输阶段零事件。
改为 Fetcher 在 Clone 前启动注入 ticker 的 1 秒心跳 goroutine,Clone 返回后先停止
并等待其退出再判定输出故障与终态;心跳仍只发不带数值的 running,sideband 文本
继续丢弃、不解析、不外发。心跳输出失败取消传输 context 并优先映射 OUTPUT_WRITE_FAILED。

测试:假时钟下同一瞬间多次 Write 不产生脉冲、5 秒静默 ≥ 4 条心跳、事件不含 Git
原文与数值字段、输出失败停止心跳并取消传输、终态之后不再出现 running(顺序断言)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
设计文档的数据流漏了 newCloneHeartbeat 的 interval 参数、把 quit 通道写成 stop,
TDD 计划少列了输出失败映射那条测试;AGENTS.md 的 M13 状态行补上 T13.14。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
5deb1d8 给 uv 下载接上字节进度回调后,下载器把回调错误包成
Kind=FailureProgress 的 DownloadFailure,但 bootstrap 的 download 闭包只认
Published 与校验和失败,其余一律 OutcomeSwitchSource。协议输出一断,5 个 uv
镜像源会被各空跑一次(每次都先发 HTTP 请求拿到响应头才触发首个回调),attempt
报告被标成 download_failed,uv 层错误码也降级成 UV_DOWNLOAD_FAILED,与设计文档
「协议写失败沿错误链保留 OUTPUT_WRITE_FAILED」冲突。

新增 isDownloadProgressFailure 谓词,命中时记下错误并以 OutcomeTargetFailure
结束整轮轮换;轮换返回后原样返回该错误(不经 wrapBootstrapError,避免用户可见
文案变成「uv 准备失败」)。对外错误码/退出码/message 本就取最外层四元组,
修复前后字节不变。写法对齐 dependencies_mirror.go 的报告失败与 gitrepo/clone.go
的 emit 失败先例。

测试:TestBootstrap_DownloadProgressFailureStopsRotation 与
TestUVDownloadProgress_OutputFailureStopsRotation 均用真实 Rotator(假 rotator
只调一次尝试且不看 Outcome.Kind,正是此前漏测的原因),修复前二者均报
calls = 5,修复后下载器只调 1 次、错误链保留原因且不是 RotationError,
classifyFailure 得到 OUTPUT_WRITE_FAILED / uv.download。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
human_renderer 用 FormatFloat(-1) 原样打印 percent,而下载器给的 percent 是
received/total*100 未取整,human 模式会输出 percent=42.857142857142854%。

只改渲染端:先 math.Round(percent*100)/100 再格式化。NDJSON 数值不动——设计文档
要求三个数值逐字来自 mirror.DownloadProgress、CLI 不二次估算,且生产端取整会在
99.995% 时提前打出 100;架构文档也写明 human 模式不承诺机器可解析。

human 契约矩阵中 1e-9 那条快照期望由 percent=0.000000001% 改为 percent=0%,
42.86% 与 50% 两条不受影响。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
设计文档补「进度回调失败以 OutcomeTargetFailure 结束整轮镜像轮换,不逐源重试」;
任务拆分在 T13.12 条目下登记 2026-09-07 复核修正证据(7c7fc67 / d4fef5a)并在
变更记录加一行。T13.12 的完成状态文字不动。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @qiyinxi, you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 5 days and 2 hours by commenting @sourcery-ai review. Upgrade to get a review now.

@sourcery-ai

sourcery-ai Bot commented Sep 7, 2026

Copy link
Copy Markdown

审查者指南

本 PR 独立修正三处进度可观测性问题:以可注入 ticker 驱动克隆传输心跳并确保生命周期和错误收口,以 TargetFailure 终止 uv 进度输出失败后的镜像轮换并保留原始错误,同时将 human 模式百分比限制为两位小数而保持 NDJSON 数值不变。

克隆传输心跳生命周期时序图

sequenceDiagram
    participant Fetcher
    participant Heartbeat as cloneHeartbeat
    participant Ticker
    participant Git as go-git
    participant Progress as progressEmitter

    Fetcher->>Heartbeat: start()
    Heartbeat->>Ticker: newTicker(1 second)
    Fetcher->>Git: Clone(cloneCtx, Progress=Heartbeat)
    Git->>Heartbeat: Write(payload)
    Heartbeat-->>Git: len(payload)
    loop Every tick during Clone
        Ticker-->>Heartbeat: tick
        Heartbeat->>Progress: emit ProgressEvent(running)
    end
    Git-->>Fetcher: Clone returns
    Fetcher->>Heartbeat: stop()
    Heartbeat-->>Fetcher: goroutine stopped
    Fetcher->>Heartbeat: Err()
    Fetcher-->>Progress: emit terminal event
Loading

UV 进度失败停止镜像轮换流程图

flowchart TD
    Download["download()"] --> Rotator[rotator.Run]
    Rotator --> Attempt[Download attempt]
    Attempt --> Failure{isDownloadProgressFailure}
    Failure -->|yes| TargetFailure[OutcomeTargetFailure]
    TargetFailure --> ReturnError[Return original progress error]
    Failure -->|no| OtherOutcome[Other attempt outcome]
    OtherOutcome --> Retry[Mirror rotation may continue]
    ReturnError --> Output[OUTPUT_WRITE_FAILED preserved]
Loading

human 模式进度百分比渲染流程图

flowchart LR
    Event[ProgressEvent.Percent] --> Human[HumanRenderer.RenderProgress]
    Human --> Round[math.Round to two decimal places]
    Round --> Text[Human-readable percent output]
    Event --> NDJSON[NDJSON numeric value]
    NDJSON --> Unchanged[Value remains unchanged]
Loading

文件级变更

变更 详情 文件
将 workspace.clone 的进度通知从 sideband 写入计数改为可测试、可停止的固定间隔时间心跳。
  • 注入 ticker 工厂并以 1 秒间隔发出不带数值的 running 事件。
  • 继续实现 io.Writer 但丢弃 Git sideband 文本,避免泄露外部文案或 URL。
  • 输出失败时取消 clone context;Clone 返回后同步停止并等待心跳 goroutine,再发送终态。
  • 新增假时钟测试,覆盖静默传输、sideband 不触发脉冲、输出失败和终态事件顺序。
internal/gitrepo/clone.go
internal/gitrepo/clone_test.go
AGENTS.md
doc/current/README.md
doc/current/M13/设计-T13.14-克隆传输期时间心跳.md
任务拆分.md
让 uv 下载进度回调失败立即终止镜像轮换,并保留原始协议输出错误。
  • 识别 FailureProgress 并返回 OutcomeTargetFailure,避免继续尝试其他镜像。
  • 绕过 bootstrap 错误包装,保持 OUTPUT_WRITE_FAILED、原始错误链和用户可见文案。
  • 使用真实 Rotator 新增测试,验证下载器只调用一次且不返回 RotationError。
internal/uv/bootstrap.go
internal/uv/bootstrap_test.go
internal/cli/m5_test.go
doc/current/M13/设计-T13.12-初始化长耗时阶段进度可观测.md
任务拆分.md
仅调整 human 模式的百分比格式化精度,不改变 NDJSON 中的进度数值。
  • 渲染百分比时四舍五入至两位小数。
  • 更新 human renderer 合约测试。
internal/protocol/human_renderer.go
internal/protocol/human_renderer_test.go

提示和命令

与 Sourcery 交互

  • 触发新的审查: 在 pull request 中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的审查评论。
  • 根据审查评论生成 GitHub issue: 回复审查评论,请 Sourcery 根据该评论创建 issue。你也可以回复审查评论并使用 @sourcery-ai issue,根据该评论创建 issue。
  • 生成 pull request 标题: 在 pull request 标题的任意位置写入 @sourcery-ai,即可随时生成标题。你也可以在 pull request 中评论 @sourcery-ai title,随时(重新)生成标题。
  • 生成 pull request 摘要: 在 pull request 正文中任意位置写入 @sourcery-ai summary,即可在指定位置随时生成 PR 摘要。你也可以在 pull request 中评论 @sourcery-ai summary,随时(重新)生成摘要。
  • 生成审查者指南: 在 pull request 中评论 @sourcery-ai guide,即可随时(重新)生成审查者指南。
  • 解决所有 Sourcery 评论: 在 pull request 中评论 @sourcery-ai resolve,即可解决所有 Sourcery 评论。如果你已经处理完所有评论且不想再看到它们,这会很有用。
  • 忽略所有 Sourcery 审查: 在 pull request 中评论 @sourcery-ai dismiss,即可忽略所有现有的 Sourcery 审查。如果你想从新的审查开始,这尤其有用——别忘了评论 @sourcery-ai review 来触发新的审查!

自定义你的使用体验

访问你的控制面板

  • 启用或禁用审查功能,例如 Sourcery 生成的 pull request 摘要、审查者指南等。
  • 更改审查语言。
  • 添加、删除或编辑自定义审查说明。
  • 调整其他审查设置。

获取帮助

Original review guide in English

Reviewer's Guide

本 PR 独立修正三处进度可观测性问题:以可注入 ticker 驱动克隆传输心跳并确保生命周期和错误收口,以 TargetFailure 终止 uv 进度输出失败后的镜像轮换并保留原始错误,同时将 human 模式百分比限制为两位小数而保持 NDJSON 数值不变。

Sequence diagram for clone transfer heartbeat lifecycle

sequenceDiagram
    participant Fetcher
    participant Heartbeat as cloneHeartbeat
    participant Ticker
    participant Git as go-git
    participant Progress as progressEmitter

    Fetcher->>Heartbeat: start()
    Heartbeat->>Ticker: newTicker(1 second)
    Fetcher->>Git: Clone(cloneCtx, Progress=Heartbeat)
    Git->>Heartbeat: Write(payload)
    Heartbeat-->>Git: len(payload)
    loop Every tick during Clone
        Ticker-->>Heartbeat: tick
        Heartbeat->>Progress: emit ProgressEvent(running)
    end
    Git-->>Fetcher: Clone returns
    Fetcher->>Heartbeat: stop()
    Heartbeat-->>Fetcher: goroutine stopped
    Fetcher->>Heartbeat: Err()
    Fetcher-->>Progress: emit terminal event
Loading

Flow diagram for UV progress failure stopping mirror rotation

flowchart TD
    Download["download()"] --> Rotator[rotator.Run]
    Rotator --> Attempt[Download attempt]
    Attempt --> Failure{isDownloadProgressFailure}
    Failure -->|yes| TargetFailure[OutcomeTargetFailure]
    TargetFailure --> ReturnError[Return original progress error]
    Failure -->|no| OtherOutcome[Other attempt outcome]
    OtherOutcome --> Retry[Mirror rotation may continue]
    ReturnError --> Output[OUTPUT_WRITE_FAILED preserved]
Loading

Flow diagram for human progress percentage rendering

flowchart LR
    Event[ProgressEvent.Percent] --> Human[HumanRenderer.RenderProgress]
    Human --> Round[math.Round to two decimal places]
    Round --> Text[Human-readable percent output]
    Event --> NDJSON[NDJSON numeric value]
    NDJSON --> Unchanged[Value remains unchanged]
Loading

File-Level Changes

Change Details Files
将 workspace.clone 的进度通知从 sideband 写入计数改为可测试、可停止的固定间隔时间心跳。
  • 注入 ticker 工厂并以 1 秒间隔发出不带数值的 running 事件。
  • 继续实现 io.Writer 但丢弃 Git sideband 文本,避免泄露外部文案或 URL。
  • 输出失败时取消 clone context;Clone 返回后同步停止并等待心跳 goroutine,再发送终态。
  • 新增假时钟测试,覆盖静默传输、sideband 不触发脉冲、输出失败和终态事件顺序。
internal/gitrepo/clone.go
internal/gitrepo/clone_test.go
AGENTS.md
doc/current/README.md
doc/current/M13/设计-T13.14-克隆传输期时间心跳.md
任务拆分.md
让 uv 下载进度回调失败立即终止镜像轮换,并保留原始协议输出错误。
  • 识别 FailureProgress 并返回 OutcomeTargetFailure,避免继续尝试其他镜像。
  • 绕过 bootstrap 错误包装,保持 OUTPUT_WRITE_FAILED、原始错误链和用户可见文案。
  • 使用真实 Rotator 新增测试,验证下载器只调用一次且不返回 RotationError。
internal/uv/bootstrap.go
internal/uv/bootstrap_test.go
internal/cli/m5_test.go
doc/current/M13/设计-T13.12-初始化长耗时阶段进度可观测.md
任务拆分.md
仅调整 human 模式的百分比格式化精度,不改变 NDJSON 中的进度数值。
  • 渲染百分比时四舍五入至两位小数。
  • 更新 human renderer 合约测试。
internal/protocol/human_renderer.go
internal/protocol/human_renderer_test.go

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@qiyinxi
qiyinxi merged commit 945673f into AUTO-MAS-Project:main Sep 7, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant