模板 manifest 滞后:resource_key 已提交 main,CLI 渲染仍为旧模板 #1
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
现象(2026-09-23/24)
82e0dcc等),但 CLI 拉取到的 manifest 渲染出的模板仍为旧版(0 个 resource_key)execution target lease does not match registered target;需手工在本地补 resource_key 才能运行期望
yuvu resources refresh或校验模板 hash)影响
摘要
该问题根因不在 CLI 缓存逻辑,而在 yuvuResources 发布链路:manifest.json 是手工生成、手工提交的产物,模板提交(
82e0dcc,2026-09-24 00:07)未同步重生成 manifest;CLI 的refresh只拉取已提交的单一 manifest 文件(含 SHA-256 与 revision),manifest 滞后则模板必然滞后,与缓存无关。事后2188ec1(08:55 "Regenerate manifest after MAA-End template update")单独补提交才修复,佐证了"提交即滞后"的手工流程缺陷。CI 虽有--check门禁,但该 push 未被拦截(见下)。【代码级根因】
cmd/build-manifest/main.go:32-61仅在手工运行make manifest(Makefile:33-34)时生成;Makefile:39-40的check含go run ./cmd/build-manifest --check(main.go:48-56比对不通过即 fatal "manifest.json is stale; run make manifest")。CI 为.woodpecker.yml:1-8仅跑make check——是"检查"而非"重建",没有针对 templates/catalog 变更自动重生成 manifest 的步骤。--check门禁被绕过/未生效:82e0dcc提交时.woodpecker.yml已含make check(git show 82e0dcc:.woodpecker.yml确认),738aab4即引入--check,且82e0dcc未改动 manifest —— 按设计该 push 应 CI 失败。实际却合入 main,说明门禁(触发条件为push事件,.woodpecker.yml:1-2)当时未拦截或未被运行;这是流程缺口而非代码缺口,需人工核实该次 CI 记录。yuvu resources refresh走client.Refresh(yuvuCli 引用yuvuResources/client:client/client.go:199-219)→remoteManifest(client.go:414-430)仅 GETDefaultManifestURL(client.go:19,raw/branch/main/manifest.json)并按 manifest 内 SHA-256 校验下载模板(client.go:370-372、cache.go:24-30)。manifest 内容是什么,模板就是什么;refresh 不能修复滞后。缓存 fallback(client.go:214-218)只在远端拉取失败时用 last-known-good 并打印 warning,非本次原因。build-manifest/main.go:96-116的 revision 是内容 SHA-256 拼接哈希,非 git commit;template render(internal/cli/template_commands.go:154-172)输出文档不含来源 revision,用户无从核对线上版本——与 issue"期望 3"一致。【触发链路】
82e0dcc改templates/maaend/*.yaml加resource_key(git show 82e0dcc:仅 2 个模板文件,无 manifest)→ main 上 manifest 的maaend/daily-fullSHA-256 仍是旧值(对比2188ec1diff 中0baad4b5…→f75e1222…)→ CLIrefresh拉到旧 manifest,按旧 SHA 取到旧模板(无 resource_key)→ 直接应用时 Core 报execution target lease does not match registered target,用户需手工补字段。2188ec1(08:55)补生成 manifest 后 SHA 与templates/一致(已用shasum -a 256核对 HEAD 上f75e1222…匹配),问题解除;说明整条链路无任何自动机制。【影响面】
maaend/*模板的 CLI 用户在 00:07–08:55 窗口(及任何未来类似提交)内直接template run/render必得旧模板,apply 必挂。make manifest"(README Publishing 章节虽有说明,但无强制)。【修复建议】
.woodpecker.yml保持make check(已含--check,Makefile:39-40),但补充独立步骤确保--check的失败即中断合并(当前push触发若失败应已阻塞,需排查为何82e0dcc通过);并把pre-push: diff-check check(Makefile:44)纳入工作流文档作为强制本地门槛。manifest-auto)在templates/、catalog/、runtime-profiles/有 diff 时执行go run ./cmd/build-manifest(Makefile:33-34),并在.woodpecker.ymlpush 步骤中先行自动重建、提交或失败——最稳妥是 CI 直接跑make manifest && git diff --exit-code manifest.json,diff 非空即 fail,把"重建并检查差异"变成门禁本体。make manifest && git commit manifest.json为唯一发布入口,并在 commit message 模板/PR 检查清单中要求 manifest 与内容同 commit(对齐2188ec1的补救做法)。template render/show在 stderr 输出manifest.Revision与Source(数据已在LoadResult/resourceStatusOutput,见template_commands.go:17-31、client.go:41-46),或支持--output json附带 revision;yuvu resources status已能打印revision=(template_commands.go:93),补一句 README 指引即可。若需 commit 溯源,可在build-manifest/main.go:114-120的 manifest 增加source_commit(由 CI 注入git rev-parse HEAD,本地运行时留空并 warning)。【验证建议】
cd yuvuResources && make check—— 若build-manifest --check报 stale 即复现滞后;再grep -A2 '"maaend/daily-full"' manifest.json与shasum -a 256 templates/maaend/daily-full.yaml比对 SHA。--check拦截);再make manifest提交后应通过。yuvu resources status打印revision=…,与仓库manifest.json的revision字段比对;yuvu template show maaend/daily-full确认含resource_key后再run。【分析基线】
e2e2ace(2026-09-24 22:48),yuvuClic4edf5e。82e0dcc为 HEAD 祖先且未动 manifest;2188ec1补生成;HEAD 上--check通过(实际运行go run ./cmd/build-manifest --check,exit=0);82e0dcc时.woodpecker.yml/Makefile已含门禁(git show 82e0dcc:验证)。source_commit注入方案取决于 CI 环境变量可用性。修复计划定位
全局序列(13 项)
建议实施步骤(顺序)
.woodpecker.yml增加独立步骤go run ./cmd/build-manifest && git diff --exit-code -- manifest.json(fail 即阻断),替代仅依赖make check内嵌--check;保留make check作为兜底。82e0dcc未被拦截的原因:查 Woodpecker 该次 push 的 pipeline 记录(是否未触发/被跳过/被忽略),恢复门禁有效性——这是本 issue 的核心未解点。templates/、catalog/、runtime-profiles/必须同 commit 携带manifest.json",并给出make manifest && git commit manifest.json作为唯一发布入口。template render/show向 stderr 输出revision/source(数据已具备);resources status已打印revision=,补 README 指引。source_commit溯源:cmd/build-manifest的 manifest 增加source_commit(CI 注入git rev-parse HEAD,本地留空+warning)。依赖与编排
docs/QUALITY_GATE_CHECKLIST.md:行为变更(CI 配置)单独提交、文档单独提交。验收标准
make manifest的 push,CI 必须失败。make check通过)。关联
状态:修复已合并 ✅(仅记录,不触发关闭)
2026-09-26 00:48→ main9c6cd28fffb41eCI 新增manifest-guard(make manifest+git diff --exit-code -- manifest.json,漂移即失败)5e3b23cREADME 发布规程;975f47a命令顺序修正(先make manifest再make check)915f96f+9c6cd28门禁意图注释,并将 manifest-guard 前置到 check 之前(fail-fast,可读 diff 可见)make check全绿;build-manifest --check通过;负向实测通过(改模板不重生成 manifest → 门禁非零退出;staged 陈旧 manifest → 非零退出)仍待处理(延后项):
82e0dcc那次 CI 记录排查(需 Woodpecker 平台权限)(本评论为状态记录;关闭由 Hermes 处理。)
R4 复验报告(独立复验,不采信 agent 自述)——本轮 FAILED,issue 保持 open
轮次
01a0dec7-fd6a-7b3a-be6f-65020ba769ad,实例 opencode-omo(01a05373-…),并发组rg-rs-12026-09-27 01:34:03 CST→ finished02:04:06 CST,status=failed,error_code=execution_timeout,error_message=task execution timed out(execution_timeout_seconds=0→ 平台兜底 ≈30 分钟,实测 1803 s 被收割)DIAGNOSIS-R4.md(12036 B,02:02 落盘)。无ROUND-NOTES-R4.md→ 任务书要求的「ROUND-NOTES 首行 =skill返回首行原文」证据缺失,本轮不能作为交付98b605b;Forgejorefs/heads/main=98b605b7ee4758f21c32934ff4c59ea820d5faf9(我自己git ls-remote核对)M README.md(+21 行,Provenance 段)+ 未跟踪DIAGNOSIS-R4.md/TASKD-R4.md。按协作约定提交由 fixer 侧完成,本轮不代为推送我独立复验的原始输出(独立副本
/home/zx/yuvu/workspaces/hermes/r4verify,HEAD=98b605b,go1.26.5,GOFLAGS=-mod=vendor GOTOOLCHAIN=local)→ 期望 1、3 在 HEAD 上确实成立,且可独立复现(生成器确定性)。
82e0dcc「为何未被拦」的历史复跑(我按各提交的原始树重跑,非采信诊断)→ 与
DIAGNOSIS-R4.md§2b 结论一致:仓库侧当时就具备检出能力,漏拦在平台侧。这条我独立复跑得到相同结果,认可。file:line抽验(我自己读源码核对)client/client.go:457=decoder.DisallowUnknownFields()✓(「新增 manifest 字段会击穿存量 CLI」的论据成立)README.md:23 ## Publishing、README.md:32-37门禁与发布规程说明 ✓cmd/build-manifest/main.go:33=-check开关、48-56= stale 判定 + fatal 文案 ✓.woodpecker.yml步骤行号 4-10 / 12-15 / 17-20 ✓.woodpecker.yml新增步骤原文逻辑正确:先重建再比差异,并置于
check之前(fail-fast、差异可读)。我新发现的一条(比诊断更进一步,属假绿风险,必须记录)
manifest-guard(以及整个.woodpecker.yml)目前永远不会被执行:xm/yuvuResources在 Woodpecker 上根本没有注册。→ 「
82e0dcc为何没被拦」在现有平台上的答案是:该仓库没有任何 pipeline 在跑(不是被 skip、不是 paths 过滤、不是红而不卡)。这也意味着期望 1 的「提交即校验」在最后一环是空的:门禁存在,但无人执行。限制:我只查了
woodpecker.nas.zuixue.site(技能记载的唯一 CI,admin=zx)。VM 上woodpecker.zuixue.site连不通(curl 000)。若另有 CI 实例,请以该实例记录为准。门禁残余风险(供复验方判断)
git diff --exit-code -- manifest.json单独使用是盲的(实测 EXIT=0)。门禁正确性 100% 依赖「先make manifest」。当前文件顺序正确,但后来者一旦把两条命令拆开/调序就会静默假绿——建议在步骤里加一条断言(重建后manifest.json与 HEAD 不一致即视为「漂移已发生」而直接失败)或注释写死「两条命令不可拆分」。make check作为兜底只在独立 checkout 下有效:同一 job 内若manifest-guard先跑过,manifest.json已被重建,随后的make check必然绿(实测 guard 先跑后make checkEXIT=0)。CI 中两者是不同 step、共享同一 workspace,兜底的实际拦截力比看起来弱。请复验方验证的清单
82e0dcc当时的 Woodpecker / AGit 事件记录是否存在。若确认无 pipeline → 本 issue 期望 2 的根因即「仓库未接入 CI」。xm/yuvuResources接入 CI(注册到 Woodpecker + 配置 forgejo.nas 镜像),或改用 pre-push / 服务端 hook 强制make manifest;两者都不做时,期望 1 仍不成立。98b605b的复现(make manifest→git diff --exit-code -- manifest.json= 0;make check= 0)。README.mdProvenance 段是否由 fixer 侧补提(本轮因 30 分钟超时未提交)。revision+ 来源 commit」仍是跨仓后续项。诊断 §4 的取舍(manifest 不嵌source_commit)我认可:与client/client.go:457的严格解码、以及「重建+diff」确定性门禁都相容。状态:issue 保持 open,label 未改(仍
needs-fix)。本轮判定未通过——轮次被超时收割、无 ROUND-NOTES、无 commit;期望 1/3 虽有历史提交在 HEAD 上成立,但执行链(CI 接入)缺失,属「有门禁、无执行」的假绿风险。(本评论中所有凭据/令牌均已 [REDACTED]。)
第 31 次独立复验(cron 巡检轮 · 2026-09-28 15:5x CST)—— ✅ 通过并已合并(issue 保持 open)
车道:
fix/res-1-manifest-lagtip945b70c(worktreewr-1,基于 maina3a9f5d)。复验方式:另一会话(非修复方)在宿主 VM 138 上亲自跑门禁 + 亲自做反向验证,不采信轮次自述。
1. 门禁(分支上实跑,全部 exit 0)
2. 反向验证(3 处,全部「红得指向缺陷」→ 还原后 sha256 与 HEAD 逐位一致 → 复绿)
templates/smoke/ffmpeg.yaml不改 manifest)+ 保留修复make validaterc=2:manifest.json is stale; run make manifestcmd/check-resources/main.go里的CheckManifest(".")调用(漂移仍在)make validaterc=0 → 说明缺陷确实未被捕获,新增检查是唯一拦截点CheckManifest的字节比较改成&& false(恒不触发)TestCheckManifestDetectsDriftFAIL:expected drift to be reported as stale, got <nil>还原后:
templates/smoke/ffmpeg.yaml/cmd/check-resources/main.go/internal/resources/manifest.go三者sha256 == sha256(git show HEAD:<file>),git status --porcelain仅剩未跟踪的ROUND-NOTES-R1.md/TASKD-R1.md,make check复绿 rc=0。3. 合并(串行、--no-ff、合并后门禁复跑全绿才 push)
a3a9f5d→a4369ca2b1178d252941c40ee33310ab274bf3d6(git merge --no-ff fix/res-1-manifest-lag,无冲突)go build ./...rc=0、go test -count=1 -p 2 ./...rc=0、make checkrc=0 → 才git pushgit ls-remote origin refs/heads/main=a4369ca2…,与本地 HEAD 逐位一致;manifest.json内容未变(revision 语义保持)wr-1已git worktree remove --force清理,lanes.json该车道置merged4. 请验证这几点(PM/用户侧)
make validate现在会在模板/目录与manifest.json漂移时直接失败 —— 请确认这符合你期望的「提交即拦」语义(此前make validate只做结构契约校验,漂移只在make check尾部的build-manifest --check里被发现)。make check里validate与build-manifest --check的重复执行(两次都做新鲜度比较)可以接受;若要精简,需同步改.woodpecker.yml:5的注释后才可移除其一。client消费方(yuvuCli)显示 manifestrevision与来源 commit 属跨仓,本仓只在 README 记录了revision作为权威溯源键。5. 未完成 / 后续精修(明确不声称已解决)
xm/yuvuResources未注册到 Woodpecker(/api/repos无此仓)⇒.woodpecker.yml的manifest-guard从不执行。「82e0dcc未被拦」= 根本没有 pipeline,而非被 skip。这是本 issue 原始症状的根因之一,需平台侧(Woodpecker 注册 + 分支保护)才能闭环,仓库内不可自证。internal/resources/manifest.go沿旧生成器的扩展名处理,过滤用strings.ToLower(ext)、TrimSuffix也用同一小写ext,对.YAML/.JSON大写扩展名会把扩展名留进 name;当前仓无此类文件。证据位置:分支
fix/res-1-manifest-lag(tip945b70c)、合并点a4369ca、ROUND-NOTES-R1.md(随分支入库)。✅ 真机验证通过(测试方 · 2026-09-28 19:55 CST)— 关闭
验证方式说明:我本地 clone 落后于 main(不含修复),差点据此误判门禁失效;改走 Forgejo main 归档
/repos/xm/yuvuResources/archive/main.tar.gz解出干净树(含 vendor)后跑门禁。问题 1「
make validate是否『提交即拦』」—— 通过(正/负向都做了)make validate→resource catalog, templates, and manifest are valid(新消息已含 manifest)exit 0templates/maa/daily.yaml追加一行注释):manifestec73d339…vs 文件93b9ce4f…→
make validate→manifest.json is stale; run make manifest+ exit 1 ✓make check尾部的build-manifest --check同样报 stale(两条路径都拦得住)问题 2「
make check里重复执行可接受吗」—— 可接受validate与build-manifest --check都是确定性、无副作用、亚秒级本地比较;多花一次go run换取「单独调用 validate 也能拦」,值得保留(.woodpecker.yml:5注释无需改)。问题 3「CLI 侧可见性」—— 本轮未做,作为后续项
仓库侧门禁已闭环;客户端(yuvuCli)显示 manifest revision 仍缺。若需要"客户端能看到用的哪版模板",可另开一条(按约定我不擅自新增 issue)。
关闭。