Skip to content

fix(reward-memory): share surface checkpoints and typed input diagnostics - #5154

Merged
huangruiteng merged 5 commits into
mainfrom
codex/reward-memory-surface-checkpoint-20260927
Sep 27, 2026
Merged

huangruiteng merged 5 commits into
mainfrom
codex/reward-memory-surface-checkpoint-20260927

Conversation

@huangruiteng

@huangruiteng huangruiteng commented Sep 26, 2026 •

Copy link
Copy Markdown
Collaborator

Summary / 摘要

Repair a reusable caller boundary in the existing Reward Memory capability:

  • Export build_reward_memory_surface_read_authority_checkpoints for the actual configured consumer surface. The original config owner selects its exact corpora; TS assembles scope and original caller-supplied proof. The Turn wrapper delegates to the same projection instead of maintaining its own checkpoint builder.
  • Preserve strict SDK validation and ValueError compatibility. Add safe, typed diagnostics distinguishing invalid age/freshness input from missing/invalid checkpoint input, while retaining exact_corpus_request_invalid and zero provider calls on these failures.
  • Handle pre-provider checkpoint transport failures in the explicit CLI with safe runtime_unavailable feedback and exit code 2; retain managed fail-open and same-Turn recovery without writing a false successful receipt.
  • Keep defaults, enablement, source authority, provider routing, private scope, call caps and application/utility separation unchanged. No age coercion, inferred read proof, automatic permission repair or new memory store.

在原能力内补足通用 surface checkpoint 和输入诊断。TS 持有共享组装/诊断投影;Python 保留原配置/provider 适配及原 SDK 校验,不新增平行的准入规则。False 不变成 True,checkpoint 生成不等于读权限已核验,非法参数不调用 provider。

Validation / 验证

  • uv run --extra test python -m pytest -q tests/capabilities/test_reward_memory*.py tests/capabilities/test_agent_turn_recall.py tests/test_reward_memory_pipeline.py tests/capabilities/test_capability_configuration_ui.py: 252 passed, plus 56 passed in existing outbound guidance regressions.
  • node --experimental-strip-types --test tests/control_plane_ts/reward_memory_decision.test.ts: 7 passed; strict unknown-code rejection and exact-scope assembly.
  • npm run typecheck:control-plane, focused Ruff, repository-declared mypy (20 source files) and new adapter mypy: passed.
  • Expanded strict mypy on legacy imported modules reports 20 errors, identical after normalizing shifted line numbers at immutable base 96a3b90f41094bd2ddea7263b9c7ee37371a9c3b and candidate; this is not a passing full legacy type check. No unrelated fixes included.
  • Same public fixture through real config loading, SDK/applier and TS transport at immutable base/candidate: disabled, preview, context delivery + ignored assessment + exact replay, wrong-surface proof, invalid age and missing checkpoint retain identical semantic output and provider counts. Turn checkpoint digest is identical. Only the declared typed failure detail is added.
  • The diagnostic oracle fails on the historical baseline (invalid_age lacks detail); the six initial new regression cases also failed before implementation. Candidate passes. No live model/provider qualification is claimed.
  • Explicit helper cost with an already-warm production TS transport, 20 warm samples: baseline median ~0.001ms, exact-head candidate ~9.85ms/max ~13.53ms. This is a bounded added projection cost, not an overall quota/recovery latency improvement.
  • Exact-head public boundary scan: 13 files clean, no credentials/private evidence. Existing warnings concern unrelated Goals, not this branch.
  • Exact committed head 4caf96f2728d3f3762b6f6756068c9dec199a6a4: 10 catalog + 8 risk + 1 boundary checks passed. Broad TS suite at the TS-identical predecessor: 3166 passed, 30 PostgreSQL-environment tests skipped; TS sources/fixtures/manifest diff is empty at final head. This does not qualify the skipped real-service cases.
  • CLI checkpoint failure regression failed before self-review refinement, then passed with safe zero-call feedback; managed recovery and newly-added unbound corpus isolation passed. No post-provider zero-call claim is fabricated.

Product boundary / 产品边界

CLI/managed callers receive the reusable SDK helper and safe detail; source-level SDK/Turn/config-editor contracts were exercised. No configuration fields change: Dashboard still edits config_path/enabled_agents through its existing owner and roundtrip, so no companion control or rebuilt frontend is needed for this slice. Lark remains status-only; universal frontend/Lark semantic-consumption delivery is not complete.

This is an independently reviewable slice, not completion of the broader memory lifecycle. A caller must retain the complete private result; saving only context/public packet/application receipt does not support assessment after EOF/restart. There is no supported cross-process restore API yet. No re-query or fabricated semantic completion is allowed. The bilingual reference makes this limitation explicit.

配置与前端控件未改;没有声称打包 UI/Lark 已新增消费链路。跨进程私有 result 的安全恢复、统一展示和真实效果仍是已有任务的后续缺口。未改变 Goal provider、验收、交易权限或默认自动化;测试/PR 数量不算研究效果。

Signed commits separate initial runtime/API and tests/docs; a third signed self-review refinement fixes the narrow CLI failure boundary with regression tests. All outgoing author/committer noreply identities verified. Ignored local comparison harnesses and raw validation logs are excluded. Maintainer merge required for this runtime/API change; no self-merge or unmerged-source installation.

Maintainer repair at the merged head

The reviewed head a87508553dfba995437151c213d5f496b6666b99 was reached by merging origin/main (af3e7f1f0) into the branch and refreshing the checked-in registry I/O census.

  • Conflicts (3) were all "both sides added at the same place" against main's 46621c4be (fix(reward-memory): preserve verified context delivery across assessment #5158, verified delivery across assessment): loopx/control_plane/capabilities/reward_memory_decision.ts (main's boundReceiptDigests vs this PR's buildRewardMemorySurfaceReadCheckpoints), tests/control_plane_ts/reward_memory_decision.test.ts (main's three delivery-receipt cases vs this PR's two checkpoint/detail cases), and docs/reference/reward-memory-decision-consumption.md (both new paragraphs, English and Chinese). All three kept both sides; the TS merge also needed the PR test's closing }); restored.
  • loopx/semantics/project_registry_io_manifest_v1.json: the new read_authority import moved _goal_repo's load_registry from line 40 to 41, so test_project_registry_io_census was red on this branch. Regenerated with the owning generator (one line).
  • Validation at the merged head: npm run typecheck:control-plane; npm run test:control-plane (3264 tests, 3234 passed, 30 environment-gated skips, 0 failures); the reward-memory/agent-turn-recall Python set (256 passed); the three architecture suites (125 passed); targeted Ruff; git diff --check; and loopx check over all 14 changed paths ("public boundary scan clean: 14 files").
  • loopx canary premerge --from-git-diff: catalog canaries 10/10, direct checks and public boundary passed; the only red is examples/canary/catalog-run-e2e-smoke.py, whose selected examples/control_plane/bounded-context-namespace-smoke.py needs ~56-59s on this machine against the canary's 60s per-check cap. That smoke passes standalone (59.4s) and guards the legacy loopx.capabilities shims, which are not in this PR's 14 files. Recorded as an environment-timeout hold, not a code failure.

Merged with admin bypass because the protect main ruleset requires require_last_push_approval and dismiss_stale_reviews_on_push; the maintainer main-integration push made the maintainer the last pusher. The exact-head review is published at #5154 (review).

…agnostics

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
…n diagnostics

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
…failure

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>

@huangruiteng huangruiteng left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Approval conclusion (author-owned PR; GitHub blocks formal self-approval)

精确评审 head:4caf96f2728d3f3762b6f6756068c9dec199a6a4;不可变对照:96a3b90f41094bd2ddea7263b9c7ee37371a9c3b。评审范围为全部 13 个文件,而非只看新增 helper。现有 PR 评论/评审为空;以下结论以源码、原契约和本地执行为依据,不代替维护者合并授权。

动机

这是让既有记忆消费调用方能够正确接入的有界增量。原 Turn 的 checkpoint 构建只适用于 Turn surface,其他消费入口不能直接借用;非法年龄和缺失读授权又被压成同一个宽错误,调用方难以纠正。改后复用实际 surface 的原配置,并提供安全、可行动的细分诊断。并未把上下文注入算作语义消费完成,也没有把测试通过算作真实效果。

改动思路

沿用原配置所有者选择 corpus,Python 只把必要范围引用与原始读权限证明传入既有 TS effect runtime,由 TS 组装 checkpoint;原 Turn wrapper 改为调用这个共享投影。范围、时效、冲突和 provider 准入仍由原 SDK 执行,本次没有在 TS 复制一套校验规则,也不另建存储或开关。

正向路径是原配置读回、实际读权限证明、共享 checkpoint、既有 SDK/provider、绑定当前产物的判断、原结果复用。负向路径覆盖 False 证明、错 surface、非法 age、缺 checkpoint 和 TS 启动失败,保留基线和真实调用次数。新增 corpus 不会因属于同一配置而自动加入既有 surface。outbound 的独立 actor/目的地/advisory 约束仍保留,未把这个 helper 当作发消息授权。

具体改动

全 diff 为生产代码 162 增/47 删,测试 226 增/1 删,双语文档 62 增/2 删;合计 450 增/50 删。无生成产物或纯机械搬移。Python 输入错误包装保留 ValueError 兼容及原校验顺序;hook 只把类型码传给 decision,TS 只在原 exact-corpus 拒绝分支输出四种白名单 detail。导出与 handler 注册让共享投影进入真实调用路径,而不是测试专用接口。

关键代码讲解

  1. buildRewardMemorySurfaceReadCheckpoints(TS,第 32 行):只组装明确传入的 corpus/五类身份范围及原 source_ref;严格检查布尔值、紧凑引用和重复 corpus,不自行证明权限。False 原样保留。
  2. build_reward_memory_surface_read_authority_checkpoints(Python adapter,第 11 行):复用原配置 route,只发紧凑范围到 TS。Turn 的旧局部构建代码被删除,保留原 registry 来源;显式 SDK 消费者可指定自己的实际 surface。
  3. RewardMemoryRecallInputError(application,第 107 行)及 hook 捕获:把原输入拒绝转成四个类型码,不匹配异常正文;不放宽年龄、revision 或读权限门禁。
  4. projectRewardMemoryDecision(TS,detail 分支第 111 行):未知 detail 或把 detail 挂在成功分支均拒绝,正常 packet 不添加该字段,不泄漏异常内容。
  5. handle_agent_turn_recall_command(CLI,第 248 行):自审发现并修复构建失败直接逃逸的问题。只有 provider 调用前的 TS 失败才返回零调用、安全 packet 和退出码 2;managed Turn 沿用 fail-open,恢复后沿原 Turn 重试且无失败的成功回执。

双语文档明确年龄的合法输入、读权限证明责任、失败恢复以及必须保留完整私有 result;仅保存 public packet/context 不支持重启后 assessment。配置字段未变,现有 Dashboard config_path/enabled_agents 编辑与 roundtrip 被复用;无需新增控件或打包 frontend,本次也未声称新 UI/Lark 消费链路已完成。

对主干的风险

最大新增风险是一次 TS 投影调用及其故障面。最初只有普通正例可能掩盖 CLI 异常逃逸;新故障测试先失败后修正,目前证明 provider 未调用、未写成功回执、无私有错误泄漏,并可在 transport 恢复后取得有界结果。关闭/未配置路径仍零 TS/provider 调用,无新 packet、引导或 quota 义务。

同一公开 fixture 通过真实配置加载、SDK、应用回调及生产 TS transport,在 baseline/head 下比较 disabled、preview、交付后 ignored 判断及精确复用、错 surface、非法 age、缺 checkpoint。除预期新增 detail 外,输出/调用次数的语义摘要均为 c2e095c71024…,Turn checkpoint 摘要均为 f6da1b822d0a…。同一诊断 oracle 在旧主干失败,新 head 通过;没有用绿色测试代替历史对照。

暖 TS transport 的 20 个样本新增投影中位数约 9.85ms、最大 13.53ms,原局部构建约 0.001ms。这是已披露的 bounded cost,依据 TS-first/单一共享投影方向接受,不是 quota 或恢复总延迟优化证明;没有全程 cold/soak 或真实 provider 性能认证。

语义与 CI 对齐

这是既有诊断词汇的有界扩展及原 checkpoint 构建的共享化,符合 TS RFC 的替换优先/单一权威边界;没有新增调度义务、持久化版本分支或预算放宽。本 Goal 的评审配置 wait_for_ci=false,因此没有查询或等待远端 CI。

精确 head:252 项记忆/Turn/配置测试、56 项 outbound 回归、7 项 TS 专项、TS typecheck、Ruff、仓库声明的 mypy 20 个文件及新 adapter mypy 通过;精确 head canary 的 10 项 catalog、8 项 risk、1 项 public-boundary 共 19 项全部通过。广义 TS 套件在 TS 代码完全相同的上一提交通过 3166 项,30 项 PostgreSQL 环境用例跳过,非全库实机资格。扩展 legacy mypy 仍有 20 条错误:不可变基线和当前 head 用同一命令,规范化路径及错误正文摘要完全相同(902454fe86dd…);没有宣称该检查通过或让本 PR 承担无关修复。

我的整体评价

APPROVE,作为完整可用的 caller 修复切片,不代表父目标或整个记忆生命周期完成。长程推进接受已测得的有界 TS 成本,同时保留基线、同 Turn 复用、可恢复失败和原权限;用户体验改善为准确定位输入与安全 CLI 反馈。没有未解决的阻断发现。

可保留原 v0 packet/回执:真实 Turn、SDK 和既有私有结果读取仍依赖它,当前 Python/TS 同包部署,无并行 request decoder;后续迁移应在原消费者和持久化义务退役后处理。公共跨进程恢复、统一 frontend/Lark 展示、实际决策效用及其它控制面超时仍在本 PR 之外。该 runtime/API 改动等待维护者合并,不自合并,也不安装未合入源码。

English verdict: APPROVE - 4caf96f; shared exact-surface projection and typed diagnostics preserve authority and replay, with safe pre-provider CLI failure; scoped regressions and baseline comparison passed. Public restore/UI-Lark completion and live-provider utility remain out of scope.

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Delegating reward_memory_turn_read_authority_checkpoints to the shared
read_authority adapter added an import line above _goal_repo, moving its
load_registry call from line 40 to 41. Regenerate the checked-in census with
the owning generator so test_project_registry_io_census stays current on the
pull-request path.

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>

@huangruiteng huangruiteng left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Approval conclusion (author-owned PR; GitHub blocks formal self-approval)

PR #5154 复审 — exact head a87508553dfba995437151c213d5f496b6666b99

动机

reward-memory 的 surface read checkpoint 此前有两处实现:Turn 包装层(agent_turn_recall/runtime.py)自己拼一份,被配置化的消费方(reward_memory.read_authority)另有一份等价规则,二者日后会各自演化。与此同时,SDK 的输入拒绝只抛出笼统的 ValueError,调用方无法区分"age/freshness 输入非法"与"checkpoint 缺失或非法";显式 CLI 在 provider 之前遇到 runtime 传输失败时也没有安全反馈,容易留下"看起来成功"的缺口。本 PR 把 checkpoint 组装与诊断收敛成一个共享投影,并在 provider 之前失败时给出真实的安全反馈。默认值、enablement、source authority、provider 路由、私有范围、调用上限与 application/utility 分离都不变,也不新增平行的准入规则。

改动思路

第一步是把"谁拥有 checkpoint 规则"收敛到一处:TypeScript 的 buildRewardMemorySurfaceReadCheckpoints 只组装调用方提供的原文证明(verified、source_ref、scope 与 corpus 身份),从不验证或授予权限;Python 新增 read_authority 适配器从既有配置所有者 resolve_reward_memory_surface_config 取出该 surface 的 corpora 后交给该投影,Turn 包装函数退化成一行委托,删掉了自己那份重复实现。第二步是让输入诊断变类型化:引入 RewardMemoryRecallInputError(ValueError) 与 allowlist 的 Literal 码,把 checkpoint 缺失/非法、age 非法、freshness context 非法区分开;因为它是 ValueError 子类,既有 except ValueError 的 fail-open 边界语义不变,但 hook 现在先捕获它并把码作为 boundary_detail_code 上报。第三步是给诊断加边界:TS 侧只接受 allowlist 值,且必须同时满足 guard_rejected 与 exact_corpus_request_invalid,否则直接拒绝,避免诊断退化成任意文本或成功凭据。第四步是 CLI:把读 checkpoint 的调用单独包起来,在 provider 之前遇到 runtime 失败时输出安全 packet 并返回退出码 2,与既有"ok 为真则 0,否则 2"的契约一致。

具体改动

loopx/capabilities/reward_memory/read_authority.py 是新增的适配器:从配置取该 surface 的 corpora,调用 effect runtime handler reward_memory.read_authority.surface_checkpoints,返回 checkpoints;注释与实现都明确它不调用 provider、不选策略源、不验证或扩大调用方权限。loopx/control_plane/capabilities/reward_memory_decision.ts 新增导出 buildRewardMemorySurfaceReadCheckpoints(严格 token/boolean 校验、corpus 去重、scope 字段可选透传),并新增 BOUNDARY_DETAILS allowlist 与 boundary_detail_code 的使用约束;effect_runtime_handlers.ts 注册该 handler。loopx/capabilities/agent_turn_recall/runtime.py 让 reward_memory_turn_read_authority_checkpoints 委托共享适配器(保留 verified=True 与 registry:<goal_id>:reward-memory),删除了原先的重复拼装。loopx/capabilities/reward_memory/application.py 新增 typed RewardMemoryRecallInputError,把 _authority_checkpoint 拆出 _normalize_authority_checkpoint 并把缺失/非法分别映射为 read_authority_checkpoint_missing/_invalid,把 freshness 的 age 与 context 非法映射为 freshness_age_invalid/freshness_context_invalid。runtime_hooks.py 先捕获 typed 错误并返回 guard_rejected + boundary_detail_code(保留 attempts 与 telemetry),其余异常仍走原有宽捕获。decision.py 让 telemetry 透出 boundary_detail_code。agent_turn_recall/cli.py 在 provider 之前捕获 RuntimeError,输出 status=runtime_unavailable、reason_code=automatic_recall_runtime_failed、provider_call_count=0、suppress_external_sinks=True 并以 2 退出。文档新增"必须保留完整私有 result"与"交付回执与语义回执相互独立"两段。合并 main 时三处冲突(reward_memory_decision.ts、其 TS 测试、reference 文档)都按"两侧都保留"解决:main 的 boundReceiptDigests/交付回执用例与本 PR 的 surface checkpoint 构建器/诊断用例共存。此外本 PR 新增的 import 把 runtime.py 中 _goal_repo 的 load_registry 从第 40 行推到第 41 行,已用所有者生成器刷新 project_registry_io_manifest_v1.json(这是本 PR 自引入的 census 漂移)。

对主干的风险

主风险是诊断变成泄漏或伪成功:boundary_detail_code 受 allowlist 限制且必须绑定 guard_rejected + exact_corpus_request_invalid,测试断言私有异常文本不会出现在结果里("private runtime" not in json.dumps(result)),拒绝路径的 decision_consumption_complete 保持 false、provider_call_count 保持 0。第二个风险是 ValueError 兼容性:新异常是其子类,既有 fail-open 捕获仍然生效,hook 只是把处理提前以获得 typed detail,没有改变"拒绝即不调用 provider"的语义。第三个风险是 CLI 退出码与回执语义:新分支只在 provider 之前触发,0/2 与既有契约一致,managed 路径保持 fail-open 与同 Turn 恢复(有测试覆盖失败后恢复并写出同 Turn 回执)。合并侧的风险是丢失 main 的 #5158 交付回执逻辑,已核对合并结果同时保留两侧实现与用例。需要记录的环境观察有两条:loopx canary premerge --from-git-diff 的唯一失败项是 examples/canary/catalog-run-e2e-smoke.py,它内部选中的 examples/control_plane/bounded-context-namespace-smoke.py 在本机需要约 56 至 59 秒,而 canary 单项上限是 60 秒,单独运行该 smoke 通过(59.4 秒),且它校验的 loopx.capabilities 历史 shim 不在本 PR 的 14 文件内;主仓库本地还残留一个未跟踪的 loopx/capabilities/cross_runtime/ 目录,会让同一 smoke 在本机快速失败,这是本机工作树残留而不是 main 的缺陷,PR 的干净工作树与 premerge 的 public boundary 扫描(14 文件)都通过。

我的整体评价

这是一次收敛重复、加类型化诊断并修正 CLI 边界的小切片,方向正确:它删掉 Turn 包装层的重复 checkpoint 规则而不是再引入一层抽象,把诊断限制在 allowlist 并要求绑定拒绝上下文,在 provider 之前失败时返回真实的安全回执而不是伪成功,同时保留了 ValueError 兼容与 managed fail-open。我在该 head 上复验了 TS 定向用例 10/10、npm run typecheck:control-plane、全量 control-plane TS 3264 项 0 失败、Python 相关 13 个文件 256 项、架构 125 项、定向 Ruff、git diff --check 与 public boundary 14 文件;premerge 的唯一红项已按上面的环境原因归因。建议合入。

English verdict: APPROVE - a875085 consolidates the duplicated reward-memory surface checkpoint rule into one TS-owned projection with a Python adapter, adds an allowlisted typed input-diagnostic path that preserves ValueError compatibility, and fails the explicit CLI safely before any provider call; TS 10/10 plus 3264-suite, 256 Python, 125 architecture checks and the 14-file public boundary scan pass, with the single premerge red attributed to a local 60s canary timeout.

@huangruiteng
huangruiteng merged commit 70b3cca into main Sep 27, 2026
9 of 10 checks passed
@huangruiteng
huangruiteng deleted the codex/reward-memory-surface-checkpoint-20260927 branch September 27, 2026 04:39
@huangruiteng

huangruiteng commented Sep 27, 2026 •

Copy link
Copy Markdown
Collaborator Author

Merged as 70b3cca010cd8ab69ca93a0b87cf478ce873ac06 (second parent = reviewed head a87508553dfba995437151c213d5f496b6666b99).

  • Exact-head review: fix(reward-memory): share surface checkpoints and typed input diagnostics #5154 (review) (approval conclusion; author-owned PR).
  • Conflicts (3) with main's fix(reward-memory): preserve verified context delivery across assessment #5158 resolved by keeping both sides: the TS module now carries main's boundReceiptDigests and this PR's buildRewardMemorySurfaceReadCheckpoints; the TS test file and the bilingual reference doc keep both sides' additions.
  • Repaired a self-introduced census drift: the new read_authority import moved _goal_repo's load_registry reference, so project_registry_io_manifest_v1.json was regenerated with the owning generator.
  • Validation at the merged head: typecheck; 3264-test control-plane suite (0 failures); 256 reward-memory/agent-turn-recall Python tests; 125 architecture tests; Ruff; git diff --check; loopx check over 14 changed paths (clean).
  • Premerge: catalog canaries 10/10, direct checks and public boundary passed. The single red is an environment timeout in catalog-run-e2e-smoke -> bounded-context-namespace-smoke (~57s vs the canary's 60s cap); it passes standalone and guards legacy loopx.capabilities shims outside this PR's file set.
  • Admin bypass was required by the protect main ruleset's require_last_push_approval after the maintainer main-integration push.

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.

1 participant