Skip to content

feat: qualify transferable reward-memory experiences - #4824

Merged
huangruiteng merged 3 commits into
mainfrom
codex/reward-memory-experience-quality
Sep 21, 2026
Merged

huangruiteng merged 3 commits into
mainfrom
codex/reward-memory-experience-quality

Conversation

@huangruiteng

Copy link
Copy Markdown
Collaborator

Reward Memory currently treats exact provider persistence as enough to activate a procedural record. A fact-only summary can therefore survive readback while omitting the actual transferable lesson, and an exact-id query says nothing about whether the lesson can be found at the decision surface.

This change introduces a typed procedural_experience_contract_v0 with applicability, observed outcome, attribution, future behavior, limitations, and evidence references. Procedural candidates are rejected before provider access when that contract is missing or malformed; the normalized contract participates in candidate identity, survives the active envelope, and is projected into Agent recall guidance. Legacy v0 Turn reflections remain audit-compatible but cannot write durable memory.

Activation now separates three claims:

  • exact readback proves persistence;
  • the experience-quality receipt proves structural and evidence-bound qualification;
  • destination business_recall, queried without the candidate id, proves the record is discoverable at its configured surface.

A destination miss remains recall_unverified for bounded reconciliation. Qualified experience is explicitly marked unproven_until_application_evidence; the gate does not claim that one outcome proves utility or alpha.

User entry points:

  • CLI and managed Turn now author turn_reward_memory_reflection_v1 and receive typed quality/destination-recall receipts.
  • Existing frontend capability settings remain schema-driven; this change does not add a user-authored reflection field or a second configuration owner.
  • Lark does not author outcome reflections and continues to consume the shared typed projection, so no separate Lark state or control is needed.

Validation:

  • ruff check on all changed Python and test paths
  • 167 tests across experience quality, outcome lifecycle, Codex App outcome, content seed compatibility, quota settlement, and Turn driver
  • 143 remaining Reward Memory, provider retrieval, agent recall, and pipeline tests
  • 24 Decision Context and content-seed compatibility tests
  • post-rebase focused verification: 22 tests
  • git diff --check

No local Goal state, provider scope, account data, credentials, or private finance material is included. This changes runtime and persisted receipt behavior, so it is intentionally left for maintainer review and is not self-merged.

@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.

Request changes conclusion (author-owned PR; GitHub blocks formal self-review)

评审 head:785033da33e042990cc12b9ba6aefdc1eb5a1720

当前有 1 个 P1 阻塞:升级前已经落盘为 pendingturn_reward_memory_reflection_v0 sidecar,在本 PR 中会被 _reflection 转成 legacy_no_write 后提前返回,但 reconciliation 不会把原 sidecar 写成 terminal 状态。因此同一个文件会在每个后续 Turn 被重新扫描和“尝试”,永远保持 pending。我用真实临时 sidecar 文件连续执行两次 production reconciliation entrypoint,两次都得到 pending_count=1attempted_count=1completed_count=0,文件仍是 status=pending,provider 调用数为 0。已有 legacy 测试只覆盖“新收到 v0 不写”,没有覆盖“升级前已经 pending 的 v0 如何收敛”。

最小修复:在 replay 旧 v0 pending sidecar 时,原子地写入明确的 terminal rejected/quarantined migration receipt;receipt 必须如实保留“旧 provider 写可能已经提交”的不确定性,不能冒称已从 provider 删除。补一个两次 reconciliation 的回归:第一次 terminalize 且 provider 零调用,第二次应为 pending_count=0 / attempted_count=0

动机

这项改动解决的是一个真实且重要的语义缺口:旧实现把“provider 写入并能按精确 id 读回”近似成“形成了可迁移的经验”。但事实摘要可能没有适用条件、因果归因、未来触发动作、验证方式和停止条件;精确 id 读回也不能证明在实际决策 surface 上能够被找到。PR 用 procedural_experience_contract_v0 将适用情境、已观察结果、归因、未来行为、限制和证据引用拆成可检查字段,并把“持久化”“结构/证据资格”“目标 surface 可发现性”“后续真实效用”分开,这是正确的产品方向。

旧行为是:compact summary 经 standing policy 接受并完成 exact readback 后即可 active。新行为是:procedural candidate 必须先通过结构化经验门禁;active envelope 保留完整经验;目标 surface 再以不含 candidate id 的 business_recall 查询并核对 candidate/experience digest;真正价值仍标成 unproven_until_application_evidence。这既避免了把存储成功当成经验质量,也没有把 advisory recall 误写成动作 authority。

但这次同时把 Turn reflection 从 v0 升为 v1,并声明 v0 只保留审计兼容。这个兼容边界不能只覆盖新输入,也必须覆盖升级时已经存在的 pending sidecar。当前缺口会让历史状态永远占据有界 reconciliation 批次,所以整条迁移还不能视为完成。

改动思路

入口和 authority 分层总体合理:

  1. normalize_procedural_experience 是纯、严格、provider-neutral 的结构/规范化 owner;它不靠关键词判断经验价值。
  2. candidate_review 把 experience 纳入 deterministic candidate_ref,并在 provider 访问前运行 quality guard。
  3. ingestion 继续复用 standing policy、active envelope、provider sync 和 exact readback;对 procedural class 再做一次 query_kind=business_recall 的 destination check。
  4. application 只把通过相同质量门禁的 active record 暴露给 recall,agent_turn_recall 将结构化 experience 注入私有推理上下文,仍明确不授予新 authority。
  5. CLI/App Turn outcome 仍要求独立 validator + durable writeback + quota spend,reflection 本身不能自证;未启用 capability、no-evidence 和显式 disable 路径保持零 provider 调用。

最强的“不应该这样发”的反例不是新 v1 happy path,而是 persisted compatibility:reconcile_pending_turn_outcome_ingests 的 authority 是 sidecar lifecycle,任何被扫描的 pending 都必须最终变为 completed/rejected/quarantined,或确实保留一个可重试 provider 条件。现在 parser 的 legacy_no_write 分类没有与这个 state owner 合并,造成“语义上不可再写、状态上却永远可重试”的非法组合。

具体改动

关键代码讲解

  • experience_quality.normalize_procedural_experience:严格要求 exact field set;applicability / limitations 是有界非空列表,future_behavior 必须完整包含 triggeractionvalidationstop_condition,evidence refs 必须唯一且 opaque。canonical JSON digest 被 candidate identity、active envelope 和 recall 使用,避免不同阶段重新解释同一经验。
  • candidate_review.build_reward_memory_candidate / _guard:experience 参与 candidate_ref,procedural class 缺失合同会在 provider 调用前得到 procedural_experience_quality_contract_missing。soft preference / hard policy 不会被强制升级成 procedural experience。
  • ingestion.ingest_reward_memory_candidate:先做 exact resource/candidate/content digest readback;procedural record 再用 applicability + trigger + action 发起 business_recall,并核对 candidate 与 experience digest。失败返回 recall_unverified,而不是伪装成 activated。
  • application._active_itemagent_turn_recall.apply_guidance:recall 端重复验证质量并把结构化 experience 放进私有 guidance;公开 packet 只暴露 quality receipt,不扩大 scope 或动作权限。
  • outcome_lifecycle._reflection / reconcile_pending_turn_outcome_ingests:v1 解析和新 v0 no-write 判定本身清楚,但 v0 early return 发生在 sidecar 加载/更新之前;reconcile 仅凭原文件 status=pending 再次选中它,形成上述永久循环。

文档、content-ops seed import、decision-context producer、Codex host prompt、App candidate staging都同步到同一个 vocabulary,没有再造第二个 lifecycle/store。未来向的小步重构也应继续放在现有 outcome_lifecycle owner 内:增加一个明确的 legacy pending terminal transition 即可,不需要新的 migration framework。

对主干的风险

阻塞路径

触发条件:升级前一次 v0 Turn outcome 已完成独立 validation/writeback/spend,但 provider 返回 commit pending 或 readback unverified,因此 sidecar 持久化为 status=pending

执行路径:下一 Turn 扫到 sidecar -> serialize v0 reflection -> _reflection 返回 legacy_no_write -> run_configured_turn_outcome_ingest 返回 no_eligible_evidence -> reconciliation 只把 receipt 放进本次返回值,不写原文件 -> 下一 Turn 再扫描同一文件。

错误结果:这是无 provider 调用、无可变化外部条件的永久 retry;多个旧 sidecar 可以占满 pending[:limit],让真正的 committed_pending / recall_unverified 新记录长期得不到 reconciliation。

最小修复与验证:写入 terminal migration receipt,并用真实临时文件运行两遍 production entrypoint,第二遍必须不再尝试;同时继续运行现有 outcome lifecycle、experience quality、App outcome 和 Turn driver suites。不要把旧 provider 对象“可能已提交”误报成删除或完成质量资格。

语义与 CI 对齐

这是一个具体的 persisted-contract migration 违例,不是 CI 是否绿色的问题。当前 exact head 的 187 个 focused tests 全部通过(528.35s),Ruff、git diff --check 和 current-main merge-tree 都通过;这些结果证明新 v1 路径和大多数负路径扎实,也恰好证明现有测试矩阵没有覆盖历史 pending state。Goal 的 capability 配置是 wait_for_ci=false,本次 verdict 没有等待或轮询 CI。

其余 residual risk 是 live OpenViking 的 semantic ranking:deterministic provider tests能证明控制流和 digest 约束,不能证明真实检索排序质量。这个风险适合 rollout observation,不应替代当前可稳定复现的 sidecar liveness blocker。

我的整体评价

架构方向、authority 边界、typed contract 和测试投入都很好,改动也复用了正确的现有 owner;1137 行虽然不小,但大部分是 focused tests 与双语协议说明,机制本身没有增加第二套 provider、scheduler、store 或权限系统。默认关闭/显式禁用路径仍隔离,前端和 Lark 不需要新状态 owner 的理由也成立。

当前不能批准的唯一原因是 schema 迁移没有覆盖已持久化的 pending v0 状态。它是运行时会反复发生的 replay/liveness 问题,而且会阻塞更晚的真正 retry 项;不是可以留作 post-merge follow-up 的文档瑕疵。修复并补两遍 reconciliation 回归后,请在新 head 上重新执行完整 exact-head review。

English verdict: REQUEST_CHANGES - head 785033d strands pre-upgrade pending v0 sidecars in perpetual reconciliation; terminalize/quarantine them truthfully and add a two-pass replay regression. Existing focused validation: 187 passed, Ruff and diff check passed, and the independent persisted-sidecar counterexample reproduced twice.

Comment thread loopx/capabilities/reward_memory/outcome_lifecycle.py
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
@huangruiteng
huangruiteng force-pushed the codex/reward-memory-experience-quality branch from 785033d to a5df1e2 Compare September 21, 2026 04:53

@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:a5df1e268b0dcdc391630c823df197dd1a96ef33

动机

这项改动补上了 Reward Memory 的核心语义缺口:旧实现把“provider 已持久化且能按精确 id 读回”近似成“得到可迁移经验”,但事实摘要可能没有适用条件、归因、未来动作、验证方式和停止条件,精确 id 读回也不能证明它能在真实决策 surface 被找到。

本 PR 用 procedural_experience_contract_v0 将适用情境、已观察结果、归因、未来行为、限制和证据引用变成可验证字段;把结构资格、持久化、目标 surface 可发现性和后续真实效用拆成不同证据。它仍把价值标成 unproven_until_application_evidence,也明确不新增动作权限,因此解决的是“经验能否被可靠存取和召回”,不是声称已经证明效用或 alpha。

初审发现的升级迁移缺口也已在新 head 修复:升级前已经落盘为 pending 的 v0 sidecar 不再每个 Turn 永久重试,而会在不调用 provider 的前提下原子收敛为 terminal rejected,同时诚实保留“历史 provider write 可能已经提交”的不确定性。

改动思路

整体继续复用现有 owner,没有建立第二套状态或执行系统:

  1. experience_quality 只负责 provider-neutral 的结构化规范化、digest 和资格 receipt;
  2. candidate_review 把 experience 纳入确定性 candidate identity,并在 provider 访问前执行质量门禁;
  3. ingestion 继续拥有 standing policy、provider sync、exact readback,并额外通过不含 candidate id 的 business_recall 验证目标 surface 可发现性;
  4. applicationagent_turn_recall 复用同一质量合同,把结构化经验作为私有 advisory guidance,而不是动作 authority;
  5. outcome_lifecycle 继续独占 persisted sidecar 的 retry/terminal transition,v0 只读兼容和迁移不会另建 migration runtime。

最强反例是历史 persisted state:如果旧 v0 已经 pending,但 parser 只返回 legacy_no_write 而不更新 sidecar,它会永远占据有界 reconciliation 批次。新 helper 在正常 provider retry 之前识别这一状态,原子写成 rejected,receipt 标记 provider_write_state=unknown_may_have_committedprovider_cleanup_performed=false;第二次扫描不再看到它。

具体改动

关键代码讲解

  • experience_quality.normalize_procedural_experienceexperience_quality.py:87):严格验证 exact field set、1–5 项 applicability/limitations、完整的 trigger/action/validation/stop_condition 和唯一 opaque evidence refs;canonical JSON digest 供 candidate、active envelope 和 recall 共享。
  • ingestion.ingest_reward_memory_candidateingestion.py:299):quality guard 未通过时在 provider 构造前返回;通过后依次验证 sync、exact readback 和目标 surface business_recall,destination miss 保持 recall_unverified,不伪装成 active。
  • _terminalize_legacy_pending_sidecaroutcome_lifecycle.py:176):只处理真正解析为 legacy_no_write 的旧 pending sidecar,复用现有原子 sidecar writer,零 provider 调用且不冒称清理了历史对象。
  • reconcile_pending_turn_outcome_ingestsoutcome_lifecycle.py:680):pending 扫描保留 path/value,先终结 legacy,再让仍可重试的 v1 进入原有 provider reconciliation;同一模块仍是唯一 failure/retry owner。

CLI/managed Turn 已切到 v1 typed reflection;现有 schema-driven 前端配置和 Lark shared projection 不需要第二个配置 owner。双语 README、content seed、Decision Context producer、Codex App staging、Turn driver 和 recall 消费方都对齐同一 vocabulary。

对主干的风险

没有剩余阻塞 finding。

初审 P1 的真实历史 sidecar 复现已经变成 production-entrypoint 两遍回归:第一遍把 v0 pending 终结为 rejected,provider sync/retrieve 都是 0;第二遍 pending_count=0attempted_count=0。这证明修复覆盖的是落盘迁移状态,不只是“新收到 v0 不写”的容易路径。

验证结果:

  • 受影响的 Codex App outcome、experience quality、outcome lifecycle、content seed、quota settlement 和 Turn driver:168 passed
  • outcome lifecycle 单文件:11 passed
  • Ruff、changed-file py_compilegit diff --check、semantic vocabulary drift 和 public/private boundary 均通过;
  • standard premerge 选择 19 项,其中 18 项通过。唯一红项 cli-output-budget-regression-smoke.py 在相同环境下对最新 origin/main 和本 head 都是 798 > 750,属于当前主干已有输出预算漂移,#4824 没有修改该路径或放大结果。

仍需 rollout 观察的是 live OpenViking 的语义排序质量及后续真实 application utility;fake provider 只能证明控制流、digest 和无副作用边界。PR 已正确避免把这些未验证维度声明为成功。

语义与 CI 对齐

这次扩展的是现有 reward-memory/Turn outcome vocabulary,而不是创建并行协议:v1 新写、v0 audit-only、legacy pending 有明确 terminal migration receipt。semantic-vocabulary-drift-smoke.py 在补齐仓库 Node 依赖后通过。Goal 配置是 wait_for_ci=false,本评审使用仓库原生本地证据,没有等待远端 CI。

我的整体评价

全量 exact-head 复审结论为 APPROVE。方向、authority 边界、Goal/Agent scope 和默认关闭隔离都成立;虽然 22 文件、+1286/-92 不小,但 +691 是 focused tests,文档/seed 约 66 行,生产机制仍集中在既有 qualification、ingestion、application 和 lifecycle owner,没有新增 daemon、datastore、scheduler、provider 或 UI source of truth。

初审唯一 blocker 已用最小且正确的 lifecycle 修复闭环,且没有用“删除 provider 对象”这种无法证明的结论掩盖不确定性。现在剩余风险是部署后质量观测,不是合并前的代码正确性缺口。

English verdict: APPROVE - head a5df1e2 closes the persisted legacy-sidecar liveness defect with a truthful terminal migration, preserves authority boundaries, and passes the affected exact-head validation; the sole standard-canary failure is an identical current-main CLI output-budget baseline drift.

@huangruiteng
huangruiteng merged commit 65afc48 into main Sep 21, 2026
20 of 25 checks passed
@huangruiteng
huangruiteng deleted the codex/reward-memory-experience-quality branch September 21, 2026 05:05
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