Repository navigation
Conversation
Signed-off-by: ChengLong GU <88359731+gcl-coder@users.noreply.github.com>
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent | gpt-6.1-sol | OpenAI | runtime_reported | reasoning_effort=max
- [P1] 把畸形 quota_event 归入不可读事件:0269323f:ledger_readback.py:167。
- [P2] 把不可读原因保留到公开额度投影:0269323f:quota.py:375。
动机 / Motivation
依赖滚动额度限制自动工作的用户和 Agent,通过 quota plan、should-run 和 history 读取已消费预算。 原先丢失一个已索引的消费文件,会把已用额度12/12读成11/12并重新允许执行;现在截断文件被拦住,但内容仍为 object、缺失事件类型的文件仍会放行。 可观察交付是识别部分不可读消费文件并阻止自动执行;完整的失败关闭和错误原因读回仍未成立。 范围限于现有消费账本的只读解释和额度投影;消费与撤销的 TS 事务、费用测量和宿主采用仍由原边界负责。
改动思路 / Approach
保留同一只读账本 owner,并让预算与 usage 共享 observation 是合适的边界;最小修复应在这个 owner 验证事件并完整转送原因,无需增加故障库或重写事务层。 范围限于现有消费账本的只读解释和额度投影;消费与撤销的 TS 事务、费用测量和宿主采用仍由原边界负责。真实 File/SQLite CLI 对截断、非 UTF-8、非 object 和缺失 event 的 artifact 给出 blocked_health;原件恢复后回到原预算状态。
具体改动 / Changes
精确 head 0269323f57d1d801f73d3092f8bd0d8b1fc6e5d0,完整比较基线 f2990e3de49d6aa7441e0e0b739f49bfc84aae38。验收依据 https://github.com/loopx-project/loopx/issues/6869,版本 2026-10-23T12:33:09Z;先读原合同/问题,未用本次新写的文档替代独立规范。A spend that cannot be read should not silently become no spend 在此 head 为 not_met:缺失事件类型的正槽数对象仍会放行。新增原因字段还存在下述公开投影缺口。 五个路径的全部改动及相关合同条款均已阅读;改动行数只用于定位成本,结论来自真实 caller 与反例。quota_slot_contribution 保持 compatibility export,既有导入者仍可使用原接口。
关键代码讲解
- quota_slot_observation:把消费或撤销解析成贡献与错误;末尾仍把未知事件当零贡献。
- goal_quota_config:保留策略与 count,但遗漏 reason 列表。
- quota_status:把检测到的损坏转成既有 blocked_health。
- _apply_unreadable_ledger_fail_closed:防止旧的 eligible 投影覆盖当前损坏计数。
对主干的风险 / Risks
P1:非法事件仍被当零消费。 保持 index 为 quota_slot_spent,把一个原消费文件变成 {"quota_event":{"slots":1}}。两种 backend 的真实、带已认领 Todo 和明确 agent 的 should-run 都从12/12变成11/12,返回 normal_run、should_run=true。未知 event_type 的 plan 同样 eligible。这是本问题仍未覆盖的失败关闭路径;在可读 object 后继续验证事件类型和必要字段,并单独保留合法零消费。
P2:原因列表丢失。 截断和非 UTF-8 文件在 ledger 中有原因,但 goal_quota_config 只复制 count;后续已是 blocked_health 又提前返回。真实 plan 和 scoped guard 的 quota 均缺失 unreadable_event_reasons。保留经验证的原因并在恢复后清除,才能兑现本次文档中的错误读回。
161项 focused Python 和 Ruff 通过。相邻六个 recorder keyword 失败在不可变 PR base/head 均为6 failed、100 passed,完整规范化失败段相同;这是独立的现有测试问题,不要求此 PR 修复。严格 CQR 的当前目标基线归因未单独资格化,其 hold 仍保留。 native standard premerge FAIL/HOLD:六项直接检查、17项选择检查通过;cli-output-budget-regression-smoke.py 在原120秒限制内 timed_out,尚未归属。当前 CQR 因已验证缺口失败,不能把这些红项写成通过。 不查询或等待 GitHub CI;本地失败、缺少资格和 merge readiness 分开保留。
我的整体评价 / Verdict
当前任务尚未完成验收:截断文件得到拦截,但缺失事件类型的对象仍可重新放行已耗尽额度,错误原因也未到达公开读回。应在现有账本解释和投影 owner 内补齐两项修复,用同一真实 CLI 路径重验整个 PR。没有 rendered presentation 变化;安装宿主、实际模型执行、长期效果和完整成本未资格化。相邻测试的既有失败与尚未归因的本地超时分别保留,不抵消上述可复现缺陷。
English verdict: REQUEST_CHANGES — malformed indexed quota events still reopen exhausted quota, and public diagnostics lose their reasons.
| return None | ||
| return None, QUOTA_EVENT_UNREADABLE_EVENT_INVALID | ||
| return ("voided", voided_run_generated_at, slots), None | ||
| return None, None |
There was a problem hiding this comment.
[P1] 将缺失或未知事件类型作为不可读事件。保留 quota_slot_spent 索引,把一个消费 artifact 改为 {"quota_event":{"slots":1}} 后,真实 File/SQLite scoped should-run 均把 12/12 读成 11/12,返回 normal_run、should_run=true;未知 event_type 的 quota plan 也为 eligible。这里的 fallthrough 仍将畸形正槽数事件归零,重新放行已耗尽预算。请验证事件类型和必要字段并返回 event_invalid,同时区分合法零消费与撤销,并补上同一公开 CLI 路径的反例。
| unreadable_event_count = _int_number(raw.get("unreadable_event_count"), default=0) | ||
| if unreadable_event_count > 0: | ||
| payload["unreadable_event_count"] = unreadable_event_count |
There was a problem hiding this comment.
[P2] 保留不可读事件的原因列表。截断或非 UTF-8 artifact 已在账本投影中产生 unreadable_event_reasons,但这里只复制 count;后续 _apply_unreadable_ledger_fail_closed 遇到已是 blocked_health 又提前返回。真实 File/SQLite plan 和 scoped should-run 因而只显示 count=1,缺少本次文档承诺的原因读回,无法据此判断修复方式。请在规范化及该 fallback 中保留经验证的原因,恢复原件后清除原因,并验证完整公开投影。
|
Hi @gcl-coder, the DCO If the log confirms a missing |
…sons The first fail-closed pass covered artifacts that could not be read, but an indexed event that reads back and still names no known event type fell through as a zero contribution, which reopened an exhausted quota. The ledger now reports such an event as `event_invalid`, while a readable event that names a known type and spends zero slots stays a legal zero. The public projection also kept only the count: `goal_quota_config` dropped the reason list, and the fail-closed fallback returned early for an already blocked Goal, so the reason never reached the status or plan read-back. The count now travels with the validated reasons. Signed-off-by: ChengLong GU <88359731+gcl-coder@users.noreply.github.com>
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent; gpt-6.1-sol; OpenAI; runtime_reported; reasoning_effort=max
[P1] 不要把缺失或非法 slots 当作合法零消费。 loopx/control_plane/quota/ledger_readback.py:173–177 仍以 default=0 解析额度。把已索引消费文件的 slots 删除,或改为不能解析的字符串,当前真实 File/SQLite CLI 都从耗尽的12/12读成0/12;独立新 fixture 的 scoped guard 返回 normal_run、normal_delivery_allowed=true,并提交准入回执。应区分未知量与明确合法的零,在既有 owner 报告 event_invalid,保留支持的合法历史表示与显式零,并补真实入口回归。
动机 / Motivation
依赖滚动额度限制自动工作的用户和 Agent,通过 quota plan、history 和具名 Todo 的 should-run 读取预算。
原本已消费12/12额度;只删除消费文件中的 slots 后,当前版本仍把它读为0/12并重新允许工作。
截断文件、缺失或未知事件类型现在会阻止自动执行并公开原因;缺失或不可解析的 slots 仍会静默放行。
范围是现有消费账本解释与额度投影;不新增预算、修改事务 owner 或认证已安装宿主及长期收益。
丢失消费证据后继续执行,会使滚动预算失去限制作用;修复必须恢复可信额度,不能猜一个消费数。上述探针只观察准入,没有继续业务或新增扣额。
改动思路 / Approach
同一账本只读 owner 同时服务额度和 usage 是合适边界;保留现有贡献兼容别名,补齐字段校验即可,不需要新故障库、执行器或框架。
当前PR限于现有账本只读解释和公开额度投影,不改变消费/撤销事务与宿主权限。
先解析物理文件或 inline event,再由同一 observation 生成贡献及错误,额度与 usage 均消费它;原 TS 消费/撤销事务没有改写。新健康计数是每次读取派生的投影,无需人工同步另一份状态。plan 对旧 eligible 投影再检查当前账本,能够覆盖旧投影遗漏错误的情况。
| 输入 | 当前结果 | 独立判断 |
|---|---|---|
| 健康12槽 / 修复原件 | 12/12,throttled | 保留原预算 |
| 截断 / 缺失或未知类型 | blocked_health,原因可见 | 本轮修复成立 |
| 类型合法、slots缺失或不可解析 | 0/12,normal_run | 原失败关闭任务仍有缺口 |
| 明确合法零 | eligible、无损坏计数 | 保留合法零语义 |
表格来自真实命令观察,不是流程图或采用证明。
具体改动 / Changes
审查 head e40353daff1db29cf3a2f01e8a628efaeebb39f0,实际共同基线 f2990e3de49d6aa7441e0e0b739f49bfc84aae38;最新目标 main 为 2f544f969236f5dbbe858720108c41d07be60a6c。独立接受依据为 issue #6869,spec_revision 2026-10-23T12:33:09Z。先读原问题及上一轮评审,再检查差异;新写的文档没有被当作独立规范。
- Expected — not_met: 未知消费不能静默变成零,plan/history 至少应显示不可读计数。截断及类型缺失已满足;slots缺失/非法仍未满足,最小修复见首段。
- Trigger honesty — implemented: 合成输入明确模拟损坏或部分恢复,没有声称同版本原子写会自然撕裂。
完整5文件 +792/-27:三生产文件 +257/-27,新增测试模块516行/21例,状态合同19行。相对上次 0269323f57d1d801f73d3092f8bd0d8b1fc6e5d0,四路径 +190/-13;当前完整提案重新判断,没有继承旧结论。旧P1中的缺失/未知事件类型、旧P2中的原因列表转送已由本头实际命令验证解决;剩余finding是同一失败模型下尚未验证完整的必需数值字段。
关键代码讲解
- quota_slot_observation 统一贡献与错误,但缺失或不可解析的slots仍走合法零返回。
- goal_quota_with_spend_ledger 保留原Goal/实例/时间窗过滤,增加不可读计数与原因;
goal_quota_config现在转送原因列表。 - _apply_unreadable_ledger_fail_closed 阻止旧eligible投影掩盖账本错误,并在已blocked时保留原因。
- build_usage_summary 消费同一observation,补24小时/7天样本诊断;样本计数不成为精确历史账本。
旧 quota_slot_contribution 兼容导出仍是同一函数的别名,内部委托共同observer。已检查history、slot_accounting、status/plan及既有额度结果消费者,没有要求新增配置或编辑页面。
对主干的风险 / Risks
最强反例不是未知事件类型,而是已知消费类型缺少数值。_int_number 把读取失败改成0,后续分支便无法区分合法零和未知预算。两种实际后端、缺失及非法字符串共四个独立新guard fixture 均提交准入;不是借前一个未结轮次的恢复回执证明新执行。原完整Todo及消费index逐字保持,探针未实际消耗额度。
同一不可变基线/head各8种输入、File/SQLite共32个case,执行64次CLI;另外10个独立guard fixture验证上述四失败及健康/类型/零控制。六个健康、明确零、恢复后完整quota对象与基线逐字相同;截断和类型缺失在基线放行而当前阻断,证明探针对已修复缺陷敏感。首次顺序探针后续命令曾进入正常的原轮次恢复,已保留该事实;最终新准入判断使用独立fixture,未混用结论。
PASS:三个完整focused模块 106项通过,四个变更Python文件Ruff、配置指定17文件mypy及完整差异空白检查通过。保留一项既有Hypothesis目录收集warning。UNVERIFIED:本头完整standard premerge未在确定阻断后重跑,不把旧头120秒超时或六个recorder keyword失败换名为当前结果,也不把旧通过项继承为本头通过。没有查询、轮询或等待远端CI。当前新增提交还缺少 Signed-off-by trailer,是另一个合并前须修复的贡献规范问题。
语义与 CI 对齐
新原因词汇扩展现有只读诊断,额度状态和执行权仍复用既有owner。健康阻断是机器执行约束;不存在“仅建议不执行”的新解释。修复公开披露了默认变化,但声明“必需字段非法也报告event_invalid”尚未由实现兑现。补字段校验及两方向反例即可,不需要重写整个控制面或增加权限门。当前失败是已证明的不完整修复;其余未执行环境保持未验。
我的整体评价 / Verdict
REQUEST_CHANGES。 完整提案有明确有用增量、边界与维护成本相称,但同一个预算失败关闭请求尚未完成。long_horizon 与 user_experience 均为 not_yet_proven:读回为可用额度仍可能导致额外自动工作,不能由测试全绿或更完整类型判断代替实际反例。
同一账本只读 owner 同时服务额度和 usage 是合适边界;保留现有贡献兼容别名,补齐字段校验即可,不需要新故障库、执行器或框架。 当前PR限于现有账本只读解释和公开额度投影,不改变消费/撤销事务与宿主权限。
请在共同observer内完成slots的缺失/解析失败判断,保留明确合法零,再重验完整plan/history/scoped guard及必要标准检查。检查CLI、history、status及现有zcode quota结果消费者;没有渲染组件、首屏或布局变化,视觉评审不适用。视觉未验证范围:未执行浏览器、已安装App/Lark或原生Windows;没有改变渲染布局。真实宿主采用、PostgreSQL与持续完整成本未在本轮资格化;本评论不授予合并或安装权。
English verdict: REQUEST_CHANGES — e40353d. Known quota events with missing or unparseable slots still reopen exhausted quota; four fresh File/SQLite guards admit normal delivery. Prior type/reason findings are fixed, 106 focused tests pass, and healthy/zero/restored controls retain baseline quota. Complete amount validation and real-entry regression coverage before requalification; current full standard premerge remains unverified.
| return None | ||
| return ("spent", run_key, slots) | ||
| return None, QUOTA_EVENT_UNREADABLE_EVENT_INVALID | ||
| slots = max(0, _int_number(event.get("slots"), default=0)) |
There was a problem hiding this comment.
[P1] 区分未知 slots 与明确合法零。此处 default=0 会把缺失 slots 或 slots="lost" 当作零消费;保持 index 为 quota_slot_spent,真实 File/SQLite 的12/12耗尽额度会读成0/12,独立新 scoped guard 均 normal_run 并提交准入。请在共同observer验证字段存在及可解析性,未知量报告 event_invalid,保留显式合法0和受支持的历史表示,并补 physical artifact→plan/history/scoped guard 回归。
e40353d to
bc3a65b
Compare
The base/head CLI output differential budgets the agent-facing status JSON. Two always-present counters in the usage summary grew that surface past its growth allowance on every scenario, even when nothing was unreadable. The counters now appear only when a quota event could not be read back, so the healthy summary keeps its original shape and the growth budget applies only to the integrity failure it describes. Signed-off-by: ChengLong GU <88359731+gcl-coder@users.noreply.github.com>
|
Both review points are addressed in P1 — a malformed quota event is no longer read as a zero spend
The reason vocabulary is closed ( Before, a malformed event reduced the observed spend and a Goal at 12/12 read as 11/12. Now the count is non-zero and the Goal fails closed. P2 — the public projection keeps the unreadable reasons
The count says how many events broke; the reasons say which artifact to repair. Output budget
TestsFive regression tests were added to
|
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent; gpt-6.1-sol; OpenAI; runtime_reported; reasoning_effort=max
[P1] 缺失或非法 slots 仍会重新放行已耗尽额度。 当前 head 8f331e3cd4fca24b9edc474b03b203ca385db797 的 quota_slot_observation 仍以 default=0 解析必需数值。真实 File/SQLite 的四个独立新 guard fixture 均从12/12变成0/12,返回 normal_delivery_allowed=true 并提交 admission receipt。在共同 observation owner 区分缺失或解析失败的 slots 与明确合法零,未知量返回 event_invalid,并补真实公开入口回归。
动机 / Motivation
依赖滚动额度控制自动工作的用户和 Agent,通过 quota plan 与具名 Todo 的 should-run 读取剩余预算。
原消费文件完整时额度为12/12且禁止执行;只删除 slots 或将其改成 lost 后,当前版本读成0/12并重新准入。
截断和缺失事件类型已正确阻断且公开原因;缺失或非法额度数仍被静默解释为零,完整失败关闭尚未交付。
本次范围是现有消费账本只读解释及公开额度投影,不修改消费事务、宿主权限或实际预算。
这是当前任务同一失败模型中的未完成部分。并非要求读取器猜出损坏文件原来花了多少,也不是声称同版本原子写会自然撕裂;磁盘损坏、部分恢复或其它版本写入都可能留下可解析但不完整的对象。未知数不能被当作明确的零。
改动思路 / Approach
本次范围是现有消费账本只读解释及公开额度投影,不修改消费事务、宿主权限或实际预算。
复用原账本解释、额度和 usage owner;健康计数每次从实际 index/artifact 推导,没有新增故障库或人工同步状态。
将预算与 usage 的贡献解释并入同一 observation 是合理的有界收敛。原兼容 export 仍指向共同函数,原 TypeScript spend/void 事务没有被替换。最小修复就在当前数值读取处,不需要新开关、数据库或执行器。
相对上次 e403,当前新增的 usage 健康态字段省略有明确输出成本理由;本轮仍按整个 PR 检查五个路径。旧的类型缺失与原因转送问题在实际命令中成立修复,未将该正向结果扩展为所有未知消费都安全。
| 同输入观察 | 当前结果 |
|---|---|
| 健康12槽、恢复完整 artifact | throttled,12槽,无准入回执 |
| 明确合法0 | eligible,0槽,合法准入 |
| 截断、缺少事件类型 | blocked_health,不可读计数1及具体原因,无准入回执 |
| 类型正确但 slots 缺失或为 lost | eligible,0槽,错误准入并提交回执 |
这是实际 CLI 观察的文字对照,不是模型采用或完整成本证明。
具体改动 / Changes
实际共同基线 f2990e3de49d6aa7441e0e0b739f49bfc84aae38;本轮完整差异为五路径、+792/-27:三生产文件+253/-27,测试516行,合同23行。上一 head e403 的 finding 逐项重新审视:缺失/未知类型与公开原因已修;slots 缺失/非法的 P1 仍在当前代码上复现。三个 PR commit 现在均有 Signed-off-by,旧的 DCO 缺口不再沿用为当前问题。
独立接受依据为 issue #6869,spec_revision 2026-10-23T12:33:09Z。Expected:not_met,不可读消费不能静默变成零,plan/history 应公开未知;当前数值缺口违反同一要求。Trigger honesty:implemented,本轮只在隔离合成 artifact 中模拟损坏,保留 index,不冒称生产事故或原子写撕裂。PR 自己新增的文档不是独立规范。
关键代码讲解
quota_slot_observation统一贡献与错误,类型和键判断已补齐;173行仍把必需数值的缺失/解析失败折成零。此路径影响真实读取,不是仅 helper 返回格式。goal_quota_with_spend_ledger在原 Goal/窗口过滤内汇总同一 observation;goal_quota_config和_apply_unreadable_ledger_fail_closed继续把实际检测出的原因传到 plan/guard。observer 未报告的未知量无法由投影层补救。build_usage_summary使用同一解释器,在实际损坏时增加窗口计数,健康状态省略新计数;它是样本汇总,不成为第二账本 authority。
对主干的风险 / Risks
最强反例是事件类型合法而数值缺失:{"quota_event":{"event_type":"quota_slot_spent","run_generated_at":"<original timestamp>"}}。保留原 index、Todo 与认领,只改变 artifact 的 slots。两种 backend 的 plan 均为 eligible/0,随后各自全新原生 guard 提交准入;不是把前一个未结 Turn 的恢复当作新执行。探针没有继续业务或新增 spend,不能据此声称已经额外收费。
同一探针在不可变 base/head 各运行两后端×七输入,合计28个匹配案例、56次实际 CLI 调用。缺失/非法 slots 在两版本均失败,说明这是本次修复仍未完成的原缺口;截断和缺失类型则由 base 的错误准入变为当前阻断,证明探针对修复敏感。健康、明确零和恢复完整文件的已观察预算/准入控制保持原行为。每例使用独立合成根;除已验证观察时间和临时位置外,没有删除身份、状态或错误字段来制造一致。完整输出均保留,未声称所有字节或所有消费者逐字段等价。
PASS:完整 test_quota_ledger_integrity.py 与 test_usage_summary.py 共60通过、0跳过;四个改动 Python 文件 Ruff、配置17文件 mypy、完整差异 whitespace 通过。diff-scoped semantic advisory 成功且未发现受支持的新 carrier;动态 frozenset 仍由本轮人工判定为现有 quota 只读原因词汇,空 advisory 不证明所有语义已分析。
UNVERIFIED:发现确定性 P1 后未重跑完整 standard premerge;旧头的超时、相邻 recorder 失败及旧通过保留原来源,不移记为本头结果。没有查询、轮询或等待 GitHub CI。Windows、PostgreSQL、已安装 App/Lark 与实际模型使用未在本轮资格化。没有渲染布局变化,视觉评审不适用;当前未验证的运行环境与真实长期效果分别保留。
语义与 CI 对齐
失败关闭是本次明确披露的默认修复,不是可选建议或安装即启用的新能力。新原因复用原 blocked_health 与账本解释 owner,不扩张 actor 或权限。当前声明“类型必需字段非法为 event_invalid”仍未覆盖 slots;在共同 observation owner 区分缺失或解析失败的 slots 与明确合法零,未知量返回 event_invalid,并补真实公开入口回归。 以同样的 File/SQLite plan/guard 回归验证,同时保留明确零、健康、修复后恢复和完整原因。
我的整体评价 / Verdict
REQUEST_CHANGES。 有用问题和维护边界成立,范围总体相称;当前 long_horizon 与 user_experience 均为 not_yet_proven,因为损坏的消费仍能恢复自动执行资格。复用原账本解释、额度和 usage owner;健康计数每次从实际 index/artifact 推导,没有新增故障库或人工同步状态。 不需要为这处修复创建平行框架或新的确认步骤。
当前未知数→零的缺口仍需修复;本轮测试通过不替代该真实反例。检查了原 CLI/plan/status/history 和 usage 消费者,没有新增配置或页面。视觉评审不适用:本五路径差异没有渲染组件;视觉未验证范围为 none。原生平台、整包标准资格、实际宿主采用与完整成本未验,本评审不授予合并或安装权。
English verdict: REQUEST_CHANGES — 8f331e3. Missing or unparseable slots still turn exhausted quota into eligible/0 and four fresh File/SQLite guards commit admission. The same base/head probe covers28cases/56 CLI calls; type/diagnostic fixes and healthy/zero/restored controls hold.60focused tests and static checks pass; full current standard premerge remains unverified.
Signed-off-by: ChengLong GU <88359731+gcl-coder@users.noreply.github.com>
songoow
left a comment
There was a problem hiding this comment.
Reviewer: model_agent — GPT-6 Astra Pro — OpenAI — self_reported
动机 / Motivation
持续运行 Goal 的自动执行器和查看配额的操作者会遇到这类问题。
一次消费已用完 12 个配额单位后,账本索引仍在而消费文件损坏;旧版会读成零并重新放行,本 PR 要把未知消费阻断并说明修复来源。
实测截断 JSON 已能阻断,缺失或不可解析的消费数量仍会重新放行。
本次边界是现有本地配额账本的正确读回,不包含新调度策略、外部 API 费用去重或长期吞吐资格。
改动思路 / Approach
账本读取、未知数量约束和公开投影各留在既有 owner;不新增存储、调度器或手工状态,机制成本与预算越界风险相称。
当前 PR 应完成既有 ledger 的不可读分类和真实 guard 阻断;最小追加修复是数量解析,完整 provider 费用计量仍在范围外。
我按现有 ledger readback owner → usage summary / quota projection → 实际 quota plan 和 should-run 的链路检查了完整五文件 diff;没有把新增错误字段本身当作端到端完成。复用了真实 canonical fixture,分别执行 File 和默认 SQLite 路径,并用当前合并基线与候选的同一套输入做对照。
具体改动 / Changes
关键代码讲解
quota_slot_observation 共用读取和贡献分类;goal_quota_with_spend_ledger 保留既有 Goal/窗口过滤后汇总,再由 quota_status 拒绝未知预算;build_usage_summary 用同一来源向 history/status 报告数量与原因。三者都不修写账本。
规范基准:https://github.com/loopx-project/loopx/issues/6869,修订时间 2026-10-23T12:33:09Z。Issue 没有原条款编号,以下用原要求简短标题映射:未知消费不能静默计零 — not_met,slots 缺口见 F1;unreadable_event_count — implemented,物理错误进入真实计数/原因/拒绝;损坏来源可恢复 — implemented,固定 index 的截断和恢复在两个 provider 上重新计算。
新增的 unreadable reason 集合与 quota_slot_observation 被账本汇总和 usage summary 共用;非零 unreadable count 会进入 blocked_health,同时保留公开 projection 的原因。缺失文件、截断 JSON、非对象、未知类型、合法零值、正常 void 和恢复读回的方向都合理。兼容 facade 继续复用原 owner,没有另建预算 authority。
[P1] 缺失或不可解析的 slots 仍被当作合法零消费。
位置:loopx/control_plane/quota/ledger_readback.py:173–179(void 分支也使用相同转换)。_int_number(event.get("slots"), default=0) 在字段缺失或值为 "lost" 时返回 0,随后返回 (None, None)。这把「无法确定消费数量」与明确写出的合法 slots: 0 合并,绕过了本次新增的 unreadable gate。
我在 exact head 6b8b5cfbd0a6b1f7f1aa841e580351679ebf97da 的真实 CLI 上验证了这个路径:每个场景使用独立的 canonical Goal/guard,预算 12,完整 index 指向消费 12 的 artifact;健康输入和恢复输入均拒绝继续执行。只删除 artifact 中的 slots,或将其写为 "lost",保留 index 字节不变,File 与 SQLite 都读回 spent_slots=0、state=eligible,should_run=true、normal_delivery_allowed=true,并给出 must_attempt_work=true。截断 JSON 对照则被候选正确拒绝;显式零值继续合法。
请先验证消费数量是否可读,转换失败或缺失时返回已有的 event_invalid,再单独处理合法零值;相同规则也应覆盖 void。回归要穿过 plan 与新的 should-run guard,保留 index,只改变 artifact,并覆盖 File/SQLite、显式零值及恢复。不要把未知数量猜成一个具体消费量。
验证与既有审查 / Validation and Review Frame
- 本轮实际执行
tests/control_plane/test_quota_ledger_integrity.py和tests/control_plane/test_usage_summary.py:60 passed。 - 同一只读/隔离 CLI 探针在当前合并基线
428f71177db4c0acb87d16ecad2abe555d952e2d和候选分别执行 12 个场景:File/SQLite × 健康、缺失 slots、无效 slots、截断 JSON、明确零值、恢复;每个场景执行 plan、history、独立 should-run,index 均保持不变。 - 基线会错误放行截断 JSON,候选会拒绝,证明实际修复有效且探针能区分差异;缺失/无效 slots 两端仍放行,证明这是本 PR 承诺覆盖的损坏账本缺口尚未关闭,不是新引入的回归。
- 既有 review 提到的 event_type 和公开 reason 修复在当前源码与既有回归中已覆盖;没有把这些已修项重复列为 blocker。slots 问题仍可在当前 head 独立复现。
- 输入均为合成隔离状态,未执行业务交付、未消费真实 provider 预算;测试数与结论仅限本轮实际执行范围。未重跑整仓测试、CI 或长期 workload。
对主干的风险 / Risks
语义与 CI 对齐
当前“未知不是零”约束仍有 F1,须修复并重跑上述真实 CLI 对照。当前 dashboard-browser (2) 的 loaded-lens 1 秒预算另有一次 1.107 秒失败;本轮未完成该远程失败的因果归因,不豁免其合并检查,也不把它充作 ledger 缺陷证据。
主要风险是账本损坏后自动执行预算仍然 fail-open;它直接违背本 PR 的修复目标。现有 60 个测试覆盖了读取和类型缺陷,但没有验证「已知事件类型、消费数量不可读」这一类。当前错误 reason 复用与默认正常形状保持是合适的 bounded refactoring,不需要另建框架;数量验证与真实入口回归应在同一修复中补齐。
我的整体评价 / Verdict
长期持续执行的配额保证尚未成立,未知数量仍可让后续工作越过原 allowance。用户体验在物理错误诊断上改善,但数量损坏仍误报可用配额。本次只改既有 CLI/JSON 诊断,没有渲染页面改动。语义上扩展读取原因并复用既有 blocked_health,没有新 actor 或权限。
REQUEST_CHANGES。 支持当前 owner 和错误投影方向;请关闭上述已复现的数量读取缺口后再复审。当前证据不支持把 unreadable ledger 的 fail-closed 修复认定为完成。
English verdict: REQUEST_CHANGES — exact head 6b8b5cf still treats missing or unparseable slot counts as zero and reopens exhausted quota through real File and SQLite CLI admission paths.
An indexed spend or void event that names its type but not how many slots it moved was read with default=0, folding an unknown quantity into an explicit zero spend. A Goal at 12/12 then read as 0/12, stayed eligible and was admitted again. _slot_count() now separates the two cases on both the spend and the void branch: a missing, boolean or unparseable count is event_invalid, an explicit zero stays a legal zero contribution, and the supported numeric string form keeps working. The regression drives the real index.jsonl + artifact shape and holds the index row fixed while only the artifact changes, so a repair is the same edit an operator would make. Signed-off-by: ChengLong GU <88359731+gcl-coder@users.noreply.github.com>
|
The slot-count gap is closed in [P1] an unreadable slot count is now an unknown, not a zero spend
The observation is the single place both the ledger and the usage summary read, so the fix reaches RegressionsSix tests in
Four of them fail on What I am and am not claiming
The File and SQLite backends hand this reader the same index row plus Docs
Local run
Current head
The four failures are Both shards finished at 52:45 and 54:45 against a 60 minute job timeout, which is why the remaining four were cancelled rather than failed. Attribution of the red checks
The base commit This patch's diff against that base is five files -
|
songoow
left a comment
There was a problem hiding this comment.
Reviewer: model_agent | GPT-6 Astra Pro | OpenAI | self_reported
审阅固定在 05eaeeaca360f7eaccbbdf42a5067119d0be5a55;基线 428f71177db4c0acb87d16ecad2abe555d952e2d。核对了最新讨论与当前 main 21367015cce45294dcf99f54a3211d66f5d842c5,触及的关键生产 owner 在 main 仍与基线一致。
本轮未发现新的具体阻断性代码缺陷;当前为验证证据 hold。 当前失败 test-shard (2)/(3) 的完整诊断不可取得,缺少独立归因;这不是新增代码 finding。
动机 / Motivation
受影响的是依靠自动轮次配额控制成本的 Goal 维护者,以及读取使用量摘要的调度器。 当已经计费的索引仍在、对应账本事件的数量却缺失或损坏时,旧实现可能把未知消费当作零,给同一个 Goal 再发可用额度。 本次把不可读事件明确投影为健康阻塞;恢复有效事件后,下一次读取能够重新放行,合法的零消费仍然有效。 范围是现有配额读取、摘要和准入路径,不改变预算大小、计费策略或 Todo 的完成权威。
未发现为本次具体增量单独批准的书面验收规范;按原问题、基线现有owner契约与已有讨论判断,不把本PR新增文档当作独立验收依据。
改动思路 / Approach
不可读判断放在已有账本读取 owner,再由 quota_status 和计划入口传播,避免调度器与使用量摘要各自猜测消费。 当前 PR 的独立边界是从物理账本到自动轮次准入的完整闭环,包含明确零值与损坏事件的区分。 我重新检查了上一轮 missing slots / 非数值被当作零的 P1。当前 _slot_count 返回可区分的未知值,spent 与 void 两类事件都把该情况转成 event_invalid;不是只在显示层增加计数。最小方案应继续共享这一个读取语义,不需要新的账本格式或第二份可写健康状态。
具体改动 / Changes
关键代码讲解
quota_slot_observation(ledger_readback.py:182)识别索引声称的配额事件,追到真实 artifact,并分别保留读不到、格式错误、事件缺失和数量无效的原因;非配额行仍不是异常。_slot_count(:64)拒绝 bool、空值、非有限值和无效字符串,同时保留明确零与已有数值字符串兼容。goal_quota_with_spend_ledger(quota.py:389)累积窗口内有效消费与不可读事件;_apply_unreadable_ledger_fail_closed(:682)让已有 eligible 投影也回到 blocked_health,避免旧投影绕过新读取结果。build_usage_summary(usage_summary.py:141)复用同一 observation。健康结果省略新增不可读计数,故不把所有现有消费者强制迁移到新形状。测试多数是异常工件与修复的矩阵;新增生产逻辑集中于三个现有 owner,而不是另立策略层。
验证
实际运行 test_quota_ledger_integrity.py、test_usage_summary.py 与 test_int_number_non_finite.py:87 passed。另用真正的 quota should-run --codex-app,在独立 File 和 SQLite 注册表各执行有效 2 slots → missing slots → NaN → 明确 0 → 修复 2,共 10 次:损坏时 should_run=false / blocked_health / event_invalid;零与修复后 eligible。相同输入在不可变基线 428f711 的 missing/NaN 均错误 eligible,健康与零值结果一致。这是实际准入反证,不是 helper mock。 本PR对其不可变base执行git diff --check,检查通过。
对主干的风险 / Risks
未发现新的具体阻断性代码缺陷;上一轮数量解析 P1 已修复。当前仍无法取得失败 test-shard (2)/(3) 的完整诊断并独立归因,因此不能把它们声称为既有失败。已通过的局部套件和双 provider CLI 足以证明这次账本不变量,但不能证明未知失败不涉及其他准入调用者。最小补证是保留相同失败命令与签名,确认 exact head 的相关入口通过或与不可变 base 同因失败;无需把未知红灯描述成确定的产品 bug。 当前报告只覆盖明确列出的执行与源码证据,未完成的provider、scope矩阵或视觉状态不会标成通过;review结论与合并时的分支保护、完整CI资格分开记录。
我的整体评价 / Verdict
代码修复有效且比例适当,实际恢复路线没有把损坏状态永久锁死。保留验证 hold 的唯一当前原因是尚未归因的失败验证,不重开已修复 P1,不要求本 PR 修理无关历史问题;一旦当前失败得到归因或同覆盖有效证据,就应重新给出 approval 结论。
本变更只改账本与 CLI/摘要数据,没有修改渲染页面;已检查 quota 和 usage_summary 的输出边界。 不适用;本 PR 不改变页面布局。
English verdict: REQUEST_CHANGES — exact head 05eaeea; no new concrete code defect found; current validation gaps remain explicitly unverified.
Problem
The quota spend ledger silently dropped quota events it could not read back.
load_quota_event_from_run()returnsNonewhen an indexedquota_slot_spent/quota_slot_voidedrow points at an artifact that ismissing, truncated, not UTF-8, not a JSON object, or that carries no
quota_eventat all.goal_quota_with_spend_ledger()treated thatNoneas"this event spent nothing", so
spent_slotscame out lower than reality andquota_status()could report a Goal aseligibleagain after it had alreadyspent the slots. The same read path is used by the usage summary, so the
dashboard reported the same silent loss.
The real slot count of an unreadable event is unknown, so it cannot be
invented. But "unknown" must not degrade into "zero": that is exactly the
fail-open that hands a Goal back quota it already used.
Fix
Distinguish a legitimate zero-spend event from an unreadable one, and fail
closed on the latter.
loopx/control_plane/quota/ledger_readback.pyresolve_quota_event_from_run()andquota_slot_observation(): theyreturn the event and a machine-readable reason when the event cannot be
read back. The reasons are stable constants
(
missing_source,artifact_missing,artifact_unreadable,artifact_invalid_json,artifact_not_object,event_missing,event_invalid).UnicodeDecodeErroris reported instead of escaping.quota_slot_contribution()now delegates toquota_slot_observation()sothe two surfaces cannot drift; its return value is unchanged.
loopx/quota.pygoal_quota_with_spend_ledger()counts unreadable events and reportsunreadable_event_countplusunreadable_event_reasonsinstead ofinventing a slot count.
quota_status()maps a non-zero unreadable count tostate=blocked_healthwithblocked_action_scope=automatic_agent_turns._apply_unreadable_ledger_fail_closed()keeps the plan layer fromre-admitting a Goal through a quota projection that was computed before
the artifact became unreadable.
loopx/control_plane/quota/usage_summary.pyunreadable_event_count_24h/unreadable_event_count_7dnext tothe readable slot totals.
docs/status-data-contract.mddocuments the new fields and the fail-closedrule.
Behaviour
spent_slotscountedeligibleunreadable_event_count, Goal reads asblocked_healthTests
tests/control_plane/test_quota_ledger_integrity.py(new, 16 cases) covers themissing artifact, the truncated artifact, the artifact without a quota event,
the non-object artifact, the index row with no event source, the non-UTF-8
artifact, an unreadable void, the fail-closed quota state, the stale-projection
bypass in the plan layer, the repair path, the legal void path, and the
distinction between a legal zero-spend event and an unreadable one.
The related quota suites show the same failure set before and after this
change (the pre-existing failures are the local
EffectRuntimeHostPermissionErrorenvironment failures), and
ruff checkis clean on the touched files.Fixes #6869.