Skip to content

feat(finance): add a provider-neutral confirmation gate for venue executors - #6731

Merged
loopx-agent merged 2 commits into
mainfrom
codex/finance-confirmation-gate
Oct 17, 2026
Merged

loopx-agent merged 2 commits into
mainfrom
codex/finance-confirmation-gate

Conversation

@loopx-agent

Copy link
Copy Markdown
Collaborator

Why

The Core typed-action store owns the confirmation receipt, the single execution claim and the canonical envelope. A venue executor still has to prove that the effect it is about to perform is the effect the operator approved, and today every adapter re-implements that comparison ad hoc. The dangerous shortcut is to accept a matching confirmation_digest as proof of confirmation — that digest exists before the human clicks.

What

verify_bound_confirmation(proposal, plan, expected_provider='lark', now=None) in packages/loopx-finance-execution, returning {allowed, refusals, evidence}:

  • confirmation receipt exists, provider matches, principal matches the plan's authorized principal;
  • the receipt's message_id is this operation's own card (compared with the recorded delivery message id);
  • record status is applied/applying and lifecycle is claimed/consumed/outcome_observed;
  • a one-shot claim exists;
  • the confirmed payload terms (bound_order) canonical-equal the frozen plan terms;
  • neither the plan nor the payload expiry has passed.

Refusals are typed codes (confirmation_missing, confirmation_card_mismatch, terms_mismatch, expired, …) instead of exceptions, so a caller can report an honest refusal instead of retrying or working around it.

Boundaries

Pure function over records already loaded from the canonical store: no IO, no credentials, no venue call, no state mutation. It grants no execution authority; the Core store remains the only confirmation/claim owner.

Verification

/Users/huangruiteng/Developer/loopx/.venv/bin/python -m pytest tests/extensions/test_finance_confirmation_gate.py -q
10 passed

Coverage is synthetic: admitted confirmation, missing receipt, wrong channel, wrong principal, receipt from another card, cancelled operation, missing claim, missing payload terms, term drift and expiry.

…cutors

The Core typed-action store owns the confirmation receipt, the single
execution claim and the canonical envelope, but a venue executor still has to
prove that the effect it is about to perform is the effect the operator
approved. Without a typed gate every adapter re-implements that comparison ad
hoc, and the easy mistake is to treat a matching digest - which exists before
the human clicks - as proof of confirmation.

verify_bound_confirmation() checks a canonical proposal against a frozen plan:
the confirmation receipt must exist, come from the expected provider and the
authorized principal, and carry the message id of this operation's own card;
the record and lifecycle must still be live; a one-shot claim must exist; the
confirmed payload terms must equal the frozen plan terms; and the operation
must not be expired. Refusals are typed codes instead of exceptions so a caller
can report them honestly instead of retrying or working around them.

Coverage uses synthetic fixtures only. The module performs no IO, reads no
credentials, talks to no venue and grants no execution authority.

Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>

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

Reviewer: model_agent; gpt-6.1-sol; OpenAI; runtime_reported; reasoning_effort=max

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

动机

准备接入金融执行器的开发者,需要确认记录确实绑定原卡、一次性 claim 和未过期的原始条款。

预期是合法确认放行、缺失绑定或过期拒绝;实际真实 Core 记录在原始 envelope 过期后仍得到 allowed=true。

当前 head 提供纯读取比较函数,合法确认可通过,但过期、缺卡绑定和空 claim 负例仍被放行,不能视为已交付可靠确认门。

本次不验证真实账户、真人回调、下单、资金、风险预留或金融收益,不把 helper 的 allowed 当作 Core 执行授权。

改动思路

REQUEST_CHANGES。把条款校验放在独立 finance provider、让 Core 继续持有确认和原子消费的方向成立,但当前“放行”结果强于实现真正验证的事实。纯函数、没有 IO 和全部测试通过,都不能弥补负例被接受。

金融条款比较留在可选 finance provider;确认、claim、消费和期限的事实继续由既有 Core owner 提供,不应复制一套授权状态机。

当前 PR 只能作为尚待消费的纯校验前置;先修复拒绝边界并明确实际 executor 接入,不把它算作已完成的交易链路。

下表是源码判断说明,执行证据另列;没有用合成 receipt 代替真人点击。

条件 应有结果 当前实测
合法的原始确认与准确条款 补充校验通过,仍需 Core 消费 通过
原始 envelope 已过期,payload较晚过期 拒绝新执行 allowed=true
payload 已过期但 plan 给更晚时间 拒绝延长原批准 allowed=true
原卡绑定缺失或 claim 是空对象 证据不足,拒绝 allowed=true

较小方案是复用 Core 已验证的确认/消费事实,再做金融冻结条款比较;不需要 provider 自己维护一套“哪些 lifecycle 可以执行”的真相。现有 Core 无法解释金融参数,故领域比较有价值;这并不授权它减弱原始期限、消息绑定或 claim 要求。相邻边界的这次简化应随修复考虑,不能通过更多布尔开关绕过。

具体改动

完整 head f638fb4a1ce6dca9fee17d060ab393b88c544f03,base f9bdae67596c185cebc81dc555dd6a3e0242e9c5,全部2条路径、+332/-0:新 confirmation_gate.py179行,以及新测试153行。

独立规范先读 docs/architecture/rfcs/human-confirmed-domain-operations-v0.md 在完整提交 f9bdae67596c185cebc81dc555dd6a3e0242e9c5 的第2/9/13节,以及同提交 Finance execution README 的可选 M1/只读构件边界。适用行是 Exact operation、Authenticated click、One claim。原契约规定真实入口认证确认、原始期限、一次原子消费;consumed/unknown 的历史义务可继续读取和报告,不能因此再次获得执行许可。没有要求本 PR 完成真人交易或所有 M2/M3验收。

verify_bound_confirmation:85 从 normalized_parameters/operation 取记录,再核 provider、可选 principal、消息、状态、claim、金融 terms 与期限,返回 allowed/refusals/evidence。_mapping 支持 JSON对象和字符串;_canonical/_digest 使条款按排序 JSON 精确相等;_timestamp 解析 ISO时间。新测试覆盖10个合成正反例。整个现有产品树只有新测试导入该函数,原 M1 simulator、Core消费、Lark 与前端都没有接线;不能把新增函数称为用户已获得一条可靠执行通道。

三条具体阻断项:

  1. [P1] 原始期限被忽略或延长(155–156)。函数完全不读 canonical normalized_parameters.expires_at,而是用 plan 时间 or payload 时间;plan更晚时会遮住较早的 payload,解析失败则当没有期限,clock > expiry 又允许相等时刻。独立实验用真实隔离 ChatActionService.preview→record_operation_delivery→decide_operation→store.load 生成已确认/claimed记录,原 envelope 到期后30分钟仍返回 allowed=true、refusals=[],payload尚未过期。另两个受控输入复现 plan遮蔽及时间相等。修复须分别验证原始必需期限与计划/金融 payload 的限制,不能延长原批准,并用 now>=deadline 拒绝;缺失/非法期限要明确拒绝。增加原 envelope 先到期、两个期限相冲突、等时刻和非法值回归。
  2. [P2] 缺失原卡身份时跳过消息匹配(136–138)。card_message_ids 为空使第二项条件为假,只要 confirmation.message_id 非空便通过。我删除实际规范记录的 delivery 再调用纯函数,结果允许;这是受控不完整输入,不声称 Core 本身会生成这种已确认状态。既然该 API 声称检查自身卡片绑定,未知原卡应拒绝,不能退化成接受任意字符串。补缺 delivery、仅其他卡 receipt 和相冲突绑定的负例。
  3. [P2] “必须有一次性 claim”可以关闭且接受空 claim(146–147)。require_claim=false 时缺 claim 仍通过,默认设置下 {} 也被当作存在。我用两条受控输入独立复现了 allowed=true。需要求来自 canonical owner 的有效 claim identity;检查是否允许第一次消费仍交 Core,不能用这个新函数宣称一次性执行。删除绕过必须 claim 的选项,补遗漏、空对象和原消费重放测试。

真实 Core 实验只写一次性 synthetic项目/runtime,未调用 executor/账户/场所,读回记录在纯函数运行前后相同。缺卡/空claim案例是明确标记的输入变体,不能被拿来声称生产 Core 授权已经被绕过;真实过期记录案例则直接使用原 canonical 读回,没有修改它。

对主干的风险

当前无生产 import,所以没有证明既有默认执行受影响;风险是未来适配器将 allowed 当成完整确认检查。本 review 不把它升级为真实交易漏洞或账户损失结论。新 schema/refusal常量只在可选 finance模块,不应创建平行 Core vocabulary;LIVE_RECORD_STATUSES/LIVE_LIFECYCLE_STATES 的字符串集合也不能取代 Core消费规则。当前没有新增前端/Lark入口、后台计时器、凭证读取或真实场所 effect。

独立96项相关测试通过,包含新增10项与既有 simulator/Core operation/Lark路径;Ruff、DCO、diff、advisory后完整 semantic、公开路径扫描通过。advisory对packages新词表没有覆盖,空结果不证明不存在新语义。premerge为5 direct+3 selected,0失败/0hold,却没有覆盖以上反例,所以不能用绿检查消除 blocker。另在同 base 输入复现146/145预算红灯,属于既有架构问题,未把它冒记为本 PR引入。没有查询、派发或等待 CI,也没有执行 live/付费验证。

未验证的仍有实际 executor adoption、真人认证、Core消费到效果之间的期限与身份重验、恢复/重放、原会话返回、场所和长期运行。最低修复集中在上述拒绝逻辑和真正的消费边界;没有要求为一个前置 helper 另造完整平台、反复人工批准或新的配置工作流。

我的整体评价

REQUEST_CHANGES,准确 head f638fb4a1ce6dca9fee17d060ab393b88c544f03。Exact operation、Authenticated click、One claim 在这个新 API 的宣称范围内仍缺上述必要拒绝,原10个测试的输入形状掩盖了这些问题。

金融条款比较留在可选 finance provider;确认、claim、消费和期限的事实继续由既有 Core owner 提供,不应复制一套授权状态机。

当前 PR 只能作为尚待消费的纯校验前置;先修复拒绝边界并明确实际 executor 接入,不把它算作已完成的交易链路。

未来改动检查已定位可简化的相邻 owner:Core继续唯一负责确认/claim/消费,provider负责金融参数,不再独立推导“可执行”状态。应先修复反例并明确接入消费者,再由新 head 的完整证据决定;不继承当前绿测试或其他 PR批准。原 M1用户路径没有增加导航或确认轮次,当前也没有观察到新 gate 的真实用户恢复旅程。回滚可只移除这两个新增文件,既有 Core/M1路径保持。

English verdict: REQUEST_CHANGES - f638fb4. A real isolated Core-confirmed record is accepted after its original envelope expiry; controlled incomplete inputs also pass without original card binding or a valid mandatory claim.96 tests and5direct/3selected checks pass but miss these cases. This is an unused supplemental API, not evidence of a live Core or venue bypass.

elif _canonical(payload_terms) != _canonical(expected_terms):
refusals.append(TERMS_MISMATCH)

expiry = _timestamp(plan.get("expires_at")) or _timestamp(payload.get("expires_at"))

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.

[P1] Validate every original expiry without plan override

Trigger: Canonical envelope expires before payload/plan, invalid expiry or equality. Observed: allowed=true after original approval deadline. Minimum repair: Validate canonical envelope and each applicable plan/payload deadline independently; refuse missing/invalid required deadline and now>=expiry. Add coverage for Actual canonical record at before/equal/after deadline plus conflicting deadlines. The expiry case uses an actual isolated Core record; incomplete-record cases are controlled inputs, not a claimed live Core or venue bypass.

confirmed_card = confirmation.get("message_id")
# A confirmation is only valid for the card that carried this exact
# operation; a receipt from any other message proves nothing here.
if not isinstance(confirmed_card, str) or not confirmed_card or (

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.

[P2] Fail closed when original card binding is absent

Trigger: delivery/card_message_id unavailable while receipt contains any message_id. Observed: Unknown originalcard silently accepted. Minimum repair: Require authoritative nonempty original-card identity and exact receipt binding. Add coverage for Missing delivery, unrelated receipt, conflicting binding. The expiry case uses an actual isolated Core record; incomplete-record cases are controlled inputs, not a claimed live Core or venue bypass.

if record_status not in LIVE_RECORD_STATUSES or lifecycle_state not in LIVE_LIFECYCLE_STATES:
refusals.append(OPERATION_NOT_LIVE)

if require_claim and claim is None:

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.

[P2] Require a valid canonical claim unconditionally

Trigger: require_claim=false with omittedclaim or claim={} underdefault. Observed: Claim-free record accepted as confirmed execution precheck. Minimum repair: Remove claim bypass and require valid canonical claim id; Core retains firstatomicconsumption. Add coverage for Omitted/empty/foreignclaim and replay through canonicalconsumer. The expiry case uses an actual isolated Core record; incomplete-record cases are controlled inputs, not a claimed live Core or venue bypass.

Review follow-up on the confirmation gate:

- Require the authoritative original-card identity. A missing or self-
  contradictory delivery/card binding is refused instead of being treated as
  'no card to compare'.
- Require a canonical claim id unconditionally and drop the require_claim
  bypass, so an omitted or empty claim can never be admitted as a confirmed
  execution precheck.
- Validate the canonical envelope deadline as required, and enforce the plan
  and payload deadlines independently when present. An invalid deadline is
  refused and the boundary is inclusive (now >= expiry), so a later payload
  deadline can no longer extend an expired canonical envelope.

Coverage adds missing/conflicting origin card, omitted/empty/foreign claim,
missing/invalid canonical deadline, the equality boundary and conflicting
deadline cases.

Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
@loopx-agent

Copy link
Copy Markdown
Collaborator Author

Review follow-up pushed (80f2a75ad):

  • Origin card is now authoritative and fail-closed. A missing delivery/card binding yields card_binding_missing; conflicting ids yield card_binding_conflict; the receipt must match that exact id, otherwise confirmation_card_mismatch.
  • Claim is required unconditionally. require_claim is gone; an omitted, empty or shape-less claim yields claim_missing.
  • Every deadline is enforced independently. The canonical envelope deadline is required (deadline_missing/deadline_invalid otherwise); plan and payload deadlines are validated and enforced separately when present; the boundary is inclusive (now >= expiry), so a later payload deadline cannot extend an expired canonical envelope.

Tests: 18 passed (tests/extensions/test_finance_confirmation_gate.py), including missing/conflicting origin card, omitted/empty/foreign claim, missing/invalid canonical deadline, the equality boundary and conflicting-deadline cases. Fixtures remain synthetic; no live Core record or venue bypass is claimed.

@loopx-agent
loopx-agent merged commit f0b2ded into main Oct 17, 2026
28 checks passed
@loopx-agent
loopx-agent deleted the codex/finance-confirmation-gate branch October 17, 2026 13:51
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