Skip to content

Improve managed Git updates with remote preflight and safe staged refresh - #13

Open
Lowerce wants to merge 5 commits into
VintLin:mainfrom
Lowerce:fix/git-update-fast-path
Open

Improve managed Git updates with remote preflight and safe staged refresh#13
Lowerce wants to merge 5 commits into
VintLin:mainfrom
Lowerce:fix/git-update-fast-path

Conversation

@Lowerce

@Lowerce Lowerce commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR improves the managed Git source update path by:

  1. Skipping checkout preparation when the locked commit already matches the selected remote branch.
  2. Preserving branch identity throughout remote preflight, staged refresh, clean clone, and archive fallback.
  3. Validating managed checkout ownership before reading, renaming, or replacing a checkout.
  4. Protecting active long-running mutations with PID-aware lock handling.
  5. Applying bounded desktop update timeouts that scale with the selected source count.

The implementation follows the existing source authority, partial-update, staged replacement, and bridge execution models.

Motivation

Previously, a managed Git update prepared a new checkout before determining whether the remote commit had changed. This caused unnecessary Git and filesystem work for sources that were already current.

Long-running updates could also cross two existing limits:

  • A desktop bridge command could time out before a legitimate multi-source update completed.
  • A filesystem lock could be considered stale based only on age, even while its owning process was still running.

The update path also relied on lock.localPath during checkout replacement. Managed checkout ownership therefore needed to be verified before any remote preflight, local reuse, rename, or replacement operation.

Core changes

Remote commit preflight

For managed Git sources with a locked commit, the update path now:

  • normalizes the Git locator;
  • queries the exact locked branch when originBranch is available;
  • compares the remote commit with the locked commit;
  • inspects local checkout integrity when both commits match;
  • skips checkout preparation when the checkout is healthy;
  • performs the established full update flow when the remote commit cannot be verified.

This makes the confirmed-unchanged path inexpensive while preserving the existing recovery behavior for uncertain or damaged states.

Branch-preserving staged refresh

When the remote commit has changed, the update path:

  1. Attempts to prepare a temporary checkout from the existing managed Git checkout.
  2. Sets the requested remote locator.
  3. Fetches the exact remote ref.
  4. Checks out FETCH_HEAD in detached mode.
  5. Validates the prepared source snapshot.
  6. Atomically replaces the managed checkout.

If local preparation fails, the update continues through the established clean clone, HTTPS fallback, and archive fallback sequence.

When lock.originBranch is present, the same branch is used consistently by remote preflight, staged fetch, clean clone, and archive fallback. The clean-fetch path explicitly fetches refs/heads/<originBranch> before accepting the result, ensuring that the final checkout resolves to the requested branch rather than a same-named tag.

Locks without originBranch retain the established default-branch behavior.

Managed checkout ownership validation

Before any update-side checkout operation, the service verifies that:

  • lock.localPath exactly matches <stateRoot>/source/<sourceKind>/<sourceId>;
  • the normalized path remains under the managed source root;
  • the source root, source-kind directory, and checkout are not symbolic links;
  • resolved real paths remain under the real managed source root.

An invalid path returns SOURCE_CHECKOUT_PATH_INVALID before the referenced checkout is read or modified. External sources continue to leave the managed update path before managed checkout validation.

PID-aware mutation locking

Mutation lock recovery now considers the owner process:

  • a confirmed live owner keeps the lock;
  • a confirmed dead owner allows immediate reclamation;
  • missing or unreadable owner metadata uses the existing age-based stale rule;
  • reclamation first renames the lock to a quarantine path before removing it.

This keeps long-running updates serialized without leaving dead locks permanently behind.

Bounded desktop update execution

Desktop update commands use a dedicated timeout budget:

  • one explicitly selected source: 5 minutes;
  • two sources: 10 minutes;
  • three or more sources: 15 minutes;
  • update all: 15 minutes.

Selected source IDs are deduplicated before calculating the timeout. Overflow is handled explicitly, and the result is always capped at 15 minutes.

Update flow

Validate managed checkout ownership
  -> Read the locked commit and branch
  -> Query the corresponding remote ref
  -> If unchanged, verify local integrity and return
  -> If changed or uncertain, prepare a staged checkout
  -> Validate the staged snapshot
  -> Atomically replace the managed checkout
  -> Persist the updated lock and inventory

Per-source fetch failures continue through the existing partial-update result model. Authority failures such as an invalid managed checkout path stop before checkout preparation.

Verification

  • npm run build
  • @skill-flow/integration: fs-utils.test.ts — 6 passed
  • @skill-flow/core-engine: source-checkout-service.test.ts and source-authority-service.test.ts — 22 passed
  • @skill-flow/query: source-lifecycle.test.ts — 50 passed
  • Xcode 26.5 GitHub Actions validation: updated-source tests, macOS production target compilation, release packaging, and artifact validation passed

中文概要

本 PR 对托管 Git 来源更新链路进行了以下改进:

  1. 当 lock commit 已与目标远端分支一致时,跳过 checkout 准备。
  2. 在远端预检、暂存刷新、干净 clone 和 archive fallback 中保持一致的分支身份。
  3. 在读取、重命名或替换 checkout 之前验证托管路径所有权。
  4. 通过感知 PID 的锁处理保护长时间运行的 mutation。
  5. 根据来源数量为桌面更新分配有明确上限的执行时间。

实现继续沿用现有的 source authority、partial update、暂存替换和 bridge 执行模型。

背景

修改前,托管 Git 来源在确认远端 commit 是否变化之前,就会准备新的 checkout。因此,即使来源已经是最新状态,也会发生不必要的 Git 和文件系统操作。

长时间更新还可能跨越两个既有限制:

  • 桌面 bridge 可能在合法的多来源更新完成之前超时。
  • 文件锁只根据时间判断 stale,即使持有锁的进程仍在运行,也可能被其他进程回收。

更新流程还会在替换 checkout 时使用 lock.localPath,因此必须在远端预检、本地复用、rename 和 replacement 之前验证托管路径所有权。

核心改进

远端 commit 预检

对于已经记录 commit 的托管 Git 来源,更新流程现在会规范化 Git locator,在存在 originBranch 时查询该精确分支,并比较远端 commit 与 lock commit。Commit 相同且本地 checkout 完整时会直接跳过 checkout 准备;无法确认远端 commit 时则进入既有的完整更新流程。

这使已经确认未变化的来源能够快速完成,同时保留不确定状态和损坏状态下的恢复行为。

保持分支身份的暂存刷新

当远端 commit 已发生变化时,更新流程会:

  1. 尝试从现有托管 Git checkout 准备临时 checkout。
  2. 设置请求的远端 locator。
  3. Fetch 精确的远端 ref。
  4. 以 detached 模式 checkout FETCH_HEAD
  5. 校验准备好的 source snapshot。
  6. 原子替换托管 checkout。

如果本地准备失败,则继续使用既有的 clean clone、HTTPS fallback 和 archive fallback 流程。

lock.originBranch 存在时,远端预检、暂存 fetch、干净 clone 和 archive fallback 统一使用同一分支。干净下载完成后还会明确 fetch refs/heads/<originBranch>,确保最终 checkout 对应请求的分支,而不是同名 tag。

没有记录 originBranch 的 lock 继续使用既有的默认分支行为。

托管 checkout 所有权校验

在任何更新侧 checkout 操作之前,服务会验证:

  • lock.localPath<stateRoot>/source/<sourceKind>/<sourceId> 完全一致;
  • 规范化路径仍位于托管 source root 内;
  • source root、source kind 目录和 checkout 不是符号链接;
  • realpath 解析结果仍位于真实的托管 source root 内。

路径不合法时返回 SOURCE_CHECKOUT_PATH_INVALID,并在读取或修改目标 checkout 之前终止。External source 会在托管 checkout 校验之前退出该更新路径。

感知 PID 的 mutation lock

Mutation lock 回收现在会判断持有进程的状态:

  • 确认进程存活时保留锁;
  • 确认进程已经退出时立即允许回收;
  • owner 信息缺失或无法读取时使用既有的时间 stale 规则;
  • 回收时先把锁 rename 到隔离路径,再执行删除。

这可以保证长时间更新仍然保持串行,同时避免已经失去 owner 的锁永久残留。

有上限的桌面更新

桌面 update 命令使用独立的执行时间预算:

  • 明确选择 1 个来源:5 分钟;
  • 2 个来源:10 分钟;
  • 3 个或更多来源:15 分钟;
  • Update All:15 分钟。

计算时间前会对 source ID 去重,同时显式处理整数溢出,最终结果始终不超过 15 分钟。

更新流程

验证托管 checkout 所有权
  -> 读取 lock commit 和 branch
  -> 查询对应远端 ref
  -> 未变化时检查本地完整性并返回
  -> 已变化或无法确认时准备暂存 checkout
  -> 校验暂存 snapshot
  -> 原子替换托管 checkout
  -> 持久化更新后的 lock 和 inventory

普通来源 fetch 失败继续遵循既有的 partial-update 结果模型。托管路径不合法等 authority failure 会在 checkout 准备前终止。

验证

  • npm run build
  • @skill-flow/integrationfs-utils.test.ts — 6 项通过
  • @skill-flow/core-enginesource-checkout-service.test.tssource-authority-service.test.ts — 共 22 项通过
  • @skill-flow/querysource-lifecycle.test.ts — 50 项通过
  • Xcode 26.5 GitHub Actions 验证:更新专项测试、macOS production target 编译、release 打包和 artifact 校验均通过

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