ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Nexent Bugfix Contract 实战指南:缺陷回归记录、覆盖缺口分类与测试资产联动

Nexent Bugfix Contract 实战指南:缺陷回归记录、覆盖缺口分类与测试资产联动
  • 人工智能
  • AI Agent
  • Agent 编排
  • 低代码
  • AI 技能
  • RAG

【免费下载链接】nexent

Nexent 是一个开源智能体SDK和平台,能够将描述流程的自然语言转化为完整的多模态服务 —— 无需编排,无需复杂拖拉拽。基于 MCP 工具生态系统构建,Nexent 提供灵活的模型集成、可扩展的数据处理和强大的知识库管理。我们的目标很简单:将数据、模型和工具整合到一个智能中心中,让任何人都能轻松地将 Nexent 集成到项目中,使日常工作流程更智能、更互联。

项目地址:https://gitcode.com/ModelEngine/nexent
点击查看免费下载

导读

本文围绕 Nexent 仓库中 nexent-test-assets 技能包 的bugfix 契约(Bugfix contract)展开,讲解在正式测试资产体系(Requirement/Bug → Feature → D1-D5 Case → Manifest → 固定脚本 → 结果)下,一个已确认的产品缺陷应当如何被记录、如何补齐回归覆盖、如何分类覆盖缺口,以及当产品契约本身变化时如何向 Feature、Cases、product、scripts、manifest 逐层传播。读完本文,你将掌握test/changes/bugs/*.yaml变更记录的完整字段语义、七类覆盖缺口(coverage gap)的判别方法,以及一条可执行的 bugfix 落地链路,并能直接在仓库中对照真实案例进行实操。

一、Bugfix Contract 是什么:一条缺陷必须留下的三重证据

在 Nexent 的正式测试资产体系中,测试资产的主权归属产品仓库(见 lifecycle.md),其核心主张是“每个被确认的产品 bug,都必须沉淀为可追踪、可复验的资产”,而不是一次性的口头修复。

bugfix.md 定义了这条契约的硬性要求:每个已确认的产品 bug 必须同时具备以下两部分产出:

  1. 一条test/changes/bugs/*.yaml变更记录:描述缺陷的观察行为、期望行为、逃逸阶段、覆盖缺口及影响面;
  2. 一个回归结果(三选一):要么复用一条已因正确原因失败的正式 Case,要么强化一条现有 Case 及其脚本,要么在最低证明阶段新增 Case,并在任何缺陷逃逸到的更高阶段补充保护性覆盖。

同时,覆盖缺口必须被归类为七种既定类型之一(见第二节),禁止使用insufficient coverage这类无法指导行动的模糊描述。

从源码结构看,这条契约已被校验器固化:test/tools/validate_changes.py会强制change_type: bugfix的记录必须存放在test/changes/bugs/目录(对应映射表CHANGE_DIRECTORIES = {"requirement": "requirements", "bugfix": "bugs", "refactor": "refactors", "test-fix": "test-fixes"}),并且 change.schema.json 中的allOf条件校验会要求 bugfix 类型的记录必须携带bug_id、observed_behavior、expected_behavior、escaped_stage、coverage_gap、feature_contract_changed六个字段,缺一不可。这意味着“没写清楚缺陷细节”的记录在统一校验阶段就会直接失败。

二、三类回归结果(Regression Outcomes)的选择逻辑

契约给出的三种回归结果并非并列可选,而是按“覆盖缺口的位置”递进选择的策略:

回归结果适用场景特点
复用(reuse)现有正式 Case 已经覆盖该行为,且当前以正确原因失败(例如 D1 单元测试本应拦截该缺陷但未通过)不新增资产,先修脚本/产品,让既有 Case 转绿
强化(strengthen)现有 Case 覆盖了相邻行为,但断言边界不够,恰好放过了缺陷修改现有 Case 及其脚本,收紧断言或补充边界输入
新增(add)现有资产完全未覆盖该行为在最低证明阶段(通常是最便宜的 D1)新增 Case;若缺陷已逃逸到更高阶段(如 production、D4/D5),还需在逃逸到的每个更高阶段补充保护性 Case

一个值得注意的细节:契约强调“在最低证明阶段新增 Case,并在任何逃逸的更高阶段补充保护”,这对应 SKILL.md 中描述的设计纪律——先用成本最低的单元/组件级测试锁定行为,再用更重的集成、E2E、可靠性测试验证链路。这样既能快速回归,又不至于把所有验证都堆在昂贵的端到端阶段。

三、七类覆盖缺口(Coverage Gap)分类体系

契约明确要求用以下七种类型之一来描述“现有正式基线为何漏掉了这个缺陷”,不得使用insufficient coverage这类笼统表述:

类型含义(可推断)对应的修复动作方向
missing_case该行为没有任何正式 Case 覆盖新增 Case(最低阶段 + 逃逸阶段)
missing_boundary有 Case,但边界条件未覆盖(如“完成前可见”与“回滚后不可见”的边界)强化现有 Case 的边界输入与断言
missing_assertion有 Case,但断言不足,未校验关键结果强化现有 Case 的 expected_results / assertions
manifest_omissionCase 存在但未登记进 manifest,或 manifest 条目缺失补齐 manifest 条目并重新计算哈希
incorrect_skip某阶段被错误地跳过(如误标skipped_by_policy或未实现)修正 skip 状态,恢复执行
non_blocking_result缺陷表现为“结果非阻塞但行为错误”,而现有断言未拦截增加阻塞性断言
missing_contract产品契约本身未定义该行为,或定义错误先更新 Feature 契约,再传播到 Cases/脚本/manifest

在 change.schema.json 的coverageGap定义中,type字段被限制为上述七种枚举值(enum),且强制要求description非空。这说明分类不仅是约定,而是被 JSON Schema 层面强制执行的机器可校验约束。

四、契约变更的传播顺序:什么时候改 Feature,什么时候只改 Case

契约最后一条给出了明确的边界判定规则:

  • 如果 Feature 契约已经是正确的,不要修改它——缺陷只说明测试漏了,修复动作应该落在 Cases/脚本/manifest 上;
  • 如果预期行为发生变化,或行为原本未定义,则必须先更新 Feature(及其业务规则),再更新 Cases、产品实现、脚本与 manifest。

这条顺序之所以重要,是因为 Nexent 的正式测试资产是一条可追踪链(见 SKILL.md 顶部图示):

Requirement or Bug -> Feature -> D1-D5 Case -> Manifest -> Fixed Script -> Result

Case 通过feature_id与 Feature 绑定,manifest 通过case_id与 Case 绑定、通过contract_hash/implementation_hash与 Case 内容和脚本内容绑定。若先改 Case 再改 Feature,会导致 traceability 校验失败(test/tools/validate_traceability.py专门负责这类一致性检查)。只有按“契约先行、资产跟随”的顺序,才能保持这条链从需求到结果始终闭合。

五、真实案例拆解:CMSR-BUG-001~004 的完整记录

仓库中的 test/changes/bugs/model-attempt-stream-visibility.yaml 是该契约的完整落地样本,围绕 Feature CMSR-005(Silent model retry stream visibility) 记录了 4 个缺陷。下面拆解其字段与结构。

5.1 YAML 骨架与顶层结构

schema_version: '1.0' changes: - change_id: CMSR-BUG-001 change_type: bugfix bug_id: CMSR-BUG-001 title: Silent retry produces blank steps and delayed successful output description: Correct model stream delivery and label-only reasoning while preserving attempt rollback. observed_behavior: ... expected_behavior: ... escaped_stage: production coverage_gap: type: missing_boundary description: ... feature_contract_changed: true affected_features: {existing: [], added: [CMSR-005], modified: [], retired: []} affected_cases: {existing: [], added: [CMSR-D1-001, ...], modified: [], retired: []}

关键字段语义(与 change.schema.json 一一对应):

  • schema_version:固定为'1.0'(Schema 中用const强制);
  • change_id/bug_id:符合^[A-Z][A-Z0-9]*(?:-[A-Z0-9]+)+$的标识符模式;
  • observed_behavior/expected_behavior:缺陷的实际表现与期望行为,是回归 Case 设计的输入;
  • escaped_stage:缺陷逃逸到的阶段,枚举为D1、D2、D3、D4、D5、production或unknown。本案例中四个 bug 全部标记为production,说明缺陷穿透了全部五级防线,因此每个逃逸阶段都需要补保护;
  • coverage_gap:包含type(七枚举)与description;
  • feature_contract_changed:布尔值,标识本次修复是否改变了产品契约;
  • affected_features/affected_cases:{existing, added, modified, retired}四个列表组成的增量描述,用于追溯影响面。

5.2 四个缺陷的缺口分类对照

Bug标题覆盖缺口缺口描述(要点)资产影响
CMSR-BUG-001Silent retry produces blank steps and delayed successful outputmissing_boundary既有测试只断言最终回滚,未断言“下一个 provider 片段/纯代码步骤渲染之前的可见性”新增 CMSR-005,新增 D1/D2/D3/D4/D5 六个 Case
CMSR-BUG-002Valid semantic repair omits streamed and persisted model outputmissing_boundary既有测试覆盖了 provider 传输层重试,却断言语义修复输出应被隐藏修改 CMSR-005,新增 CMSR-D1-003,修改 D2~D5 五个 Case
CMSR-BUG-003Step labels arrive after content and disappear from reasoningmissing_boundary既有测试断言标签被抑制,但未校验 CoreAgent 事件顺序与首个 token 前的运行态 UI新增/修改 D1~D5 六个 Case
CMSR-BUG-004Reasoning-only output budget exhaustion lacks actionable diagnosismissing_assertion既有测试只检查修复生命周期标记,未检查模型预算、reasoning-only 响应形状、attempt 关联与诊断隐私新增 CMSR-D1-004,修改 CMSR-D5-001

从中可以总结出该契约的实践规律:大部分缺陷不是“完全没有测试”,而是“测试断言的边界或对象选错了”——这正是分类体系禁止笼统使用insufficient coverage的原因。只有精确指出缺的是边界(missing_boundary)还是断言(missing_assertion),回归资产才能被精准补强。

5.3 从记录到用例:affected_cases 如何落地

bug 记录的affected_cases列表与 test/cases/d1/chat-message-model-silent-retry.yaml 等各阶段用例文件一一对应。以 CMSR-D1-001 为例,该用例(type: SDK-UT,priority: P0)围绕Observer.add_model_new_token与 attempt 生命周期,明确断言“安全前缀在 flush 前可见、原始<code>标签保留、无失败 attempt 残留”,并声明forbidden_side_effects(无陈旧片段跨 attempt 传播、observer 不执行任何代码)——这正是对 CMSR-BUG-001 缺口(“完成前不可见”)的正面拦截。

而 CMSR-D1-004(type: SDK-UT,priority: P1)则直接回应 CMSR-BUG-004 的诊断隐私诉求:断言默认日志不含原始文本、开启NEXENT_PROTOCOL_DIAGNOSTIC_CONTENT显式 opt-in 后仅输出转义且截断的预览(见 test_protocol_rejection_diagnostics.py,其中断言"PRIVATE_REASONING" not in first与"x" * 257 not in preview,即预览被截断、敏感尾部永不出现)。

六、从 Bug 记录到可执行回归的完整链路

结合 lifecycle.md 中“Bug”一节与仓库实况,一个 bugfix 的标准落地链路如下:

  1. 确认缺陷:区分observed_behavior与expected_behavior;
  2. 判定契约归属:若产品契约缺失或错误,先在 test/features/ 下更新 Feature 与业务规则;若契约正确则跳过;
  3. 识别逃逸阶段:确定escaped_stage,据此决定需要在哪些阶段补保护;
  4. 分类覆盖缺口:从七类中选定最精确的一类并给出描述;
  5. 写入变更记录:在test/changes/bugs/下新建 YAML(注意:一个文件内多条记录必须同为change_type: bugfix,且文件不得直接放在test/changes/根下,否则 validate_changes.py 会报错);
  6. 设计/强化 Cases:按“最低证明阶段新增 + 逃逸阶段保护”原则,更新 test/cases/d1/ 至 test/cases/d5/ 下对应模块的用例文件;
  7. 实现固定脚本:在test/automation/d1~test/automation/d5下实现或修改脚本(禁止引用 Legacy UT 路径,即test/backend、test/sdk、test/ext_components);
  8. 增量更新 manifest:在 test/manifests/ 下登记/更新条目,Case 变更时重算contract_hash,脚本变更时重算implementation_hash(算法见 test_asset_lib.py 中的case_contract_hash与implementation_hash,前者对规范化 JSON 做 sha256,后者对实现文件内容做哈希);
  9. 统一校验并生成 Excel 视图:运行python test/tools/validate_test_assets.py --phase implementation --generate-excel;只读漂移检查用python test/tools/generate_excel.py --check;
  10. 执行受影响的选择器并记录结果:Schema 合法不等于执行通过,必须实际运行用例并保留结果(见 SKILL.md)。

manifest 的哈希机制值得特别注意:在 d1-d5-manifest.schema.json 中,contract_hash与implementation_hash均要求sha256:前缀的 64 位十六进制串;而 tests/test_formal_test_assets.py 中的test_implementation_hash_detects_script_drift验证了脚本内容变化会立即导致哈希漂移——这保证了“改了脚本却忘了更新 manifest”会被机器拦截。

七、分级回归在 Bugfix 中的实践:以 CMSR 为例

为了直观展示“最低阶段 + 逃逸阶段保护”的落地方式,下表汇总了 test/manifests/chat-message-model-silent-retry.yaml 中各 Case 的状态与实现:

Case阶段状态实现框架/脚本说明
CMSR-D1-001D1implementedpytest:test_model_attempt_stream.py 的test_cmsr_d1_001_*安全内容在完成前可见、回滚重置分裂标记状态、legacy fence 抗碎片化
CMSR-D1-002D1implementedcustom:test_reasoning_card.ts 的CMSR-D1-002运行中 Step 标签先于内容且跨回滚保留、历史标签前置、终态纯标签卡片移除
CMSR-D1-003D1implementedpytest:test_semantic_repair_stream.py 的test_cmsr_d1_003_*语义修复复用同一流式 attempt 生命周期,首个安全块在完成前可见,修复仅执行一次
CMSR-D1-004D1implementedpytest:test_protocol_rejection_diagnostics.py 的test_cmsr_d1_004预算耗尽诊断可归因、默认无内容泄漏、opt-in 预览有界转义
CMSR-D2-001D2implementedpytest:test_model_attempt_sse.py 的test_cmsr_d2_001SSE 边界校验:成功 delta 在 provider 完成前发布、control 事件不入库
CMSR-D3-001D3blocked—(required_mock_services: [model-provider])需要可销毁的 Nexent 实例;blocked 不等于通过(见 manifest 规则)
CMSR-D4-001D4blocked—需要浏览器对新部署实例与可控暂停 provider 的验证
CMSR-D5-001D5blocked—需要物理 provider 反复故障与并行子 agent 持久化验证

其中 D2 的 test_model_attempt_sse.py 是一个典型的“强化 + 边界”样本:它用可控的异步 provider 夹具先发布step_count与begin,然后暂停在成功 fragment 发出之后,断言 SSE 客户端此时已收到该 fragment("<code>print" in published[-1]),随后才释放 provider 完成commit与parse。这直接验证了 CMSR-BUG-001 要求的“成功内容在完成前可见”,且通过persist_assistant_run_batch的 mock 断言了持久化单元中不含失败内容(failed_content not in "".join(contents))。测试同时覆盖transport与semantic两种失败类型,印证了契约“同一缺陷在不同失败形态下都要有边界覆盖”的要求。

八、校验与落地工具:让契约可执行

Nexent 把上述契约固化为一套工具链,避免“文档写了但没人执行”:

  • validate_test_assets.py:统一入口,串行执行 Feature、Case、Change、Traceability 校验,--phase implementation时追加 Manifest 校验,全部通过后--generate-excel再生成确定性 Excel 视图;任何问题都会以ValidationIssue汇总输出并返回非零退出码;
  • validate_changes.py:校验 change 记录目录归属与 Schema,强制执行“bugfix 必须放test/changes/bugs/”;
  • validate_manifest.py 与 validate_traceability.py:分别校验 manifest 条目/哈希与 Feature↔Case 的追踪一致性;
  • generate_excel.py:生成test/generated/Nexent_测试基线.xlsx的七个 Sheet(00_说明、01_功能清单、02_D1~06_D5),--check模式用于只读漂移检测,杜绝手工编辑基线表(SKILL.md 明确要求 Excel 是确定性只读视图)。

从测试来看,tests/test_formal_test_assets.py 还覆盖了若干边界规则,例如:test_validation_rejects_local_sql_path(业务测试不得依赖具体 SQL 文件路径)、test_credential_fields_require_asset_reference(凭据必须用{asset_ref: ...}逻辑引用,禁止内嵌字面凭据)、test_manifest_rejects_cross_stage_and_traversal(manifest 实现路径必须停留在自身阶段目录内、解析符号链接后也不得越界)。这些规则共同保证了 bugfix 资产的可移植性与安全性。

九、小结:Bugfix Contract 的实操要点

  • 每个已确认 bug =test/changes/bugs/*.yaml记录 + 一个明确的回归结果(复用 / 强化 / 新增),缺一不可;
  • 覆盖缺口必须从七种既定类型中精确选择,insufficient coverage是被禁止的无效描述;
  • 契约正确时只动 Cases/脚本/manifest;契约缺失或变化时必须先改 Feature,再逐层传播,保持“Bug → Feature → Case → Manifest → Script → Result”链路闭合;
  • 新增回归遵循“最低证明阶段 + 逃逸阶段保护”原则,D1 用 pytest/node 测试锁定单元行为,D2 用 SSE 契约测试锁定 API 边界,D3~D5 在具备前提条件后逐步解锁集成、E2E 与可靠性验证;
  • 所有资产变更最终都要通过python test/tools/validate_test_assets.py --phase implementation --generate-excel统一校验,并实际运行受影响选择器记录结果。

对照仓库中的 model-attempt-stream-visibility.yaml 与其配套的 Feature、Cases、manifest、脚本,即可完整复盘一次从“线上缺陷”到“分级回归资产”的规范闭环,这也是任何新缺陷进入 Nexent 正式测试资产体系时必须遵循的模板。

  • 人工智能
  • AI Agent
  • Agent 编排
  • 低代码
  • AI 技能
  • RAG

【免费下载链接】nexent

Nexent 是一个开源智能体SDK和平台,能够将描述流程的自然语言转化为完整的多模态服务 —— 无需编排,无需复杂拖拉拽。基于 MCP 工具生态系统构建,Nexent 提供灵活的模型集成、可扩展的数据处理和强大的知识库管理。我们的目标很简单:将数据、模型和工具整合到一个智能中心中,让任何人都能轻松地将 Nexent 集成到项目中,使日常工作流程更智能、更互联。

项目地址:https://gitcode.com/ModelEngine/nexent
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进