Skip to content

perf(capabilities): load issue-fix and reward-memory facade exports on demand - #7749

Open
cocolord wants to merge 1 commit into
mainfrom
codex/lazy-capability-facades-20261031
Open

cocolord wants to merge 1 commit into
mainfrom
codex/lazy-capability-facades-20261031

Conversation

@cocolord

Copy link
Copy Markdown
Collaborator

Delivery brief

  • Goal/source: make each short LoopX CLI process in the heartbeat chain pay only for the code it calls.
  • Current gap: the PR-wait and turn-outcome hooks each import one owner submodule (issue_fix.pr_wait_hook, reward_memory.turn_hooks). Python first initializes the package. Both package __init__ facades eagerly re-exported every workflow: report sources, PR descriptions, issue-fix reward memory, evaluation, dogfood and Codex App staging. Those modules were imported on quota should-run and heartbeat-prompt without being called.
  • Observable result: importing a hook no longer imports unrelated capability workflows. Every facade export is still available and is the identical object from its owner module.
  • Owning boundary: loopx/capabilities/issue_fix/__init__.py and loopx/capabilities/reward_memory/__init__.py. This uses the existing _EXPORTS + module __getattr__ idiom already used by loopx/capabilities/periodic_report/__init__.py. It does not change TS authority, scheduling, settlement, defaults or output contracts.

Change

  • Each facade maps its unchanged __all__ to owner submodules in _EXPORTS.
  • __getattr__ imports the owner on first access, then caches the value in module globals. Lazy from . import name in turn_hooks and monkeypatch.setattr on the facade keep working.
  • __dir__ lists all exports.
  • Name→owner pairs were generated from the original import statements; all 53 + 5 names match __all__.
  • New fresh-process test in tests/test_cli_entrypoint.py:
    • importing both hooks must not load issue_fix.{periodic_report,pr_description,reward_memory} or reward_memory.{codex_app_outcome,dogfood,evaluation};
    • every export must be identical to its owner's object and listed in dir();
    • this test fails on base.

Evidence

All measurements use a synthetic SQLite fixture on macOS. Base is 04c5799b8; head swaps only the two facade files inside the same checkout.

Output parity: for quota should-run, heartbeat-prompt --thin and status, base and head JSON outputs are equal once timestamps are normalized. The compressed scheduler host-facts chunks also match after decoding and normalizing timestamps.

Modules imported per command:

Command Base Head Change
quota should-run 568 551 −17
heartbeat-prompt --thin 353 342 −11
status 414 414 0
  • Modules no longer loaded on should-run:
    • issue_fix: periodic_report, pr_description, reward_memory, repository_snapshot, repository_commit_evidence, metrics_supplement, outcome_projection;
    • domain_packs, domain_state;
    • reward_memory: decision, dogfood, evaluation, evaluation_fixtures, health, memory_utility, utility_reducer.
  • Modules no longer loaded on heartbeat-prompt: reward_memory codex_app_outcome, decision, dogfood, evaluation, evaluation_fixtures, health, memory_utility, outcome_lifecycle, read_authority, runtime_hooks, utility_reducer.

Isolated hook import cost (n=20, alternating order, after importing effect_runtime): 348 ms → 179 ms median. Head was faster in 20/20 pairs, and 227 → 162 loopx modules were loaded.

End-to-end CLI time is not distinguishable from noise at this sample size. Most of the deferred modules are still imported by other paths in the full command.

Command Wall-clock, n=10 (paired median, head faster) CPU time, n=16 (paired median, head faster)
should-run −19 ms (5/10) −65 ms (8/16)
heartbeat-prompt −9 ms (5/10) −19 ms (8/16)
status −1 ms (5/10) —

The expected per-command gain is tens of milliseconds, not the ~110 ms this Todo originally estimated.

Validation at head:

  • pytest on the reward-memory, issue-fix, PR-wait, outbound-guidance and agent-turn-recall capability suites, plus tests/test_cli_entrypoint.py, tests/test_loopx_turn_driver.py, the import-boundary/owner architecture tests and the compact-identifier owner test: 1022 passed.
  • The facade example smokes all passed: examples/reward-memory-*-smoke.py (8), issue-fix-pr-description-builder-smoke.py and openviking-project-peer-provider-smoke.py.
  • loopx canary premerge --from-git-diff --git-diff-base origin/main (standard tier): 6 direct and 9 selected checks, 0 failures, 0 manual holds.

Not covered

  • Linux and Windows were not measured.
  • The end-to-end latency win is small and was not established as statistically distinguishable. The deterministic result is fewer imported modules and lower hook import cost.
  • This changes runtime behavior in loopx/**, so it needs maintainer review and merge authorization. It is not self-merged.

…n demand

PR wait and turn-outcome hooks import one owner submodule each, but the
package facades eagerly imported every workflow (report sources, PR
descriptions, evaluation, dogfood, Codex App staging). Resolve facade
exports through _EXPORTS and module __getattr__ so each export stays
identical to its owner while unselected workflows are not imported.

Signed-off-by: lusendong.6789 <lusendong.6789@bytedance.com>
Co-authored-by: TRAE CLI <traecli@bytedance.com>

@songoow songoow left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewer: model_agent; GPT-6; OpenAI; self_reported.

动机 / Motivation

受影响的是每次唤醒都加载 issue-fix 或 reward-memory 子模块的 CLI 进程。以前导入一个子模块会展开整个包的公开facade;本项把未选择的导出推迟到首次访问。已验证所有公开导出仍解析到原所有者对象,轻量hook导入不再展开未选中的工作流。本项不改变召回、写入、奖励评估策略,也不证明完整任务延迟或token费用下降。

这次判断绑定PR #7749的完整head 52fa115。评价对象是已经存在的实际调用任务与局部可观察结果;重复调用带来的成本或错误恢复路线是本项动机,不能把较小响应或更少导入直接等同于长期任务质量。

改动思路 / Approach

使用有限显式名称映射和模块级 getattr;首次访问导入原模块并缓存原对象,__all__和dir保留发现性。本 PR 的合理边界是两个包facade的导入时机;业务函数和状态所有者保持原位。

沿入口跟踪到原所有者、返回值和失败恢复后,这个实现仍在现有边界上处理问题。无改动会保留原成本或错误指引;另起一套业务规则、持久化状态或通用缓存会增加同步义务,此处没有这类必要性。新增说明和投影应服务原调用方完成任务,不能改变原来的准入、权限、结算或来源完整性要求。

具体改动 / Changes

关键代码讲解

  • getattr:将原公开函数名映射到原模块,未知名称继续抛出AttributeError。 输入与拒绝分支沿用现有调用路径;这一处的返回内容或依赖时机改变,不把局部投影提升为新的业务判定。
  • getattr:按需返回原class/function对象并缓存,不包装或复制业务实现。 输入与拒绝分支沿用现有调用路径;这一处的返回内容或依赖时机改变,不把局部投影提升为新的业务判定。

调用者公开名称、对象身份和业务实现保持;导入时的异常时机有意变为首次真正访问相应导出。正向场景是:分别访问每个公开导出并比较原所有者对象身份。负向场景是:只导入轻量hook时禁止未选择的heavy模块;未知属性保持拒绝。这些是依据实际源码与已有调用测试核对的行为边界;失败和恢复继续由原owner负责,没有以返回字段存在代替真实状态读取。

规范依据为原公开调用行为、既有所有者和相应回归;已检索改动路径与周边文档,没有为这次局部性能/适配修正找到另一个已接受专项RFC。作者的量测叙述不充当独立功能合同。

实际验证 / Validation

3 项 facade/导出身份/导入隔离测试 PASS;所有改变Python源码编译和 diff hygiene PASS。

执行命令:python -m pytest -q tests/test_cli_entrypoint.py -k 'facade or reward_memory or issue_fix'。本轮统计只包含上述实际执行;作者PR内微基准、旧head完整回归和旧review的历史量测都已阅读,但不冒称本轮重新执行的结果。

对主干的风险 / Risks

调用者公开名称、对象身份和业务实现保持;导入时的异常时机有意变为首次真正访问相应导出。最需要防止的情况是:只导入轻量hook时禁止未选择的heavy模块;未知属性保持拒绝。已核对相关调用方和已有review,未重复发布旧head意见;本轮对新增head的源代码与局部回归单独作判断。原持久化状态和业务所有者保持,仍然不能由小范围通过推断整仓或所有部署入口已经合格。

当前精确head失败证据:test-shard (6)、test-shard (3)、test-shard (2)、chat-bundle-browser (1)、dashboard-browser (2)。当前多个test-shard失败日志未成功取得,且chat与dashboard存在实际失败;源码与局部导入测试不足以归因全部当前失败。 未取得的分片日志明确留作未知;同名错误或改动文件不相交都不足以建立pre_existing归因。

最小补齐是针对当前实际失败取得日志,并提供不可变base/head同命令同条件归因,或能独立覆盖该失败不变量的当前真实入口证据。已完成的聚焦回归不应无条件重做,也不要求无关功能重写;如果归因确认是回归,再修相应最小代码路径。未完成的检查不是单凭pending就要求改代码的理由。

我的整体评价 / Verdict

REQUEST_CHANGES:当前阻断是具体的验证证据缺口。聚焦审查尚未发现可复现的阻断性生产代码缺陷,不能把未归因红灯写成代码finding。 审查仅适用于exact head 52fa115。

本 PR 的合理边界是两个包facade的导入时机;业务函数和状态所有者保持原位。 已验证所有公开导出仍解析到原所有者对象,轻量hook导入不再展开未选中的工作流。 已检查 loopx/capabilities/issue_fix/init.py:getattr;loopx/capabilities/reward_memory/init.py:getattr;变更是CLI/内部读取或文本协议,没有修改浏览器页面布局,视觉截图不适用。

当前多个test-shard失败日志未成功取得,且chat与dashboard存在实际失败;源码与局部导入测试不足以归因全部当前失败。 本项不改变召回、写入、奖励评估策略,也不证明完整任务延迟或token费用下降。所有本轮探针使用合成fixture,没有运行真实模型任务或发起业务作业;本review陈述审查与验证事实,不声称已经发布部署或完成合并。

English verdict: REQUEST_CHANGES — exact head 52fa115. Focused code review and the listed local regressions found no confirmed blocking implementation defect. Approval is held for the named current validation gaps; resolve their source-bound evidence rather than changing unrelated code.

This branch has not been deployed

No deployments
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