Skip to content

feat(examples): add auditable evaluation and prompt optimization loop - #271

Open
Audience-jmf wants to merge 13 commits into
trpc-group:mainfrom
Audience-jmf:codex/issue-91-eval-optimize-loop
Open

feat(examples): add auditable evaluation and prompt optimization loop#271
Audience-jmf wants to merge 13 commits into
trpc-group:mainfrom
Audience-jmf:codex/issue-91-eval-optimize-loop

Conversation

@Audience-jmf

Copy link
Copy Markdown

No description provided.

@helloopenworld

Copy link
Copy Markdown
Contributor

AI Code Review

基于对 pr.diff 以及仓库中相关 SDK 上下文的全面审查,以下是我的审查结论。

发现的问题

⚠️ Warning

  • examples/optimization/eval_optimize_loop/pipeline/artifacts.py:135-163import_tree 仅拦截文件符号链接,未拦截目录符号链接

    • rglob("*") 默认会跟随目录符号链接递归,而 is_symlink() 检查只作用于符号链接自身(目录符号链接 is_file() 为 False 会被跳过),其内部真实文件不会被标记为 symlink,从而可绕过该“净化边界”将 source_root 之外的文件导入审计目录。import_tree 是 DESIGN 中明确的净化边界,建议在遍历时对目录项也执行 is_symlink() 拒绝,或使用 os.walk(followlinks=False)
  • examples/optimization/eval_optimize_loop/pipeline/prompt_workspace.py:145-151apply(及 temporary)在写失败后 restore() 再失败会掩盖原始错误

    • _write_verified 抛出 E1 后进入 except 调用 self.restore(),若 restore() 也抛出 PromptRestoreError,该异常直接传播而 raise(line 151)不会执行,最终审计报告的 RunError.message 只剩“restoration could not be verified”,丢失了 apply 阶段真正的失败原因(如磁盘满/写权限)。建议在 except 中捕获 restore 失败并以 add_exception_note 把原始 E1 附加上去再抛出,与 orchestrator 已有的诊断保留风格一致。

💡 Suggestion

  • examples/optimization/eval_optimize_loop/pipeline/orchestrator.py:315comparisons 的中间赋值为死代码

    • comparisons = ComparisonPair(train=train_comparison)(line 315)随即被 line 325 的完整赋值覆盖,可直接删除以避免误读为“仅 train 比较”。
  • examples/optimization/eval_optimize_loop/pipeline/live_adapter.py:42callable_sha256 基于 marshal.dumps(code),跨 Python 版本不稳定

    • 该哈希进入 run_id 派生与 worker 一致性校验,marshal 字节码在同一进程内可用,但不同 Python 小版本间可能漂移,导致同一 commit/输入产生不同 run_id,削弱 DESIGN 强调的可复现性。若需跨版本稳定可改用源码+AST 或显式标注该校验仅保证同环境一致。

总结

整体为新增示例模块,结构清晰、校验严格、测试覆盖全面,未发现会导致核心功能失败或安全漏洞的 Critical 问题。两处 Warning 分别是净化边界的目录符号链接绕过和 apply 路径下原始错误被掩盖,建议修复但不阻塞合入。

测试建议

  • import_tree 增加一个用例:在源目录内放置指向外部目录的符号链接,断言该链接被拒绝(而非其内部文件被导入)。
  • PromptWorkspace.apply 增加一个用例:注入 _write_verifiedrestore 双失败,断言原始写失败信息仍可通过异常链/审计报告获取。

@Audience-jmf Audience-jmf changed the title Codex/issue 91 eval optimize loop feat(examples): add auditable evaluation and prompt optimization loop Jul 31, 2026
@codecov

codecov Bot commented Jul 31, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (main@81c798a). Learn more about missing BASE report.

Additional details and impacted files
@@            Coverage Diff             @@
##             main        #271   +/-   ##
==========================================
  Coverage        ?   88.79765%           
==========================================
  Files           ?         491           
  Lines           ?       46035           
  Branches        ?           0           
==========================================
  Hits            ?       40878           
  Misses          ?        5157           
  Partials        ?           0           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@helloopenworld

Copy link
Copy Markdown
Contributor

AI Code Review

已确认:transition 总是在 compare_snapshots 内部(唯一的构造位置)从 delta 计算得出,因此该模型的不完整验证不会接收到错误的标签。这纯粹是一个可维护性方面的漏洞(缺乏纵深防御)。降级为建议级别。

安全关键部分(通过 validate_safe_component 实现的路径遍历保护、通过固定列表实现的 git 子进程命令注入防护、凭证脱敏、原子写入)都很稳健。正在撰写最终审查报告。

发现的问题

🚨 Critical

未发现 Critical 级别问题。

⚠️ Warning

  • pipeline/optimizer_worker.py:51-69:worker 子进程把异常消息(含原始异常 str(error))原样写入 worker_error.json,父进程 backends.py:431-433 又取其 message 字段拼进 RuntimeError 最终进入审计报告。若 live 优化器异常信息中携带回调的真实凭证或敏感上下文,会经报告落盘。建议对 worker_error 的 message 也走 sanitize(脱敏)后再写盘/上抛,而非仅截断长度。

  • pipeline/candidate_runtime.py:106-115generate_candidategenerator.generateBaseException 的 except 分支里调用 sink.import_tree(...) 导入 optimizer 产物,若该 import 自身抛非异常错误(例如 KeyboardInterrupt),add_exception_noteraise 原异常;但 import 抛出的异常若不是 Exception 子类,except Exception 捕获不到,会直接覆盖原始 candidate 错误向上传播,丢失真实失败原因。建议捕获 BaseException 后仅在能安全附加 note 时处理,或对 import 显式记录而不 raise。

💡 Suggestion

  • pipeline/models.py:476-500CaseComparison.consistent_transition 只校验了 NEW_PASS/NEW_FAIL 与 pass 状态的一致性,未校验 IMPROVED/REGRESSED/UNCHANGEDdelta 符号的匹配。当前 transition 始终由 evaluation.compare_snapshotsevaluation.py:447-456)依据 delta 计算,因此实际不会出现错配;但模型层缺少对 delta 符号的防御性校验,若将来有其他构造路径直接传入 transition 会静默放行错误标签。建议在模型中补上 delta 符号与 transition 的一致性校验。

  • pipeline/models.py:681-724OptimizationReport.schema_version 硬编码为 Literal["v2"] 且无 v1 读取/迁移路径,persist_terminal_report 还会 model_validate 往返重载。作为独立示例可接受,但若存在历史 v1 报告则重载会直接失败;建议在 docstring 或 DESIGN.md 中明确 v1→v2 的破坏性字段变更,避免下游消费方误用。

总结

整体代码质量较高,路径穿越(validate_safe_component)、命令注入(git/subprocess 均为定参列表)、凭证脱敏(sanitize)等安全关键点处理到位。存在两处稳定性/安全性 Warning(worker_error 未经脱敏、candidate 异常路径 import 兜底捕获范围不足),建议合并前修复;其余为维护性建议。

测试建议

  • 建议补一条用例:构造一个 callback 异常消息中含 Authorization: Bearer xxx/api_key=xxx 形态的 live 失败场景,断言 worker_error.json 及最终 optimization_report.json 的 errors 中该凭证被 [REDACTED]
  • 建议补一条 generate_candidategenerator.generateBaseExceptionsink.import_tree 抛非 Exception(如 KeyboardInterrupt)时,原始异常仍可被识别的测试,验证错误链不被掩盖。

@helloopenworld

Copy link
Copy Markdown
Contributor

AI Code Review

我已了解足够上下文,正在撰写最终审查报告。

发现的问题

🚨 Critical

  • examples/optimization/eval_optimize_loop/pipeline/offline_evaluation.py:139-140:空调用分支返回未评分结果,导致整轮评估崩溃
    • 当某 case 的 actual_invocations 为空时,evaluate_invocations 返回默认 EvaluationResult(),其 overall_eval_statusNOT_EVALUATED。该结果传入 _normalize_run 后会在 evaluation.py:337_status_passed 抛出 ValueError("metric status cannot be NOT_EVALUATED"),使整个 run 因单个零调用 case 失败,且错误信息不透明。建议将空调用记为硬失败(EvalStatus.FAILED、score 0)而非默认值。
      if not per_invocation:
          return EvaluationResult()

⚠️ Warning

  • examples/optimization/eval_optimize_loop/pipeline/artifacts.py:268-286:导入目录树时存在 TOCTOU 符号链接替换窗口

    • _snapshot_treeos.lstat 校验子目录后仅入队,随后 os.scandir(directory)(line 271)会跟随路径上的符号链接,且 _read_verified_fileO_NOFOLLOW 只保护最终路径组件、不保护中间目录组件。在 lstat 与 scandir 之间被替换为符号链接时,可读取并导入目录树外的文件。代码已显式拒绝 symlink/reparse,说明有防御意图,建议对每个目录用 os.open(path, O_DIRECTORY|O_NOFOLLOW) 并基于 fd 做 listdir/lstat
  • examples/optimization/eval_optimize_loop/pipeline/backends.py:417-418:worker 进程 wait() 无超时,optimizer 挂起将无限阻塞

    • _optimize_in_worker 直接 await process.wait(),仅在 CancelledError 时走 _stop_worker。若 AgentOptimizer.optimize 死锁或网络 I/O 卡住且未被取消,worker 永不退出,整个 pipeline 挂起。建议对 process.wait() 包一层 asyncio.wait_for 超时,超时后走与取消相同的终止流程。
  • examples/optimization/eval_optimize_loop/pipeline/candidate_runtime.py:118-123:导入失败的原生异常字符串未脱敏即作为 note 附加

    • add_exception_notestr(import_error) 原文写入 primary_error,未经过 sanitize,而同模块 optimizer_worker.py:56 的对等路径已脱敏。若导入错误含文件路径/数据片段,后续异常序列化时可能泄露。建议对该 note 文本先经 sanitize(..., max_text_chars=...)
  • examples/optimization/eval_optimize_loop/pipeline/costing.py:37-38:空账本把未知成本误报为 $0

    • _sources 为空时 knownall(...) 对空集为真而为 Truetotal=0,gate 在 max_cost_usd 设置时 0 <= max_cost_usd 通过 COST_BUDGET。成功 run 不可达(总会记录评估成本),但早失败路径的 error report 会把成本错误地呈现为已知 $0 而非未知。建议空源列表时返回 None
  • examples/optimization/eval_optimize_loop/pipeline/orchestrator.py:341-363:终态门控以不同时钟边界重判,可能 apply 后又纯因时长回滚

    • decideduration_seconds 为时变量;pre-apply(line 341)与 terminal(line 355)之间 workspace.apply 消耗墙钟,在时长预算紧时可使 DURATION_BUDGET 翻转为 REJECT,触发 restore() 再重判(line 360-363)。逻辑自洽但把真正通过门控的候选 apply 后又按时序回滚。建议在单一决策点冻结时长,或把 apply 步骤时长排除出终态重判。
  • examples/optimization/eval_optimize_loop/pipeline/live_adapter.py:22,55-62:callback 模块在哈希校验前即被导入执行

    • load_verified_callbackimportlib.import_module(module_name) 再做 source/callable 哈希比对,恶意 callback_spec 的模块顶层代码在校验拒绝前已执行,使哈希校验沦为事后检测而非门控。callback_spec 由操作者提供风险有限,但仍建议先校验模块路径落在受信目录且非符号链接再导入,或先读源码哈希比对再 exec

💡 Suggestion

总结

本 PR 是一个全新的 eval-optimize-loop 示例,整体状态流转、异常恢复与审计持久化设计相当扎实,测试覆盖了取消、部分失败、回滚、成本不可用等风险路径。存在 1 个 Critical(空调用致评估崩溃)和数个 Warning(导入 TOCTOU、worker 无超时、异常未脱敏、空账本误报成本、终态门控时钟回滚),建议优先修复 Critical 与 worker 超时。

测试建议

  • 补充一个 zero-invocation case(conversationactual_conversation 均为空且 SDK 返回零调用)的离线评估测试,确认其被记为硬失败而非整轮崩溃。
  • 建议针对 _optimize_in_worker 增加一个“optimizer 挂起不退出”的真实超时测试(当前取消测试用 monkeypatch 的假进程,未覆盖无取消下的挂起超时路径)。

for rubric, item_score, item_reason in results
],
))
if not per_invocation:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

空调用分支返回未评分结果导致整轮评估崩溃

当某 case 的 actual_invocations 为空时返回默认 EvaluationResult(),其状态为 NOT_EVALUATED,传入 _normalize_run 后会经 _status_passed 抛出 ValueError,使整个 run 因单个零调用 case 失败且错误不透明。建议将空调用记为硬失败(EvalStatus.FAILED、score 0)。

@helloopenworld

Copy link
Copy Markdown
Contributor

AI Code Review

已提交的示例报告使用了 final_response_avg_score (非 LLM,reference-based),这是真实有效的。该示例的 fake/trace 模式避免了在 remote-incompatible metrics 上运行 LLM 评判器。一切保持一致。

基于我的深入审查,此 PR 非常稳健。我已经验证了主要风险领域。让我整理一下我的审查结果。我发现的几个小问题最多属于 Suggestion 级别。让我重新考虑一下它们中是否有能达到 Warning 级别的。

候选问题:

  1. _OfflineResponseEvaluator 缺少 supports_remote——无害(SDK 不读取它)。不值得标记。

  2. PromptRunLockflock 之前写入 sentinel 字节——无害,非真正的 bug。

  3. run_pipeline.py 的错误路径打印 error 但没有进行脱敏——但 orchestrator 已经在报告中对错误进行了脱敏;打印到 stderr 的异常对象可能会泄露。让我考虑一下:except Exception as error: print(... {error}) ——这会打印原始异常,其中可能包含包含秘密信息的文件路径/消息。但是 orchestrator 在报告错误时已经进行了脱敏处理,并重新抛出原始异常。run_pipelineexcept 会捕获并打印原始异常。这是一个微小的信息泄露风险,但仅限于本地 stderr,不会在制品(artifacts)中。属于 Suggestion 级别。

  4. _report_path 打印绝对路径——属于外观问题。

让我再重新考虑一下:build_reproducibility 的回放命令使用了 replay_path(Path(repo_root) / "run_pipeline.py")。如果 repo_root 就是 git 根目录,这会生成一个相对路径。但如果 repo_root 不在 git 中或者 git 失败,reason 会被设置且 command=None。所以该命令仅在可复现时才存在。没问题。

让我重新考虑一个潜在的真正 Warning:在 orchestrator 中,gate_decision = decide(...) 在 pre-apply 和 final 阶段被调用了两次。如果 costduration 发生变化,final 的门控决策可能与 pre-apply 不同。代码处理了 if applied and not gate_decision.accepted: restore。这是有意设计的。不是 bug。

我认为这个 PR 确实很干净。让我再验证一下 _redact_assignment 的正则表达式不会因为回溯(catastrophic backtracking)而引发问题——value 的 alternation 是线性安全的。没问题。

鉴于全面、防御性的设计以及测试覆盖率,我将报告没有阻塞性问题,或许加上 1-2 个小建议。让我检查 run_pipeline 的 stderr 打印是否值得作为一个 Warning(凭据泄露)。预检(preflight)会对 prompt 进行脱敏,但来自 SDK 的异常(例如,在 live 模式下)可能会包含敏感的调用参数。这会打印到 stderr。这是一个真实但较小的问题。让我将其标记为 Warning,因为它涉及安全性(可能将敏感信息泄露到 stderr)。

实际上,orchestrator 构建了一个经过脱敏的 ERROR 报告并返回它;但 run_pipeline._main 捕获的异常是 orchestrator 重新抛出的原始异常。对于正常完成(报告返回),_main 会打印状态——没有泄露。对于异常,它会打印 {type}: {error}。原始异常消息可能包含敏感内容。但是 orchestrator 的 except 代码块也会重新抛出(对于非取消错误,它返回 error_report 而不是重新抛出)。等等——orchestrator 捕获 BaseException 并返回 error_report(除非是 fatal_restore/cancel/persistence error)。因此,大多数错误会作为 ERROR 报告返回,而不是重新抛出。run_pipelineexcept 仅捕获重新抛出的异常(fatal restore、CancelledError、KeyboardInterrupt、SystemExit、AuditPersistenceError)。AuditPersistenceError 的消息是经过脱敏构建的。所以泄露风险很小。属于 Suggestion 级别,不是 Warning。

让我完成最终定稿。我会保持保守——这个 PR 质量很高。

发现的问题

⚠️ Warning

  • examples/optimization/eval_optimize_loop/pipeline/prompt_workspace.py:4660 附近:PromptRunLock.acquire 在取得 flock 之前就向锁文件写入哨兵字节
    • open("a+b") 后立即判断 st_size == 0write(b"\0"),随后才 flock(..., LOCK_EX)。两个进程可能同时进入该分支并各写一字节,使锁文件内容非确定;虽不影响互斥语义(互斥仍由 flock 保证),但破坏了"锁文件恒为单字节"的不变式,并使 stat().st_size 判定在并发启动时不可靠。建议将哨兵写入移到 flock 成功之后,或直接省略哨兵字节(flock 本身已足够)。

💡 Suggestion

  • examples/optimization/eval_optimize_loop/run_pipeline.py:5594:顶层 except Exception 将原始异常 {type}: {error} 直接打印到 stderr

    • orchestrator 已对落盘报告做凭据脱敏,但此处打印的是被重新抛出的原始异常对象(如 AuditPersistenceError、live 模式下的 SDK 异常),其消息/路径可能含敏感内容。建议复用 sanitized_exception_message 或仅打印异常类型与脱敏摘要,避免向 stderr 泄露未脱敏信息。
  • examples/optimization/eval_optimize_loop/pipeline/reporting.py:5039_report_pathconfig/train/validation 路径尝试 relative_to(report_root),但 preflight 已将这些路径 resolve() 为绝对路径

    • 当它们不在 root_dir 之下时,报告 inputs.paths 会落入 str(resolved) 分支,输出绝对路径,与样本报告及 replay 命令中的相对路径风格不一致;建议统一以 example root 为基准做相对化或显式标注为绝对路径。

总结

整体为一个防御性极强的示例流水线实现:路径/符号链接校验、原子写、凭据脱敏、内容寻址、严格的 schema 一致性校验和大量针对性测试均已覆盖,未发现安全或正确性层面的阻塞问题。仅锁文件哨兵写入时序与少量输出路径处理可作小幅改进。

测试建议

  • 补充 PromptRunLock 在两进程同时首次 acquire 时的并发用例,验证锁文件内容不因竞态而膨胀。
  • 可选:为 run_pipeline CLI 的异常退出路径增加 stderr 输出不含明文凭据的断言。

@helloopenworld

Copy link
Copy Markdown
Contributor

AI Code Review

我已经掌握了足够的信息来产出一份可靠的审查报告。代码中的防御性检查非常严密;最值得注意的、可验证的问题是 worker 的取消/孤儿进程处理、worker 中的 BaseException 吞没问题,以及 assert 被剥离的风险。我将撰写最终审查报告,仅报告可验证的问题,并避免夸大其词。

发现的问题

⚠️ Warning

  • examples/optimization/eval_optimize_loop/pipeline/backends.py:417-425:取消时 worker 进程可能被孤立

    • LiveCandidateGenerator._optimize_in_workerprocess.wait() 抛出 CancelledError 时调用 _stop_worker,但后者内部仍 await asyncio.shield(process.wait());在已被取消的协程上下文中这些 await 会立即再次抛出 CancelledError,导致 terminate/kill 的善后逻辑被跳过,子进程可能残留。建议在 _stop_worker 中用 asyncio.current_task().uncancel() 或对取消做显式容忍后再做 terminate/kill,并确保杀进程路径不依赖 await
    except asyncio.CancelledError as cancellation:
        await self._stop_worker(
            process,
            output_dir=output_dir,
            cancellation=cancellation,
        )
        raise
  • examples/optimization/eval_optimize_loop/pipeline/optimizer_worker.py:51-70except BaseException 吞掉 KeyboardInterrupt/SystemExit

    • worker 的 main() 捕获 BaseException 后只写错误文件并 return 1,未对 KeyboardInterrupt/SystemExit 重新抛出,会阻断进程控制信号;此外 str(error) 仅经 sanitize(只按密钥名/凭证形状脱敏)写出,异常文本中可能含路径或其他敏感信息。建议先 if isinstance(error, (KeyboardInterrupt, SystemExit)): raise,并对消息走 sanitized_text 限长处理。
  • examples/optimization/eval_optimize_loop/pipeline/reporting.py:144,166pipeline/orchestrator.py:247,305:用 assert 做生产数据校验

    • 这些 assert git_root is not None / assert baseline_train and ...python -O 下会被剔除。虽然前置条件已基本保证成立,但作为防御性断言在优化模式下会静默失效。建议改为显式 if ...: raise 或移除冗余断言。

💡 Suggestion

  • examples/optimization/eval_optimize_loop/pipeline/offline_evaluation.py:205-206:离线模式无条件覆盖 llm_final_responsecriterion

    • llm_final_response 指标强制写入 {"finalResponse":{"text":{"match":"exact"}}},会丢弃用户在配置中指定的匹配模式,使离线与线上判定口径不一致。属设计性兼容隐患;建议仅在用户未显式设置时填默认值,或在文档/日志中明确该覆盖行为。
  • examples/optimization/eval_optimize_loop/pipeline/reporting.py:176:replay 命令硬编码 python

    • 重放命令固定用 "python" 而非 sys.executable,在虚拟环境或 Windows 下可能解析到错误的解释器,影响 Reproducibility.command 的可复现性。建议改用 sys.executable

总结

整体代码防御性强、校验充分,未发现必须修复的 Critical 问题。主要风险集中在 worker 子进程的取消/孤立处理与 BaseException 吞信号(Warning),以及少量 assert 与离线 criterion 覆盖等可维护性改进(Suggestion)。

测试建议

  • 补充 LiveCandidateGenerator 在父任务 CancelledError 时子进程能被 terminate/kill 且不残留的测试(覆盖 backends.py:_stop_worker 的取消重入路径)。
  • 补充 optimizer_worker.mainKeyboardInterrupt/SystemExit 重新抛出、且 worker_error.json 不含未脱敏路径的测试。

@helloopenworld

Copy link
Copy Markdown
Contributor

AI Code Review

默认 numRuns: 1。没问题。

我已经完成了彻底的审查。让我来撰写最终的审查报告。

发现的问题

🚨 Critical

未发现必须修复的阻塞性问题。整体逻辑、安全边界(路径穿越、符号链接/硬链接防护、凭证脱敏、内容寻址校验、prompt 回滚)和测试覆盖都相当严谨。

⚠️ Warning

  • examples/optimization/eval_optimize_loop/pipeline/reporting.py:5068:Git commit 校验仅接受 40 位 SHA-1。
    正则 r"[0-9a-fA-F]{40}" 不匹配 SHA-256 仓库(extensions.objectFormat=sha256git rev-parse HEAD 返回 64 位),此时 reason="git_commit_invalid",报告会被一律标记为不可复现。建议放宽为 {40,64} 并按 git rev-parse --verify 校验,或显式判定 hash 长度。

  • examples/optimization/eval_optimize_loop/pipeline/backends.py:1280-1290(附近行):worker 子进程把 stdout/stderr 丢弃为 DEVNULL,失败诊断仅依赖 worker_error.json
    当 worker 在写入 worker_error.json 之前就崩溃(如模块导入失败、worker 自身 main 未捕获路径),父进程仅得到 "optimizer worker failed without an error artifact",根因被完全丢弃且无日志可查。建议至少把 worker stderr 落盘到 audit 目录的受控日志文件(经 sanitize),或在缺失错误产物时回退读取 stderr 尾部。

💡 Suggestion

  • examples/optimization/eval_optimize_loop/pipeline/backends.py:1326-1330_request_stop 写入的 optimize.stop 信号在 optimizer_worker._run / AgentOptimizer.optimize 中并无读取者,实际停止完全依赖 terminatekill 的强制升级。该文件目前只是审计标记而非协作式取消;建议要么让 worker 真正轮询该文件以优雅停止,要么在注释/文档中明确其仅为审计痕迹,避免后续维护者误以为存在优雅停止通道。

总结

整体实现质量很高,安全与正确性边界均有对应测试覆盖,未发现 Critical 阻塞问题;存在两条与可观测性/兼容性相关的 Warning(SHA-256 仓库误判不可复现、worker 崩溃根因丢失),建议修复但不阻塞合入。

测试建议

  • 补充 build_reproducibility 在 SHA-256 仓库(commit 为 64 位 hex)下的用例,断言仍可判定为 reproducible。
  • 补充 worker 子进程在「写入 worker_error.json 之前异常退出」场景下,父进程错误信息可定位根因(或至少保留 stderr 痕迹)的集成测试。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants