- 桌面应用
【免费下载链接】Vortex
Vortex Development
本文基于 Vortex 仓库watch-log技能包中的 §F 模式文档 collection-install.md,讲解如何对一个“集合(collection)安装”做完整日志审计:从识别安装信封(envelope)、逐成员生命周期追踪,到“是否完全安装”的可计算判定,以及九大安装完成不变量各自的破坏签名。读完本文,你可以照着 Steps 六步流程,把一次 Vortex 集合安装从日志中审计成一份“每个问题都有名字”的结论报告,并对应的源码锚点都能定位到具体文件。
1. 定位:§F 是什么,和 §E 有何不同
§F 是 watch-log 技能 的六种分析模式之一。它的前置知识来自三份文档:reference.md(核心)、shared/lifecycle.md(生命周期标记)与 shared/sessions.md(会话边界与终止分类)。
§E(trace 模式)沿一个实体追踪它在哪里卡住;§F 则对整个集合安装做审计:对照安装完成不变量(install-completion invariants),找出具体破坏了哪一条。审计范围默认限定在一个会话内(默认最新会话,按 §B 规则界定),且必须在报告中明确说明用的是哪个会话。
文档还给出一个关键提醒:日志文本会随代码演进漂移(log text drifts),在依赖“某条消息缺失”作为结论前,必须先 grep 各表中列出的源码文件,确认消息字符串仍然存在。
这些不变量是 LAZ-483 建立的契约,因此对 dev 集合安装运行同一套审计,同时就是一份483 回归检查。
2. 识别目标集合
审计的第一步是确定“审哪个集合”(Identify the collection):
- 从用户参数(
$ARGUMENTS)中取得集合名/ID,或“the collection install”这类泛指; - 若都没有,就在限定范围内的日志中 grep
starting install of collection,列出候选项(带上collationId与totalMods)供用户选择; - 成员(member)线程靠
modId/referenceTag/collationId,以及安装日志上携带的sourceModId(即 collection id)来串联。
starting install of collection锚点的真实出处是集合安装驱动器 InstallDriver.ts#L1023-L1026:
log("info", "starting install of collection", { totalMods: required.length, missing: this.requiredMods.length, });注意这里的totalMods取的是必需成员总数(required.length),而missing是本次运行开始前仍需安装的成员数——这两个数字正是第 5 节“完全安装判定”公式的输入。
3. 信封:集合级生命周期锚点
集合级信封(envelope)由以下日志锚点构成,它们把一次集合安装框成一段可验证的时间线(Envelope):
| 日志消息 | 出处 | 含义 |
|---|---|---|
starting install of collection {totalMods, missing} | InstallDriver(InstallDriver.ts#L1023) | 安装起点锚 |
did install dependencies {modId} | InstallDriver(InstallDriver.ts#L674) | 依赖阶段完成 |
add collection rule | postprocessCollection(postprocessCollection.ts#L57) | 集合排序规则已应用 |
postprocess collection | postprocessCollection(postprocessCollection.ts#L83) | 成功终态锚 |
pause collection/resume collection/failed to pause collection/User logged out during collection install, pausing | collections/index.ts(如 L364、L1604) | 暂停/恢复/登出暂停 |
already installing a collection | InstallDriver(InstallDriver.ts#L402) | 第二次安装请求被拒绝 |
从源码结构看,postprocessCollection之所以是“成功终态锚”,是因为它做完了集合安装收尾的全部事情:先log("info", "postprocess collection"),再应用modRules排序规则(每条新规则打出add collection rule),然后触发deploy-mods部署并调用各扩展的parse钩子(postprocessCollection.ts#L76-L104)。因此“到达postprocess collection”在日志上等价于“集合安装走到了终态”。
4. 逐成员生命周期与 SSOT 状态
每个成员(依赖)的通用安装标记沿用 shared/lifecycle.md 中 §E 使用的序列:
start mod install → installing to {modId, destinationPath} → extracting mod archive → invoking installer → finish mod install {outcome} → Installation completed successfully {installId, modId, duration}它们分别落在 InstallContext.ts#L174(start mod install)、InstallManager.ts#L1707(installing to)、InstallManager.ts#L3977(extracting mod archive)、InstallManager.ts#L4112(invoking installer)与 InstallManager.ts#L1221(Installation completed successfully)。
集合侧的“下载-安装交接”由 InstallManager 打出:Found collection for download(InstallManager.ts#L703)与Found collection for failed download(InstallManager.ts#L794)。
这里有一条审计上的重要边界:SSOT 状态写入本身不打日志。{type:"status", status:"downloaded"|"failed"|...}这类状态写入(通过writeCollectionSession,实现见 collectionSessionWrite)直接落到会话 hive(state.session.collections),不是日志字符串。所以要“观察”这些状态,只能通过上述安装生命周期日志间接推断,必要时借助persist:diff {hive:"session"}的到达记录(§C 的标记,持久化通道见 persistenceIPC.ts#L29)。
5. 完全安装判定(每份报告必须回答)
每份集合审计报告必须显式回答一个判词:该集合是否“完全安装”(fully installed)——不能只说“本次运行结束了”。计算方式(Fully-installed verdict):
- 从信封数字入手:
starting install of collection {totalMods, missing}给出成员总数与开始时仍需安装的数量,因此totalMods - missing在本次运行前已经安装; - 统计限定窗口内打出
Installation completed successfully的成员数; - 得
installed = (totalMods - missing) + completed-this-run。
三类结论:
- Fully installed—— 到达
postprocess collection,且installed == totalMods,没有失败/卡住的成员; - Completed, not fully installed—— 到达 postprocess 但有缺口,须点名(X 个失败、Y 个 skipped-or-unaccounted)。注意
totalMods包含可选(recommends)成员,而可选成员默认跳过(InstallDriver.ts#L1004-L1012 中对未显式决策的 optional 调用markCollectionMemberSkipped);跳过(用户跳过、可选默认)只写会话状态,成功路径没有日志行——所以“没有安装线程、也没有失败”的成员应归类为 “skipped or unaccounted”,本身不是错误。唯一的跳过日志出现在失败路径:could not find collection rule for skipped download(collectionSkip.ts#L138); - Not completed—— 从未到达
postprocess collection:卡住/被打断/被搁置(按不变量 9 分类),同样给出上述计数。
6. 九大不变量与破坏签名
下面是完整不变量表——健康安装必须成立的事实,以及每条被破坏时在日志里出现的签名(Invariants to assert):
| # | 不变量(健康安装成立的事实) | 日志中的破坏签名 | 源码位置 |
|---|---|---|---|
| 1 | 每个成员都到达终态(installed 或 failed) | Called callback for stuck installation(某成员卡在 "installing");或 stall-rescue 流程触发:Collection install stalled, attempting rescue之后出现Collection install stalled after rescue attempt, resolving(安装器强制解析了停滞阶段,成员可能被留在非终态) | InstallManager |
| 2 | 失败的下载会把成员结算为 failed(并让阶段继续推进) | Found collection for failed download之后没有跟随的成员 failed 结算;或Collection is not currently installing - ignoring download failure在集合确实在安装时触发 | InstallManagerhandleDownloadFailed(InstallManager.ts#L775-L847) |
| 3 | 没有重排循环(requeue loop) | 同一modId的start mod install运行次数超过MAX_DEPENDENCY_RETRIES(值为 3,定义于 InstallManager.ts#L567) | InstallManager |
| 4 | 磁盘满会让成员终结性地失败 | 解压遇到磁盘满,但成员既没完成也没打出 "Not enough disk space" 终态失败(失败模式:requeue → skip → stuck) | InstallManager(InsufficientDiskSpace,抛出点 InstallManager.ts#L4014) |
| 5 | 孤儿归档(磁盘上有文件、无下载记录)在重新下载前被对齐(reconcile) | 磁盘上存在孤儿归档,但依赖下载停滞时没有出现reconciling orphaned archive before download {fileName, gameId}(reconcileOrphanedArchive.ts#L39) | reconcileOrphanedArchive.ts |
| 6 | 集合到达 postprocess | 有starting install of collection但会话结束前没有postprocess collection/did install dependencies(阶段卡死);或Error during scheduled phase deployment(某阶段部署失败,可能阻塞推进) | InstallManager / InstallDriver / postprocessCollection |
| 7 | 暂停/恢复与登出是干净的 | pause collection/ 登出暂停没有配对的resume collection,或出现failed to pause collection | collections/index.ts |
| 8 | 进度值保持合理 | 下载/安装进度值超过 100(免费用户路径上 101/100 的freeUserDownloadPosition失败,见 collectionInstallSession.ts#L55) | InstallManager 免费用户路径 |
| 9 | 被打断的安装不丢进度;用户主动的 resume 保留已完成成员 | 重启前打出过Installation completed successfully的成员在重启后被重装(进度丢失);或resume collection重跑已完成成员/无法继续。注意:resume 是用户主动的,因此没有resume collection的搁置(parked)安装不是失败 | 会话 SSOT(state.session.collections) |
不变量 2 值得对照源码理解:handleDownloadFailed在确认集合正在安装后,会打聚合通知,然后必须调用writeCollectionSession(..., { type: "status", status: "failed" })结算成员并maybeAdvancePhase(InstallManager.ts#L836-L846)——代码注释明确说明:终结性的下载失败不会被重排,若不结算,成员会一直停在 "downloading" 并被 Failed 过滤器漏掉。这正是破坏签名“failed download 之后没有成员 failed 结算”的含义。
同理,不变量 3/4 对应依赖错误恢复的决策树planDependencyErrorRecovery:InsufficientDiskSpace与安装自身产生的ProcessCanceled被判为不可重试(重试注定重蹈覆辙),直接结算为 failed;普通瞬时错误则在MAX_DEPENDENCY_RETRIES(3)内 requeue(InstallManager.ts#L2675-L2708)。
6.1 停滞、救援与关键错误信号
安装器内置了 stall 检测安全网,以下信号一旦出现必须上报(Stall, rescue, and critical-error signals):
Collection install stalled, attempting rescue {sourceModId}([WARN],InstallManager.ts#L2833)——某阶段停滞(首次超时),救援进行中。还不是失败,但是预警信号。对应实现是pollAllPhasesComplete的 5 分钟无进展超时(STALL_TIMEOUT_MS = 5 * 60 * 1000,InstallManager.ts#L2795-L2858),首次超时会forceCleanupStuckInstalls并视需要重排已下载成员;Collection install stalled after rescue attempt, resolving {sourceModId}([WARN],InstallManager.ts#L2861)——救援未能解除阻塞(第二次超时),安装器强制解析该阶段。该阶段的成员可能停留在非终态:须结合逐成员汇总找出是哪些,并按停滞(关联不变量 1/6)上报;Critical error in dependency installation {downloadId, sourceModId}([ERRO],InstallManager.ts#L2756)——依赖安装抛出了未捕获异常;要确认成员是结算为failed而不是卡死;Error during scheduled phase deployment {sourceModId}([WARN],InstallManager.ts#L3443)——某阶段部署失败(关联不变量 6);Download not found when trying to resume {intendedId}([WARN],InstallManager.ts#L6388)——resume 时某个预期成员的下载缺失;关联不变量 9(resume 完整性)。
6.2 不变量 9 — 中断与恢复(数据丢失观测)
这是会话 SSOT 的核心承诺(LAZ-483 建立的保证):被重启/崩溃打断的安装,恢复时应从会话状态继续,不重做也不丢失已完成成员。每次中断都必须上报(它是真实信号),严重度框架如下(Invariant 9):
检测中断:一次starting install of collection,其会话在集合到达postprocess collection之前结束(下一个Vortex Version启动锚,或无postprocess collection的 EOF)。会话边界标记见 Application.ts#L213-L217(Vortex closing→clean application end)与 Application.ts#L465(Vortex Version)。
边界分类(沿用 shared/sessions.md / §B 的四态终止分类,不要塌缩成 clean-vs-crash):
- clean(
Vortex closing→clean application end) - killed-during-exit(有
Vortex closing,无 clean end;典型于 dev 停止/重启/调试器杀进程) - hard-crash(完全没有
Vortex closing,会话直接结束——真正的崩溃信号) - in-progress(最新会话,应用仍在运行)
严重度:
- prod(日志目录
$APPDATA/Vortex/):每次中断都是发现(finding)。安装中途硬崩溃是崩溃 bug;clean/killed 退出则是 resume 正确性与数据丢失隐患,需持续跟踪; - dev(日志目录
$APPDATA/@vortex/main/):中断某种程度是预期的(开发者重启测试机),要报告但标注为预期——除非开发者明确表示这次重启不是故意的(此时按 prod 对待,硬崩溃尤其如此)。
Resume + 进度丢失检查:在边界之后的会话里找resume collection(collections/index.ts#L1604)或同一集合的新starting install of collection,看它是否到达postprocess collection。然后比较边界之前打出Installation completed successfully的成员modId与边界之后重跑start mod install/ 完成安装的成员:
- 重启前已完成的成员被重装 =进度丢失/数据丢失(SSOT 未保住它们)——无论 dev/prod 一律上报;
- 已完成成员在 resume 时被正确跳过 = SSOT 保住了进度(通过);
- 没有任何
resume collection= parked,等待用户恢复——不是失败(resume 是用户主动行为)。报告为 parked/incomplete,而非 stuck。
prod 监控视角:对 prod 日志,这条不变量就是集合安装可靠性的现场信号。对每个被打断的安装,报告:边界类别、恢复后是否保留了“N 中已完成 M”式的进度(无重做)、resume 本身是否成功。硬崩溃边界、或任何“已完成成员被重装”(数据丢失)都是要升级处理的真实发现;无恢复尝试的 clean 退出则是良性的。
此外要上报“下载事件找不到归属”的诊断性忽略日志——某成员附近成批出现时,是不变量 1 或 2 即将被破坏的强提示:
No collection mod found for sourceModId(InstallManager.ts#L352)No active collection installation found(InstallManager.ts#L374)No matching rule found in collection for download(InstallManager.ts#L382)Collection mod not found in state(InstallManager.ts#L390)Skipping download failure - download not found(InstallManager.ts#L780)
7. 审计步骤(Steps)
按 Steps 执行:
- 解析日志集并按 §B 限定到会话;找到集合信封;
- 构建逐成员汇总:对每个成员线程(
modId),记录到达的阶段、结果(completed / failed /stuck)、完成时的duration(若有)以及重试次数(该modId的start mod install出现次数); - 逐条走不变量表;每个破坏点引用成员、匹配行与时间戳。“没有终态”视为 stuck(不变量 1/6),不是成功;
- 计算完全安装判定:信封是否到达 postprocess?
installed = (totalMods - missing) + completed-this-run对totalMods,缺口按 failed / stuck / skipped-or-unaccounted 分类; - 报告信封结果(是否到达
postprocess collection)以及任何暂停/恢复/登出转移是否配对。若未到达 postprocess,则执行不变量 9:找出结束它的会话边界、分类(clean / killed-during-exit / hard-crash / in-progress)、检查后续会话的 resume 与进度丢失,并套用 dev/prod 严重度框架; - 列出窗口内
[ERRO]/[WARN]中 payload 引用到成员的那些。
8. 输出格式(Output shape)
一份合规的 §F 审计输出包含以下要素(Output shape):
- 一行判词回答“该集合是否完全安装?”:fully installed / completed-not-fully-installed / not completed(stuck/ interrupted / parked),并附成员计数(installed / failed / skipped-or-unaccounted / stuck,共
totalMods); - 逐成员表:
modId· 到达阶段 · 结果 · 时长 · 重试次数; - 不变量结果:1–9 每条 pass /FAIL(附违规行),本会话未走到的路径标 n/a;
- 信封时间线(start → did install dependencies → add rule → postprocess)带 Δ 时间,外加暂停/恢复/登出事件;
- 中断(若有):边界分类(clean / killed / hard-crash / in-progress)、是否 resume 并到达 postprocess、进度丢失结论(重做 vs 保留);按严重度框架写明 dev-expected 还是真实发现;prod 一律视为发现;
- 异常清单:requeue 循环、stuck 成员、孤儿归档停滞、进度 >100、忽略下载成批出现、resume 时的数据丢失。
9. 小结
§F 把“这次集合安装到底成不成功”从一个模糊印象变成了可验证的判定:信封锚点给出骨架,逐成员汇总给出明细,九大不变量(对应 InstallManager.ts、InstallDriver.ts、postprocessCollection.ts、collections/index.ts、reconcileOrphanedArchive.ts 等实现)给出可 grep 的破坏签名,而不变量 9 则把重启/崩溃边界与数据丢失连成一条可审计的链。对 dev 安装,同一套审计顺带就是 LAZ-483 契约的回归检查。使用时注意两个前提:审计窗口按 shared/sessions.md 限定到单个会话,且依赖“消息缺失”的结论前必须先确认对应日志字符串仍在源码中存在。
- 桌面应用
【免费下载链接】Vortex
Vortex Development
相关推荐
DInvoke vs PInvoke:性能、安全性与灵活性的终极对比
DInvoke vs PInvoke:性能、安全性与灵活性的终极对比 在.NET开发中,调用非托管代码是一项常见需求,而DInvoke与PInvoke作为两种主
Warp 变更日志语言审查:Towncrier 片段措辞审查的三类标记、判断哲学与审计实践
Warp 变更日志语言审查:Towncrier 片段措辞审查的三类标记、判断哲学与审计实践 Warp 仓库用 Towncrier 片段( changelog/
高性能计算物理引擎图形学机器人Velog数据可视化:文章统计与阅读量分析功能完全指南
Velog数据可视化:文章统计与阅读量分析功能完全指南 Velog作为韩国最受欢迎的开发者博客平台,提供了强大的数据可视化功能,帮助博主深入了解文章表现。本文将
前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考