NemoClaw 维护者状态文件(state.json)Schema 全解析:维护循环的持久化状态设计与实践
【免费下载链接】NemoClawRun agents like Hermes, LangChain Deep Agents, and OpenClaw more securely inside NVIDIA OpenShell with managed inference项目地址: https://gitcode.com/gh_mirrors/ne/NemoClaw
导读
本文围绕 NemoClaw 仓库中维护者自动化 Skill 的核心持久化机制——.nemoclaw-maintainer/state.json状态文件——展开,完整解读其 JSON Schema 的每个字段、取值约束与写入规范,并结合 state.ts、triage.ts、hotspots.ts 等源码,说明该文件如何在 triage、merge-gate、hotspot 检测与每日维护循环之间传递状态、排除项与历史记录。读完本文,你将掌握这份状态文件的完整结构、每个字段的语义与边界条件,以及如何在日常维护流程中正确读写它。
状态文件的定位:维护循环的“单一事实来源”
NemoClaw 的维护者 Skill(如nemoclaw-maintainer-day)以一个可重复执行的维护循环为核心:每天对面向发布的 PR 与 Issue 执行一次“检查版本进度 → 选择一个动作 → 执行 → 汇报进度”的完整流程(见 SKILL.md)。为了让循环在多次运行之间保持连续性——知道哪些 PR 被永久排除、上一次 triage 队列长什么样、当前正在处理什么工作——维护者工具链需要一个跨进程、跨运行持久化的状态文件。
STATE-SCHEMA.md(即 STATE-SCHEMA.md)正是这份状态文件的事实性规格说明。其核心约定有三条:
- 存储位置:状态存放在
.nemoclaw-maintainer/state.json(相对仓库根目录)。 - Git 排除:通过
.git/info/exclude将.nemoclaw-maintainer/目录排除出版本控制,避免本地维护状态污染提交历史。这一点在 state.ts 的ensureExclude()中得到落实——脚本会在init时检查.git/info/exclude是否已包含.nemoclaw-maintainer/条目,没有则追加。 - 自描述版本:文件带有
version字段,便于未来 Schema 演进时进行迁移判断。
完整 Schema 示例
STATE-SCHEMA.md给出了状态文件的完整骨架,这是所有字段的权威参考。完整 JSON 如下(含默认值):
{ "version": 1, "repo": "NVIDIA/NemoClaw", "updatedAt": null, "priorities": [ "reduce_pr_backlog", "reduce_security_risk", "increase_test_coverage", "cool_hot_files" ], "gates": { "greenCi": true, "noConflicts": true, "noMajorCodeRabbit": true, "testsForTouchedRiskyCode": true, "autoApprove": true, "autoPushSmallFixes": true, "autoMerge": false }, "excluded": { "prs": {}, "issues": {} }, "queue": { "generatedAt": null, "topAction": null, "items": [], "nearMisses": [] }, "hotspots": { "generatedAt": null, "files": [] }, "activeWork": { "kind": null, "target": null, "branch": null, "goal": null, "startedAt": null }, "history": [] }这份示例与 state.ts 中defaultState()函数生成的默认状态完全一致,说明文档与实现保持了同步,你可以把它当作“初始化后的初始状态”。
顶层字段逐一解读
version与repo
version:Schema 版本号,当前为1。它标识状态文件的格式版本,供读取方做兼容性判断。repo:目标仓库标识,默认NVIDIA/NemoClaw。triage 等脚本会用它拼装gh查询参数(如gh api repos/${repo}/pulls),详见 triage.ts。
updatedAt
最近一次状态写入的 ISO 时间戳。每次saveState()都会把它刷新为new Date().toISOString()(见 state.ts),用于判断状态的新鲜度。初始值为null,表示尚未写入过。
priorities
维护工作的优先级数组,默认包含四个枚举值,体现了维护者循环的关注重心:
| 优先级键 | 含义 |
|---|---|
reduce_pr_backlog | 减少 PR 积压 |
reduce_security_risk | 降低安全风险 |
increase_test_coverage | 提升测试覆盖 |
cool_hot_files | 为热点文件“降温”(降低冲突与改动集中的文件热度) |
从源码结构看,这一数组主要作为维护者决策的参考优先级列表;实际的动作选择则由 SKILL.md 中“Step 2: Pick One Action”的有序决策链(approve → salvage → security → test gap → conflicts → sequencing)驱动。
gates:合并闸门开关
gates对象用一组布尔值描述维护循环的自主行为边界,这是整个状态文件中约束性最强的部分:
| 字段 | 默认值 | 语义 |
|---|---|---|
greenCi | true | 要求 CI 全绿 |
noConflicts | true | 要求无合并冲突 |
noMajorCodeRabbit | true | 要求无未解决的 major/critical CodeRabbit 问题 |
testsForTouchedRiskyCode | true | 要求触及的“风险代码”有测试覆盖 |
autoApprove | true | 允许自动批准(当所有闸门通过时) |
autoPushSmallFixes | true | 允许在贡献者分支上推送小修复 |
autoMerge | false | 禁止自动合并 |
其中两条需要特别注意:
gates.autoMerge必须保持为false。维护循环可以批准(approve)一个 PR,但绝不能合并(merge)它。这与 SKILL.md 的“Never merge”约束以及 MERGE-GATE.md 中“Report that the PR can proceed to a separate merge decision. Never merge in this workflow”的说明完全一致——合并决定必须交给用户。gates.autoPushSmallFixes是“小修复推送到贡献者分支”的授权开关。SKILL.md 还补充了其前置条件:必须在 CI 与定时自动化评审针对“同一个未变的最新 PR commit”稳定结束之后,才允许推送。
excluded:排除清单
excluded对象包含prs与issues两个映射,用于记录被 triage 永久跳过(直到用户手动移除)的条目:
- 键必须是数字字符串(number string),如
"1234",而不是数字。 - 值必须是
{ "reason": "...", "excludedAt": "ISO" }形式,excludedAt为 ISO 时间戳。
triage 在构建队列时会读取该清单并过滤:const excludedPrs = new Set(Object.keys(state?.excluded?.prs ?? {}).map(Number)),随后classified.filter((item) => !excludedPrs.has(item.number))(见 triage.ts)。注意excluded.issues字段在 state.ts 的类型定义中存在,但exclude子命令目前只写入prs(源码注释明确说明“triage only processes PRs”)。
queue:最近一次 triage 输出
queue缓存最近一次 triage 的结果,用于跨运行对比,避免每次循环都重复完整分析:
| 字段 | 语义 |
|---|---|
generatedAt | triage 输出的生成时间(ISO) |
topAction | 队列中排名第一的动作项 |
items | 主队列条目(merge-ready / review-ready) |
nearMisses | “差一点就绪”的条目(有明确的小修复路径) |
该对象由state.ts set-queue子命令从 stdin 读取 triage 的 JSON 输出并写入(见 state.ts):items取triageOutput.queue,nearMisses取triageOutput.nearMisses,topAction取queue[0],generatedAt优先使用 triage 输出自带的时间戳。triage 输出的完整字段(含rank、bucket、score、nextAction、riskyFiles等)可参见 triage.ts 的QueueItem接口。
hotspots:热点文件缓存
hotspots缓存最近一次热点检测的结果,用于识别最易引发合并冲突的文件:
| 字段 | 语义 |
|---|---|
generatedAt | 检测结果生成时间(ISO) |
files | 热点文件条目列表 |
该对象由state.ts set-hotspots子命令写入,同样从 stdin 读取热点检测脚本的 JSON 输出(见 state.ts)。热点检测算法见 hotspots.ts:它综合30 天origin/main上的 git churn与开放 PR 的文件重叠数,按mainTouchCount + openPrCount * 3 + (risky ? (mainTouchCount + openPrCount) * 2 : 0)打分排序,其中 PR 重叠权重 3 倍(作为冲突代理指标)、风险文件额外 2 倍加成,最终输出前 25 个热点文件。检测到的热点会路由到 HOTSPOTS.md 对应的工作流处理。
activeWork:当前进行中的工作
activeWork记录维护者“正在处理但尚未完成”的单项工作,供中断恢复与交接使用:
| 字段 | 语义 |
|---|---|
kind | 工作类型(如 approve、salvage、test 等) |
target | 目标条目(如 PR 编号) |
branch | 涉及的分支 |
goal | 目标描述 |
startedAt | 开始时间(ISO) |
初始状态为全null。从源码结构看,该字段主要服务于跨循环的上下文恢复——SKILL.md 中“Readstate.jsonto avoid repeated context”的说明即与此对应。
history:操作历史
history是一个操作审计数组,记录了每次循环完成的关键动作。每个条目的格式为:
{ "at": "ISO", "item": "PR#1234", "action": "approved|salvaged|blocked|sequenced", "note": "one line" }约束条件:
at为 ISO 时间戳;item标识目标条目,约定为PR#1234形式;action是四个枚举之一:approved(批准)、salvaged(修复抢救)、blocked(报告阻塞)、sequenced(排队序列化);note为一行简要说明;- 最多保留 50 条,超出时删除最旧的条目。该上限在 state.ts 的
cmdHistory()中有硬性实现:if (state.history.length > 50) state.history = state.history.slice(-50)。
历史记录的典型写入方式来自 SKILL.md 的 Step 4:
node --no-warnings .agents/skills/nemoclaw-maintainer-day/scripts/state.ts history <action> <item> "<note>"例如:
node --no-warnings .agents/skills/nemoclaw-maintainer-day/scripts/state.ts history approved PR#1234 "all gates passed, approved"状态文件读写工具:state.ts 子命令速查
仓库提供了配套的 TypeScript 状态管理脚本 state.ts,所有子命令均以node --no-warnings .agents/skills/nemoclaw-maintainer-day/scripts/state.ts <subcommand> [args]形式调用。支持七个子命令:
| 子命令 | 参数 | 作用 |
|---|---|---|
init | 无 | 创建.nemoclaw-maintainer/state.json(若不存在)并确保.git/info/exclude包含.nemoclaw-maintainer/ |
show | 无 | 打印当前完整状态(JSON) |
exclude | <number> <reason> | 将 PR 加入永久排除清单,记录原因与排除时间 |
unexclude | <number> | 从排除清单移除条目(同时清理prs与issues) |
history | <action> <item> <note> | 追加一条历史记录(自动控制 50 条上限) |
set-queue | stdin 传 JSON | 从 triage 输出更新queue(items/nearMisses/topAction/generatedAt) |
set-hotspots | stdin 传 JSON | 从热点检测输出更新hotspots(files/generatedAt) |
实现细节值得注意:
set-queue与set-hotspots通过 stdin 读取 JSON(readFileSync(0, "utf-8")),解析失败会向 stderr 报错并以非零码退出(见 state.ts),因此用法是管道形式:node --no-warnings .agents/skills/nemoclaw-maintainer-day/scripts/triage.ts | \ node --no-warnings .agents/skills/nemoclaw-maintainer-day/scripts/state.ts set-queue- 每次写入都会自动刷新
updatedAt为当前 ISO 时间。 exclude的reason参数支持空格(args.slice(1).join(" ")),因此“原因”中可以包含多词描述。
状态文件在维护循环中的完整流转
结合 SKILL.md 的流程,状态文件贯穿维护循环的各个阶段:
- Step 1 检查版本进度:运行
version-target.ts与version-progress.ts,前者读取本地最新 semver tag 并 bump patch 得到目标版本,同时扫描带旧版本标签的开放 PR/Issue 作为“stragglers”(见 version-target.ts)。 - Step 2 选择一个动作:triage 依据
excluded过滤已排除条目,生成queue与nearMisses,随后通过set-queue写入状态文件。 - Step 3 执行:根据动作类型走 MERGE-GATE.md(批准)、SALVAGE-PR.md(抢救)等分工作流。批准前必须运行受信任的闸门检查器(
run-trusted-check-gates.sh)——该检查器本身也被shared.ts的RISKY_PATTERNS列为风险文件,改动它需要测试覆盖(见 shared.ts)。 - Step 4 汇报进度:重新运行
version-progress.ts展示更新,并用state.ts history追加本次动作记录。若目标版本全部完成,可建议进入nemoclaw-maintainer-eveningSkill。
状态文件还服务于/loop集成场景:/loop 10m /nemoclaw-maintainer-day会周期性触发维护循环,此时“Readstate.jsonto avoid repeated context”成为避免重复上下文的关键手段——queue与hotspots的跨运行缓存正是为此设计。
实践要点与边界条件
- 永远不要打开
autoMerge:循环的最高权限是 approve,合并是独立决策,必须由用户执行。 - 排除项是持久化的“硬过滤”:被排除的 PR/Issue 会被 triage 无条件跳过,只有用户主动
unexclude才会重新进入队列。因此exclude时应提供清晰的原因。 - history 是审计线索:每个动作都应留痕,且保持 50 条以内的滚动窗口;旧条目优先删除。
queue与hotspots是“最近一次”快照:它们只反映最近一次 triage/hotspot 运行,跨运行对比时要注意generatedAt的时间差异,避免用旧快照做新决策。- 风险代码的测试闸门:
testsForTouchedRiskyCode与shared.ts中的风险模式(安装脚本、onboard 逻辑、blueprint、policy/credential/inference 相关路径等)联动——触及风险文件的 PR 必须先有测试,这是批准的前置条件之一。
结语
.nemoclaw-maintainer/state.json虽是一个小文件,却是 NemoClaw 维护者自动化体系的地基:它以gates划定自主边界,以excluded表达人为干预,以queue/hotspots缓存分析结果,以activeWork支持中断恢复,以history留下审计轨迹。理解这份 Schema,就等于理解了维护循环“如何记住自己做过什么、正在做什么、以及被允许做什么”。在实现层面,STATE-SCHEMA.md 与 state.ts 一一对应,是阅读维护者工具链代码时最值得首先掌握的入口。
【免费下载链接】NemoClawRun agents like Hermes, LangChain Deep Agents, and OpenClaw more securely inside NVIDIA OpenShell with managed inference项目地址: https://gitcode.com/gh_mirrors/ne/NemoClaw
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考