AgentsView 的 Grok Build 解析器黄金测试夹具:如何钉住上游持久化格式并坚持测试预言机独立

发布时间:2026/9/17 7:38:38
AgentsView 的 Grok Build 解析器黄金测试夹具:如何钉住上游持久化格式并坚持测试预言机独立
AgentsView 的 Grok Build 解析器黄金测试夹具如何钉住上游持久化格式并坚持测试预言机独立【免费下载链接】agentsviewLocal-first session search, analytics, insights, and token use statistics for coding agents, supporting Claude Code, Codex, and more than 20 other agents.项目地址: https://gitcode.com/GitHub_Trending/ag/agentsview本篇以 internal/parser/testdata/grok-build/README.md 为核心讲解 AgentsView 如何为一套仍在演进的第三方 AgentGrok Build持久化格式构建“黄金测试夹具”golden fixtures用上游 Rust 类型反序列化/重序列化来保证夹具与真实二进制输出逐字节等价把上游钉死在指定 commit 上同时让 Go 侧断言保持手写、使“夹具生产者”不兼任“测试预言机”。读完你将掌握这类解析器兼容性测试的完整目录结构、夹具再生成流程以及它与 internal/parser/grok.go 解析实现之间的对应关系。1. 背景为什么 Grok Build 解析器需要黄金夹具AgentsView 会为多个编码 Agent 各自实现一个“Provider”发现 解析。Grok Build 是其中之一其 Provider 工厂注册在 internal/parser/grok_provider.go解析入口是 ParseGrokSummary。这类解析器面临的典型风险是上游 Agent 的落盘格式会随版本变化且 Grok Build 实际上同时存在两代格式由chat_format_version区分current 为 1、legacy 为 0甚至还有同文件内新旧行混排的情况。仅靠手写内联 JSON 字符串做测试很容易因为字段顺序、序列化细节与真实输出不一致而“测了个寂寞”。README 开头给出的定位正是These sanitized sessions pin the persisted formats emitted by xai-org/grok-build at commit7cfcb20d2b50b0d18801a6c0af2e401c0e060894.也就是说这套夹具的价值在于“钉住”某个上游 commit 处、由真实 Rust 序列化代码产出的持久化格式使 AgentsView 的 Go 解析器始终对着一个可信的格式基线做回归。2. 夹具目录布局与文件角色夹具位于internal/parser/testdata/grok-build/分 current当前格式与 legacy旧格式两棵目录树current/%2Fworkspace%2Fgrok-worktrees%2Fparser-audit/019f6000-0000-7000-8000-000000000001/summary.json、signals.json、chat_history.jsonllegacy/%2Fworkspace%2Fagentsview/019f6000-0000-7000-8000-000000000002/summary.json、signals.json、source-v1.jsonl、chat_history.jsonl布局细节有两点值得注意cwd 键做了 URL 编码。%2Fworkspace%2Fgrok-worktrees%2Fparser-audit解码后就是/workspace/grok-worktrees/parser-audit与summary.json内info.cwd一致。这与 Grok Build 按“工作目录 → 会话 ID → 文件”三级落盘的规则一致grok_provider.go 中的grokClassifyPath正是要求“相对路径恰好三段、第二段是合法 session ID”才把变更归类到某个会话。会话 ID 使用合法 UUIDv7 形态019f6000-0000-7000-8000-000000000001/...0002因为发现逻辑用IsValidSessionID过滤目录名非法 ID 的目录会被直接忽略。各文件的角色与内容summary.json会话元数据。current 版chat_format_version: 1与 legacy 版chat_format_version: 0额外带next_trace_turn字段都由 generate.rs 的write_summary生成字段包括info.{id,cwd}、session_summary、generated_titleAudit Grok compatibility、created_at/updated_at/last_active_at、num_messages12/num_chat_messages8、current_model_idgrok-4.5、parent_session_id、source_workspace_dir、git_root_dir、head_branchfeature/parser-audit、agent_namegrok-build。signals.json两代共用同一份计数与上下文信号——turnCount: 2、userMessageCount: 2、assistantMessageCount: 2、contextTokensUsed: 12000、contextWindowTokens: 200000、primaryModelId: grok-4.5见 generate.rs。注意这里没有tokenUsage.peakContextTokens这正对应该会话在解析结果中HasPeakContextTokens为 false 的断言。chat_history.jsonl对话行。current 版 9 行覆盖system、带synthetic_reason的注入型 userproject_instructions、interjection、prompt_index标注的真实 user、reasoning、backend_tool_callweb_search、带tool_calls与model_id的 assistant、tool_result见 current/chat_history.jsonl。source-v1.jsonl 与 legacy 的 chat_history.jsonllegacy 树中的source-v1.jsonl6 行见 legacy/source-v1.jsonl是 v1 源数据README 明确说明 legacy 的chat_history.jsonl是由 Grok Build 自带的chat-history-downgrade二进制降级产出的因此它才是 legacy 会话真实持久化的形态。3. 夹具如何生成用上游自己的 Rust 类型做“格式公证”README 给出的核心机制是把本仓库的 generate.rs 复制到上游仓库的一次性 checkout 中作为crates/codegen/xai-grok-shell/examples/agentsview_golden.rs示例运行。关键在于normalize_jsonfn normalize_jsonT: DeserializeOwned Serialize(raw: str) - Vecu8 { let value: T serde_json::from_str(raw).expect(fixture must match upstream type); let mut bytes serde_json::to_vec_pretty(value).expect(fixture must serialize); bytes.push(b\n); bytes }summary.json经由上游的session::persistence::Summary类型、signals.json经由session::signals::SessionSignals类型反序列化后再重序列化generate.rschat_history.jsonl的每一行则先反序列化为sampling::ConversationItem再用 serde_json 紧凑写出并补换行write_v1与write_legacy_sourcegenerate.rs。由于反序列化目标就是 Grok Build 自己的 Rust 类型任何字段名、嵌套结构写错都会直接 panicfixture must match upstream type从而保证夹具在结构上不可能偏离真实格式main函数generate.rs再按current/与legacy/两棵树分别落盘 summaryversion 1 / 0、signals 与历史行。legacy 的chat_history.jsonl不由 generate.rs 直接写出而是把source-v1.jsonl交给上游的chat-history-downgrade二进制降级生成——这一步模拟的是“旧版本 Grok Build 落下的真实文件”与 current 树中由write_v1直出的 v1 行形成对照。4. 再生成夹具的完整命令README 给出的再生成流程如下在 AgentsView 仓库根目录执行需要自备一个钉死在上游 commit7cfcb20d2b50b0d18801a6c0af2e401c0e060894的可丢弃 checkoutGROK_BUILD_CHECKOUT/tmp/grok-build-upstream-20260719 cp internal/parser/testdata/grok-build/generate.rs \ $GROK_BUILD_CHECKOUT/crates/codegen/xai-grok-shell/examples/agentsview_golden.rs cargo run --manifest-path $GROK_BUILD_CHECKOUT/Cargo.toml \ -p xai-grok-shell --example agentsview_golden -- \ internal/parser/testdata/grok-build cargo run --manifest-path $GROK_BUILD_CHECKOUT/Cargo.toml \ -p xai-grok-shell --bin chat-history-downgrade -- \ internal/parser/testdata/grok-build/legacy/%2Fworkspace%2Fagentsview/019f6000-0000-7000-8000-000000000002/source-v1.jsonl \ internal/parser/testdata/grok-build/legacy/%2Fworkspace%2Fagentsview/019f6000-0000-7000-8000-000000000002/chat_history.jsonl流程拆解GROK_BUILD_CHECKOUT指向上游仓库的一次性 checkoutcp把本仓库的生成器放进上游xai-grok-shellcrate 的 examples 目录使其能直接use xai_grok_shell::{...}见 generate.rs 的导入与常量CURRENT_ID/LEGACY_ID。第一条cargo run运行示例程序参数internal/parser/testdata/grok-build作为输出根目录重新生成 summary、signals 与两棵树的 jsonl 源。第二条cargo run运行上游的chat-history-downgrade从 legacy 的source-v1.jsonl产出 legacy 的chat_history.jsonl。这里“钉住 commit”是刻意的工程选择夹具代表的是“某一刻的上游格式”上游升级后应当主动重新 checkout 新 commit 再生成并审查 diff——而不是让夹具悄悄漂移。5. 夹具生产者不等于测试预言机README 的最后一句话是整套设计中最容易被忽视的原则The fixtures use invented workspace paths, session IDs, prompts, and model metadata. Go-side expectations remain hand-authored so the fixture producer is not also the test oracle.两点含义数据脱敏夹具中的工作区路径/workspace/agentsview、/workspace/grok-worktrees/parser-audit、会话 ID、提示词、模型元数据grok-4.5全部是虚构值仓库里不含任何真实会话数据。预言机独立generate.rs只负责“产出输入”而 internal/parser/grok_test.go 里的期望值全部手写。若用同一份生成代码同时推导输入和期望生成器自身的理解偏差会被两边相互印证、永远暴露不出来。黄金测试的入口是parseGrokGoldengrok_test.go把testdata/grok-build/current或legacy整树拷进临时目录通过NewProvider(AgentGrok, ...)建 ProviderDiscover出唯一会话源再Parse。围绕两套夹具手写断言覆盖了currentgrok_test.go会话名优先取generated_title→ Audit Grok compatibility转录语义FirstMessage为 Review parser compatibilityUserMessageCount为 2注入型project_instructions被剔除、interjection保留共 6 条消息其中backend_tool_call行产出ws_1/web_search工具调用reasoning行折叠为下一条 assistant 的ThinkingTextInspect both formats元数据Cwd为 worktree 路径、Project为 agentsview、parent_session_id映射为前缀化的grok:019f5000-...并得到RelFork关系对应 grok.go 中parentSessionID grok: ...的分支、无峰值上下文 token。legacygrok_test.goTranscriptFidelity为 Full4 条消息assistant 消息带工具调用call_1与模型 grok-4.5思考文本 Check the old format 挂在同一条消息上工具结果按tool_call_id配对。这些断言恰好逐一“对账”了解析器 parseGrokChatHistory 的分支system行跳过、synthetic_reason非interjection的 user 行跳过、reasoning行暂存为 pending think、backend_tool_call转 assistant 工具消息、tool_result转 RoleUser 空内容载体行。6. 夹具格式与解析实现的逐项对应把夹具字段与 ParseGrokSummary 的读取顺序对照可以看到“格式 → 实现”的映射非常清晰会话发现grokDiscoverEachgrok_provider.go只把root/cwdKey/sessionID/summary.json的存在当作会话成立的锚点grokStrictMatch校验三级相对路径夹具的目录层级正是按此设计伴生文件指纹计算把signals.json、chat_history.jsonl、updates.jsonl、prompt_context.json视为伴生文件grokCompanionFileswatch 根也监听同样这五个文件名grokWatchRoots——任何一处变更都会触发重新解析首条用户消息优先取转录中第一个非空 user 消息其次回退firstPrompt字段再回退session_summarygrok.go——current 夹具的转录里真实 user 行先于注入行因此得到 Review parser compatibility保真度降级chat_history.jsonl缺失或为空时TranscriptFidelity降为 summary、SourceVersion变为grok-summary-v1计数以 signals/summary 为准grok.go。夹具两棵树都带完整历史故断言的是grok-chat-v1与 Full 保真度新旧行混排grokChatRowKind在type字段缺失时回退读role字段grok.go未知type的行不记为 malformed而是按 legacy role 处理——grok_test.go 专门用一条future_metadata行验证了前向兼容行为。7. 小结这套黄金夹具值得借鉴的三个点格式公证夹具不是“看起来像”的 JSON而是经上游真实 Rust 类型往返Summary、SessionSignals、ConversationItem后由 serde 重序列化的产物结构正确性由编译器级别的类型检查兜底版本钉住 主动升级README 把上游 commit7cfcb20d2b50b0d18801a6c0af2e401c0e060894写死在文档里升级是一个显式的、需要人审查 diff 的动作生产者与预言机分离generate.rs 只产输入Go 侧断言全部手写两代格式current/legacy共用同一套 Provider 与断言基础设施让“格式漂移”在回归测试中以精确的字段级失败暴露出来而不是以模糊的端到端异常暴露。对于任何需要长期跟踪第三方落盘格式的解析器本文即 AgentsView 对 Grok Build 的做法这三点构成的模板——类型化生成、commit 钉住、断言独立——可以直接照搬。【免费下载链接】agentsviewLocal-first session search, analytics, insights, and token use statistics for coding agents, supporting Claude Code, Codex, and more than 20 other agents.项目地址: https://gitcode.com/GitHub_Trending/ag/agentsview创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考