--- name: release-gate description: 用户要求准备或发布 CombatSolver 版本、生成 ZIP、创建版本标签、上传 Steam 创意工坊或夸克网盘、同步 GitHub、执行干净安装或完整发布验收时,按口令边界完成对应阶段;普通开发不触发外部发布。 --- # CombatSolver 发布、渠道上传与远端同步 ## 适用边界 先按用户口令确定唯一动作范围: | 用户口令 | 执行范围 | 不执行 | | --- | --- | --- | | `写更新日志` | 撰写当前开发版本的玩家更新日志 | 版本提升、构建、ZIP、标签、上传、推送 | | `准备发版` | 版本同步、更新日志、提交、一次 Release 构建、一次最小 ZIP | 标签、创意工坊、GitHub 推送、完整门禁 | | `给我审核/我拍板后再发` | 整理并提交更新日志草案 | 构建、ZIP、标签和外部动作,直到用户批准 | | `发版/发布` | 补齐准备阶段、创建 annotated tag,再用统一脚本发布创意工坊、GitHub Release 与夸克网盘 | 完整门禁、监控后台版本提示 | | `上传/更新创意工坊` | 上传当前已定版版本和玩家更新说明 | GitHub 推送、发布后复测、远端页面核对 | | `推送/同步远端` | 干净提交并推送当前分支;当前版本标签已存在时一并推送 | 构建、打包、创意工坊、创建无关标签 | | `完整发布门禁/完整验收/干净安装` | 执行第 7 节完整门禁 | 不省略用户点名的门禁项 | 用户声明活动发布批次时,批次规则优先:在用户结束批次前只提交到指定开发版本,不逐项定版。普通“发版”不等于完整发布门禁。 用户已确认:以后正式发版必须通过 `tools/release/publish-release.ps1` 同步发布创意工坊、GitHub Release 与夸克网盘。`准备发版`、普通开发提交和单独推送源码不运行统一发布脚本;只有进入 `发版/发布` 阶段时执行。监控后台的 `latestVersion` 由用户维护,agent 不读取或写入。 一句请求包含多个口令时,执行覆盖其全部要求的最窄阶段;“给我审核/我拍板后”始终暂停构建、标签和外部动作,直到用户批准。 ## 1. 版本与发布来源 - CombatSolver 的中文版本术语:`小版本`只将末位加一,`0.a.b → 0.a.(b+1)`;`大版本`将中间位加一、末位归零,`0.a.b → 0.(a+1).0`。不要套用通用语义化版本的 minor/major 定义。用户给出精确版本号时按该版本号执行;已发布版本不追溯改号。 - 同步 `CombatSolver.csproj`、`CombatSolver.json`、`docs/DEVELOPMENT_NOTES.md`、`docs/TEST_MATRIX.md` 和该版本玩家更新日志。 - 玩家更新日志使用当前游戏官方简中译名,只写玩家可感知的变化。开发日志中的根因、内部职责、runId、构建和测试信息不复制进去。 - 玩家更新日志和创意工坊 changeNote 同时提供简中与英文,使用各语言的游戏译名。PR 或其他外部贡献必须在对应变动条目的开头标明贡献者和 PR,并分别链接贡献者主页与 PR,例如“感谢 `[作者](%E4%B8%BB%E9%A1%B5)` 提交的 `[PR #123](%E9%93%BE%E6%8E%A5)`:……”/“`[PR #123](%E9%93%BE%E6%8E%A5)` by `[author](profile)`: …”,明确表示整项变动来自该贡献;禁止把致谢放在条目末尾,或在日志末尾另写脱离变动的笼统致谢段落。changeNote 直接采用同一版本玩家更新日志的完整中英正文,只允许将 Markdown 标题/列表语法转换为 Steam 富文本,不缩写、不合并或删减任何玩家变动、限制与致谢;转换到 Steam 富文本时也必须保留这两个链接。发布前逐项核对两种语言和两个渠道。若渠道长度限制无法容纳全文,停止该渠道并报告限制,不自行缩短。一次跨多个未上传版本时,先写好覆盖全部玩家变化的完整版本日志,再原样用于 changeNote。发布包含 UI 改动时参考 `../ui-localization/SKILL.md`,复用已完成的中英验证,不为了发包重复测试。 - 提交源码、fixture 和文档后,从该提交构建;记录该提交为 release source commit。构建后行为源码、编译配置、依赖或 manifest 变化才使构建失效,纯渠道暂存变化不会。 - “当前最新版”来自仓库中已同步、已提交且完成最小发包的最高版本,不来自游戏 Mods 目录或创意工坊暂存目录。 - 版本创建标签或成功上传创意工坊后即冻结。后续行为改动进入新的“下一版本(开发中)”,不回写已发布版本;用户未指定新版本号时保留待定,不自行猜版本。 - 普通发布不计算哈希。只有来源冲突、发布渠道争议或用户明确要求时才计算。 ### 玩家更新日志定稿规则 1. 以最近已发布版本为基线,描述升级后的最终体验;同一批次的未发布界面不是旧版。 2. 按玩家功能合并开发记录。新功能的试稿、反复调整、测试发现与修复过程保留在开发笔记,发布说明只介绍完成后的功能。 3. 修复条目只描述已发布版本中玩家可能遇到的现象及改善。新增功能在发布前修好的问题并入功能说明,不单列“修复”。 4. 使用游戏场景、操作与结果,避免索引、阈值、像素、容器布局等实现细节;例如写“重复出牌合并显示,标注次数,让长路线更易阅读”,不写“次数从标题行移到右侧,虚线下移两像素”。 5. 一项最终功能写一次;贡献者和 PR 链接保留在所属条目开头,中英文表达相同变化。用户已经删去的表述保持删除。 6. 定稿逐条核对已发布基线、最终默认行为和玩家收益,剔除未启用实验、开发过程、重复条目和没有证据的性能承诺,再将同一正文用于全部渠道。 ### Steam 更新日志排版合同 此规则适用于 `workshop.json` 的 `changeNote`,GitHub 与仓库更新日志继续使用 Markdown;创意工坊长介绍独立维护。 - 版本标题、简体中文与 English 小标题统一使用 `[b]文字[/b]`,段落使用真实换行,条目使用普通 `- ` 前缀。 - 链接转换为 `[url=https://…]文字[/url]`,贡献者与 PR 链接全部保留。正文只使用普通文本、换行、`[b]` 和 `[url]` 标签。 - 标题不得使用 `[h1]`、`[h2]`、`[h3]`;列表不得使用 `[list]`、`[*]` 或 `[olist]`,也不使用 `[quote]`、`[code]`、表格或原始 HTML 等块级排版。 - 原因与证据:2026-09-22 的 0.44.0 更新日志页面将正文放在带背景的 `

` 中,而 `[h1]` / `[h2]` 被转换成 `

`,浏览器会提前结束段落,导致正文脱框、上下出现空背景条。旧版的加粗标题与普通换行保持在框内。 - 上传前检查转换后的完整 `changeNote`:只含上述允许标签、标签成对、换行为真实换行;逐项核对两种语言的正文、条目数量与链接均与 Markdown 来源一致。不能只检查版本字符串或上传器退出码来判断排版正确。 - 上传成功后沿用既有不重复发布、不打开远端页面复核的规则;用户提供排版异常时按实际页面诊断,不重传二进制来修正文字。 固定格式示例(其中换行是实际换行): ```text [b]CombatSolver <版本号>[/b] [b]简体中文[/b] - 完整中文条目。 [b]English[/b] - Complete English entry. ``` ## 2. 一次 Release 构建 Windows(PowerShell 7): ```powershell dotnet clean -c Release pwsh -NoProfile -File tools\build\build-local-stack.ps1 -Configuration Release -ForPublication -PrivateConfigDirectory <私有配置目录> ``` Linux(Bash): ```bash dotnet clean -c Release ./tools/build/build-local-stack.sh --configuration Release --for-publication --private-config-directory <私有配置目录> ``` 私有配置目录必须包含 `presence.props` 和 `showcase.props`,可从本机部署定位文件确认;它不进入仓库或发布包。发布构建与统一发布脚本都会检查 DLL 中的在线连接元数据,缺失时停止。不要输出配置值。 从 release source commit 构建,不从游戏 Mods 目录反向复制 DLL,不复用未知来源旧构建。构建成功后不再反射 DLL 版本、重复构建、再次复制部署或重跑已经通过的行为场景。 目标行为已在当前行为源码上通过,之后只改版本号、文档或发布元数据时,Release 构建足以进入打包,不重复行为审计。 ## 3. 一次最小 ZIP 发布包统一输出到仓库根目录的 `releases/CombatSolver-<版本号>.zip`;当前工作区为 `D:\Desktop\sts2mod\CombatSolver\releases`。创建 ZIP 前确保目录存在,交付链接使用该路径,发布 ZIP 由现有 Git 忽略规则排除。 当前 `has_pck=false`。命令显式只写入: - `CombatSolver.json`; - 刚完成的 Release `CombatSolver.dll`; - Windows 发布还包括同次构建的 `CombatSolver.MemoryCleaner.exe`; - 根目录 `LICENSE`; - 根目录 `THIRD_PARTY_NOTICES.md`; - manifest 将来明确要求的其他资产。 `LICENSE` 和 `THIRD_PARTY_NOTICES.md` 是 MIT 许可及 Random Foreseer 来源署名文件,不得省略。不得加入源码、日志、问题包、存档、fixture、游戏依赖 DLL、`bin/obj/.godot/.local` 或旧 DLL。 ZIP 创建命令成功就是完成证据。不要重新打开、解压、枚举条目、读取 DLL 版本或计算哈希。 ### 3.1 夸克专用打包版 GitHub Release 继续上传第 3 节的最小 ZIP,创意工坊继续暂存五个发布内容文件。夸克网盘单独使用 `releases/CombatSolver-<版本号>-Quark.zip`:只保留 CombatSolver 最小包内容,不加入 RitsuLib 或其他依赖。包体不足时增加无压缩的 `QUARK_UPLOAD_PADDING.bin`,该条目不参与 Mod 加载,解压后可以删除。 统一发布脚本根据最小包的实际大小生成刚好够用的填充内容,并要求最终文件严格大于 `15 MiB`;未达到时停止发布。夸克打包版由 Git 忽略,不进入源码提交。 ## 4. 版本标签 - `准备发版` 不创建标签;用户批准并说 `发版/发布` 时,在 release source commit 创建 annotated tag `v`,消息为 `Combat Solver `。 - 如果用户跳过单独的“发版”口令,直接把已准备版本上传创意工坊,上传成功即表示该版本已经发布;此时在 release source commit 补建本地 annotated tag。 - 标签存在时不移动、不删除、不重建。标签目标与 release source commit 冲突时停止并报告,不猜测哪个版本正确。 - 创建本地标签不等于已同步 GitHub。只有用户要求推送远端时才推送该标签。 ## 5. 创意工坊上传 默认工具与暂存目录: - `D:\Desktop\sts2mod\ModUploader-win-x64\ModUploader.exe`; - `D:\Desktop\sts2mod\ModUploader-win-x64\CombatSolverWorkshop`。 Linux 不使用上述 Windows 路径。上传前必须设置 `COMBATSOLVER_MOD_UPLOADER` 和 `COMBATSOLVER_WORKSHOP_DIR`;任一路径未设置、不可执行或不存在时原样报告阻塞,不猜测其他目录或兼容层。 上传前只做一次本地暂存: 1. 用当前 release source 的 `CombatSolver.json`、刚完成的 Release DLL、Windows `CombatSolver.MemoryCleaner.exe`、根目录 `LICENSE` 和 `THIRD_PARTY_NOTICES.md` 覆盖 `CombatSolverWorkshop/content/`; 2. 保留标题、长描述、作者、封面、效果图、标签、依赖和可见性,除非用户明确要求修改或兼容性事实已经变化; 3. 按第 1 节“Steam 更新日志排版合同”将该版本完整简中和英文正文写入 `workshop.json` 的 `changeNote`,完成允许标签、正文与链接核对; 4. Windows 执行一次 `ModUploader.exe upload -w .\CombatSolverWorkshop`;Linux 执行一次 `"$COMBATSOLVER_MOD_UPLOADER" upload -w "$COMBATSOLVER_WORKSHOP_DIR"`。 创意工坊介绍已有 English / 简体中文两套,标题与正文来源维护于 `docs/workshop/`。官方 ModUploader 未指定语言时写 English,因此本地 workshop.json 的默认标题和 description 必须保持英文。单独维护任一种语言的标题或介绍时使用 `tools/release/WorkshopMetadata`([工具说明](../../../tools/release/WorkshopMetadata/README.md)),以 `docs/workshop/metadata.json` 和完整介绍为输入:先设置语言,再成对设置非空标题与完整介绍,最后提交并检查结果。简中使用明确的 `schinese`;不能把中文塞回默认 description,或仅改 tags 代替语言字段。标题与介绍按完整语言记录维护,单字段更新返回 OK 不能证明遗漏字段保持原值。元数据维护使用空 changeNote,只更新该语言的标题与介绍。 英文界面发布时语言 tags 包含 English 与 Simplified Chinese,保留其他标签;tags 用于发现,不能代替上述介绍语言字段。仅发包且介绍未变化时保留既有两种语言,不重复提交介绍。英文介绍和中文介绍各自保留依赖、单人限制、代码来源与许可署名。 普通发版保留线上展示图:上传工作区不放 `previews/` 目录,本地图片保存在 `previews-preserved/`。官方说明明确,目录不存在时线上展示图保持不变;存在时会同步完整列表,空目录会删除线上展示图,因此不能通过清空目录来跳过上传。只有用户明确要求更新展示图时才准备完整 `previews/`,当前顺序为中文、英文 1、英文 2、英文 3,对应 `01-cn1.jpg`、`02-en1.jpg`、`03-en2.jpg`、`04-en3.jpg`。主封面 `image.png` 独立处理,当前官方上传器要求其存在并每次上传,没有跳过参数;不得把跳过四张展示图说成所有图片都未上传。依据:[官方目录说明](https://github.com/megacrit/sts2-mod-uploader/blob/main/template/README.md) 与 [上传实现](https://github.com/megacrit/sts2-mod-uploader/blob/main/src/UploadCommand.cs)。 更新说明只写新增功能、实战结果修复、路线质量、UI/操作、兼容性和玩家能感知的性能变化。不要写类名、方法名、Beam/Mirror/GC 实现、runId、提交、构建、测试或打包过程。 上传命令报告成功就是远端完成证据。不要打开创意工坊页面、重新下载订阅内容、再次读取版本或重复上传。失败时只修正命令明确报告的原因,再重试一次;原因不明则原样报告。 ### 必做:统一发布脚本 正式发版只使用 `tools/release/publish-release.ps1` 作为三个渠道的执行入口。外部服务无法组成原子事务,脚本按创意工坊、GitHub、夸克网盘顺序执行,并在 `releases/CombatSolver-<版本号>.publish-state.json` 记录成功阶段;失败后从未完成阶段恢复,不重复已经成功的渠道。 脚本执行前必须满足: - 当前分支为 `main`,已跟踪文件干净,manifest、annotated tag 与 HEAD 的 release source commit 一致; - 第 3 节的 `releases/CombatSolver-<版本号>.zip`、同版本中英玩家更新日志、Release DLL、MemoryCleaner、`LICENSE` 与第三方来源说明均存在; - `workshop.json` 的 changeNote 已按本版本更新;脚本只暂存五个发布内容文件,保留工坊介绍、语言、封面、示例图、标签和依赖; - 完整读取全局 `quarkclouddrive` skill 的 `SKILL.md`、`references/file-search.md`、`references/file-ops.md` 与 `references/file-upload.md`。将用户本次发布原话和同一对话的夸克 session ID 传给脚本,不把授权码写进参数、仓库或状态文件。 - Windows 统一脚本直接使用已安装的 Node 与夸克 CLI,不调用 Bash 安装器;缺少任一命令或文件时原样停止。 Windows 入口: ```powershell pwsh -NoProfile -File tools\release\publish-release.ps1 -Version <版本号> -QuarkSessionInput "<用户本次发布原话>" -QuarkSessionId "" ``` 夸克网盘固定使用 `战斗路线求解器` 下三个发布子目录: 1. 浏览 `最新版` 的全部直接子项,把目标版本 ZIP 以外的原文件移动到 `老版本`; 2. 生成第 3.1 节的 `CombatSolver-<版本号>-Quark.zip`,不加入 RitsuLib,并按需生成填充条目使包体严格超过 15 MiB; 3. 将夸克专用打包版上传到 `最新版`;恢复执行时若目标文件已存在则复用; 4. 将 `docs/releases/<版本号>-RELEASE_NOTES.md` 上传到 `更新日志`;同名日志已存在时复用。 脚本每次发布重新按名称查询唯一目录并消费完整查询 Artifact,不硬编码目录 FID。夸克移动和上传必须返回成功;成功后不再搜索、下载或重复上传验证。脚本完成后只汇报三个渠道结果;监控后台版本提示由用户自行维护。 ### Steam 工坊 `FileNotFound` 排查 - `k_EItemUpdateStatusInvalid` 与 `k_EResultFileNotFound` 不一定表示暂存目录缺文件。若工作区的 `image.png`、`workshop.json`、`content/` 和 `mod_id.txt` 已通过一次本地读取确认存在,先读取 Steam 客户端日志 `D:\Steam\logs\workshop_log.txt` 中对应时间和 AppID 的记录。 - 如果日志写明 `Getting Workshop info for item failed : File Not Found`,而工坊页面和条目仍存在,根因是 Steam 客户端当前无法查询条目,通常与 Steam CM/网络连接状态有关,不要改名、删除或重建暂存文件。等待客户端恢复登录连接后,按第 5 节的原命令重试一次。 - 这类重试期间以 Steam 日志中的 `Uploaded new content ...` 和 `Upload finished for workshop item : OK` 作为实际成功证据;上传器末尾若同时打印中间状态 `k_EItemUpdateStatusInvalid`,以最终成功行和 Steam 日志为准,不再重复上传。 ## 6. GitHub 干净提交与推送 用户已确认:以后正式发版由第 5 节统一脚本创建 GitHub Release、上传最小 ZIP 并提供中英玩家更新日志。仅开发、暂不发版和单独推源码仍不创建 Release。统一脚本已有同版本成功状态时直接复用,不重复创建或上传。 - 读取一次 `git status --short --branch`、当前分支、远端和领先关系。显式暂存本任务的跟踪文件;保留并排除用户其他改动、发布 ZIP、构建产物、日志和创意工坊暂存内容。 - “干净提交”指提交内容边界干净,不表示删除未跟踪文件、清空工作区或回退用户改动。 - 没有新改动但本地提交领先远端时直接推送,不创建空提交。需要提交时,一个提交只表达当前这组文档、修复或发布准备。 - 推送当前分支;当前 manifest 版本的 release tag 已存在时,在同一次 `git push` 中显式推送该 tag。不要使用 `--tags` 把无关标签一并推送。 - `git push` 成功就是完成证据。不要随后 fetch、再次 status、打开 GitHub 页面或检查远端提交。 ## 7. 完整发布门禁 只有用户明确说“完整发布门禁”“完整验收”或“干净安装”时执行: 1. 运行 `tools/inspection/verify-refactor-boundaries.ps1`; 2. 对当前 DLL 运行 CoverageCatalog 全 verify; 3. 运行目标严格差分、相关类型族、增量等价和至少一场完整自动部署; 4. 跑一场稳定长线质量基准,断言跨回合复用与零非预期重算; 5. 使用正常可见 Steam 会话验证 UI、输入、动画、部署和真实卡顿; 6. 从 ZIP 干净安装到已确认的精确 Mod 目录并做最终冒烟。 完整门禁产生受控文件变化时,审查并提交,再从最终提交重新构建。安装只处理确认过的精确 Mod 目录和已知 manifest/DLL;目录含未知文件时停止,不递归清空。 ## 8. 阶段停止条件 每阶段成功一次就向前推进: - 行为 fixture 成功:不因随后只改文档、版本或发布元数据而复跑; - Release 构建成功:不反射版本或重复部署; - ZIP 创建成功:不重新打开或检查包; - 暂存复制成功:不逐文件比较; - 创意工坊上传成功:不打开页面或重新下载; - 夸克移动或上传成功:不搜索、下载或重复上传验证; - Git 推送成功:不 fetch/status 或查看网页复核。 最终汇报只列版本、release source commit、实际完成的阶段、三个发布渠道结果,以及确实未执行但用户要求的项目。监控后台版本提示不列作待办或失败;它由用户维护。只有完整门禁全部执行后才能写“完整门禁通过”。