Repository navigation
fix(runtime): reject out-of-range UTC timestamps - #6082
huangruiteng merged 2 commits into
Conversation
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent | gpt-6.1-sol | OpenAI | runtime_reported | reasoning_effort=xhigh
REQUEST_CHANGES — [P2] Align the typed expiry codec before treating UTC overflow as missing time.
Exact reviewed head: 6082@55e19670d8956841b67fb106e1e5433fe462748b; immutable base 07eb7a1288e8a33995493f9d50475f2664fcbf2e. No approval inherited, CI not queried/polled/waited for.
动机
使用监控 Todo、状态缓存和运行时间排序的调用者,包括给监控配置到期时间的用户。
UTC 转换越过 datetime 上下界时,旧解析器和部分读取者抛 OverflowError;head返回 None 并让读回继续。但监控配置仍保存这些越界到期值,非 watch-only 监控在 Python 读路径被视为没有到期边界。
同一42项输入中40项 parser 和 monitor 结果完全相同,两项 UTC 极值从 OverflowError变None;chronology42项完全相同。真实文件缓存的极值变为无写入的 miss。实际 typed plan/public writer仍接受极值 expiry;readback保留越界值、watch_only=false、无 resume,expiry=false而due=true。改成合法到期值后读回恢复。
本次只验证共享时间解析及既有监控、缓存消费者;不修改活跃 Goal、不宣称 PostgreSQL、打包前端或全量系统时区验收。
改动思路
共享 Python reader 的异常归一有价值;监控准入和时间含义已由 TS codec 持有,应在同一既有 owner 补 UTC 范围一致性,避免新增平行的 expiry 规则。
当前PR边界:现有共享 UTC parser 与受影响监控配置 codec、到期读取和恢复。
The shared Python parser is already the retained compatibility adapter used by monitor/cache/history. parseTodoTimestampMicros is the existing TS decision owner for monitor configuration and can represent instants beyond Python datetime; align that one codec rather than add a Python boundedness rule. chronology/history catches remain pre-existing consumer defenses, not a new policy owner.
最小修复:Extend the existing TS parseTodoTimestampMicros owner to reject UTC results outside the retained 0001..9999 range before the monitor update commits. Keep the Python exception normalization, and cover typed planning plus public update/no-write rejection and retained-reader expiry/due parity; do not introduce a second expiry owner. 有界后续重构检查已做:最近owner能承载修复,保留不同callback/provider错误及支持精度合同;不要求新通用框架或无关TypeScript迁移。
具体改动
规格先读公开问题 #6081(snapshot 2026-10-10T06:45:50Z),再读固定revision的既有合同,然后检查全部diff及调用者。Expected behavior在真实生产入口已实现;既有Monitor有效到期/resume/watch-only边界未满足,不把作者声明或测试数作为验收。
Criterion Existing Monitor boundedness: not_met — The typed plan and public update commit the out-of-range expiry. The new Python None result is interpreted as absent expiry: readback succeeds, monitor_todo_is_expired=false and monitor_todo_is_due=true despite watch_only=false. Typed validation and retained-reader time meaning diverge, so the required expiry does not bound that consumer.
关键代码讲解
parse_timestamp(loopx/control_plane/runtime/time.py:68):把 UTC 转换放入 ValueError/OverflowError 边界,越界返回 None。chronology_key(loopx/control_plane/runtime/time.py:88):保留原非法时间排序 fallback,42项输出相同。monitor_todo_is_expired(loopx/control_plane/scheduler/monitor_todo.py:77):parse None 被解释为无到期边界,是实际读回后的风险。
全diff两文件:生产4行新增/4行删除,把naive时区补齐和aware UTC转换移入try,并扩大catch;测试8行新增,仅覆盖直接parser的两端极值。没有修改现有TS codec及监控配置路径,因此green helper测试无法证明实际期限有效。
阻塞问题
[P2] Align the typed expiry codec before treating UTC overflow as missing time
触发:A non-watch-only Monitor is configured with expires_at="0001-01-01T00:00:00+14:00" (also reproduced at the upper boundary), no resume condition, and an ordinary due time.
实际结果:The typed plan and public update commit the out-of-range expiry. The new Python None result is interpreted as absent expiry: readback succeeds, monitor_todo_is_expired=false and monitor_todo_is_due=true despite watch_only=false. Typed validation and retained-reader time meaning diverge, so the required expiry does not bound that consumer.
修复与回归:Extend the existing TS parseTodoTimestampMicros owner to reject UTC results outside the retained 0001..9999 range before the monitor update commits. Keep the Python exception normalization, and cover typed planning plus public update/no-write rejection and retained-reader expiry/due parity; do not introduce a second expiry owner. Run todo.monitor_metadata.plan and public update_goal_todo with both extreme offset expiry values and watch_only=false/no resume: expect normal timestamp rejection without state changes. Verify legal boundaries, corrected expiry readback, and identical Python/TS expiry/due meaning.
对主干的风险
42项输入40项parser/monitor相同,两个极值按要求返回None;chronology42项相同。原先TS writer接受极值是既有问题,不声称该PR新增writer宽松行为;当前PR新增的风险是Python readback/expiry把它转为缺失边界,普通非watch-only监控成为due=true/expired=false。真实文件cache不再崩溃而返回miss,字节不改;合法缓存fixture缺envelope而miss在两边相同,不宣称完整cache-hit恢复。
77 passed in 28.11s on base; 79 passed in 29.02s on head. Ruff and diff-check pass. Current-diff semantic advisory precedes canary: 5 direct and 17 selected checks pass, zero failures/skips; CI not consulted.
作者先前Ruff记录不是当前失败归因;本轮同一source环境的base/head Ruff均通过,不猜测历史原因。上述风险在测试与canary全绿时仍通过实际入口复现,不能用green检查消除。初始评审夹具的命令匹配及读回异常记录问题已保留并修正;最终相同脚本在B/H完整运行。没有真实飞书、活跃Goal或外部服务写入,也未观察打包UI;本PR没有新UI操作入口。
语义与 CI 对齐
这里复用既有UTC有效性词汇与TS monitor owner,没有新actor/permission、substring denylist或平行状态分类。The typed plan and public update commit the out-of-range expiry. The new Python None result is interpreted as absent expiry: readback succeeds, monitor_todo_is_expired=false and monitor_todo_is_due=true despite watch_only=false. Typed validation and retained-reader time meaning diverge, so the required expiry does not bound that consumer. 这违反固定revision的既有合同,需要上面的最小修复及实际caller回归;不以未来RFC或CI状态作为阻塞依据。没有新的opt-in/default-off承诺;原permissions、claim、effect和cursor authority仍独立。机器拒绝与advisory建议不能混称。
我的整体评价
REQUEST_CHANGES。局部正向修复成立,生产机制规模合理、归属清楚;完整切片仍不能通过,因为实际负向/后续工作合同出现回归。long_horizon与user_experience均判为regression:The typed plan and public update commit the out-of-range expiry. The new Python None result is interpreted as absent expiry: readback succeeds, monitor_todo_is_expired=false and monitor_todo_is_due=true despite watch_only=false. Typed validation and retained-reader time meaning diverge, so the required expiry does not bound that consumer.
共享 Python reader 的异常归一有价值;监控准入和时间含义已由 TS codec 持有,应在同一既有 owner 补 UTC 范围一致性,避免新增平行的 expiry 规则。 现有共享 UTC parser 与受影响监控配置 codec、到期读取和恢复。 修复后重跑同一实际入口和上面的回归,并对新head重新评审。范围内没有新权力或自动激活;未执行merge、dismiss或源码修复。历史经验只作为outcome-first分析建议采用,不能据此宣称记忆带来了可测效果。
English review
Motivation. Users configuring Monitor deadlines and reading status need malformed UTC timestamps to fail ordinarily. The original upper and lower offset boundaries raised OverflowError in the shared parser and readers; the new parser returns None and the cache reader safely returns a miss without modifying its file. Issue #6081 was read before the implementation; the accepted contract at docs/reference/monitor-configuration.md@07eb7a1288e8a33995493f9d50475f2664fcbf2e supplies the unchanged negative/continuation boundary. This reviews the entire exact head, not only the added helper test.
Implementation and ownership. Moving UTC conversion inside the existing ValueError/OverflowError guard is a small, useful reader repair. The unchanged TS parseTodoTimestampMicros codec owns Monitor input validation; it must agree with the retained reader about valid UTC instants. Existing chronology/history guards remain consumer defenses. The full diff has4 production additions/4 deletions and8 test additions; it adds no lifecycle or configuration surface. The future-facing ownership pass keeps these existing boundaries and requests the smallest related correction.
Blocking finding and real reproduction. A non-watch-only Monitor is configured with expires_at="0001-01-01T00:00:00+14:00" (also reproduced at the upper boundary), no resume condition, and an ordinary due time. The typed plan and public update commit the out-of-range expiry. The new Python None result is interpreted as absent expiry: readback succeeds, monitor_todo_is_expired=false and monitor_todo_is_due=true despite watch_only=false. Typed validation and retained-reader time meaning diverge, so the required expiry does not bound that consumer. Extend the existing TS parseTodoTimestampMicros owner to reject UTC results outside the retained 0001..9999 range before the monitor update commits. Keep the Python exception normalization, and cover typed planning plus public update/no-write rejection and retained-reader expiry/due parity; do not introduce a second expiry owner. Run todo.monitor_metadata.plan and public update_goal_todo with both extreme offset expiry values and watch_only=false/no resume: expect normal timestamp rejection without state changes. Verify legal boundaries, corrected expiry readback, and identical Python/TS expiry/due meaning.
Validation and full-diff risk. The identical harness compares42 timestamp inputs:40 parser/monitor cases remain identical, two UTC-overflow cases now return None, and all42 chronology outputs remain identical. A separate real typed effect/public writer/readback probe confirms the invalid-expiry counterexample with watch_only=false and no resume condition. TS acceptance already existed; the new Python reader outcome silently treats that persisted bound as absent. Correcting the expiry through the same writer restores ordinary readback. The real cache fixture exercises malformed timestamp refusal; its valid timestamp lacks a projection envelope and misses equally at B/H, so no warm-cache recovery is claimed. 77 passed in 28.11s on base; 79 passed in 29.02s on head. Ruff and diff-check pass. Current-diff semantic advisory precedes canary: 5 direct and 17 selected checks pass, zero failures/skips; CI not consulted. Initial reviewer-fixture failures are retained and corrected; current production-counterexample evidence remains failing. No active Goal, paid model, PostgreSQL or packaged UI validation is claimed.
Overall judgment. The original local defect is fixed and the mechanism is proportionate, but sustained continuation and user experience regress at the demonstrated negative boundary. Preserve the existing typed error/UTC vocabulary and repair the named existing owner before re-review. No opt-in/default-off claim, actor authority, permission or automatic activation is added. CI and merge authority are separate; no merge was performed.
English verdict: REQUEST_CHANGES — 6082@55e19670d8956841b67fb106e1e5433fe462748b. Positive issue reproduction passes; the actual Monitor expiry counterexample above blocks this head despite passing local suites and canary.
| except ValueError: | ||
| if parsed.tzinfo is None: | ||
| return parsed.replace(tzinfo=timezone.utc) | ||
| return parsed.astimezone(timezone.utc) |
There was a problem hiding this comment.
[P2] Align the typed expiry codec before treating UTC overflow as missing time
A non-watch-only Monitor is configured with expires_at="0001-01-01T00:00:00+14:00" (also reproduced at the upper boundary), no resume condition, and an ordinary due time. The typed plan and public update commit the out-of-range expiry. The new Python None result is interpreted as absent expiry: readback succeeds, monitor_todo_is_expired=false and monitor_todo_is_due=true despite watch_only=false. Typed validation and retained-reader time meaning diverge, so the required expiry does not bound that consumer.
Extend the existing TS parseTodoTimestampMicros owner to reject UTC results outside the retained 0001..9999 range before the monitor update commits. Keep the Python exception normalization, and cover typed planning plus public update/no-write rejection and retained-reader expiry/due parity; do not introduce a second expiry owner.
Regression: Run todo.monitor_metadata.plan and public update_goal_todo with both extreme offset expiry values and watch_only=false/no resume: expect normal timestamp rejection without state changes. Verify legal boundaries, corrected expiry readback, and identical Python/TS expiry/due meaning.
Signed-off-by: mika <211269698+mikamikasuki@users.noreply.github.com>
… UTC - Reject UTC results outside the retained 0001..9999 range in parseTodoTimestampMicros - Align typed expiry validation with Python datetime overflow handling - Add test coverage for extreme UTC offset values and legal bounds Signed-off-by: mikamikasuki <211269698+mikamikasuki@users.noreply.github.com>
55e1967 to
2d5432a
Compare
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
动机
通过 Todo 或 CLI 配置持续监视到期时间的用户,以及读取这些时间的调度与状态页面。
带时区的极端年份看似合法,但换算到 UTC 后可能超出可表示范围;旧写入端接受它,随后读取任务列表或判断到期时可能崩溃。
当前提交在写入前拒绝越界时间,保留原任务;合法最早和最晚时间仍能保存、读回并正确停止或继续监视。
本次只修复现有时间编解码边界,不增加监视策略、权限、配置步骤或新的用户界面。
改动思路
在既有 Python 读取 codec 与 TypeScript 元数据 codec 内统一 UTC 可表示范围,能修复真实写入与读取的不一致;另建解析器、迁移历史状态或扩大 Python 时间域均没有必要。
当前 PR 应交付无异常读取、拒绝无效写入且不部分提交,以及合法边界和原有 ISO 语法兼容;不把这个输入修复扩展为监视器或时间系统重构。
Python 的 try 必须覆盖时区换算,而不只是字符串解析。TypeScript 也须在实际 UTC 微秒值上做同样的下限包含、上限不包含判断,否则只修读取端会让无效到期时间看起来像没有边界。合法日期、微秒和时区秒精度继续由原 codec 处理,通用 Date 解析函数没有被替换。
具体改动
关键代码讲解
完整审阅 2d5432a,实际合并基线 5706ef0,四文件 +25/-6。另对照上次55e196的 finding 与修复增量,没有继承旧 verdict。
- parse_timestamp 将 astimezone 放入既有异常边界。无法表示的 UTC 时刻返回 None,调度、状态和缓存读取各自沿既有错误或 miss 路径处理。
- parseTodoTimestampMicros 在日期和带时间两条返回路径检查最终 BigInt UTC 值,保留0001年首微秒至9999年最后一微秒;不是按本地年份或 JS Date 的较大范围猜测有效性。
- timestampOrNull / normalizeMetadata 是未改动的实际消费者:typed plan 拒绝后,公开更新不会提交同时附带的任务文字或 watch_only 修改。它仍不把配置当作监视观察回执。
规范为 https://github.com/loopx-project/loopx/blob/98709770d80e9c332f3c07d2b545226a6dd17521/docs/reference/monitor-configuration.md,spec_revision=98709770d80e9c332f3c07d2b545226a6dd17521,以及 #6081。逐条核对:retained boundedness 已实现,越界 expiry 拒绝且完整权威状态不变;configuration is not observation 已实现,原观察字段、重放和失败恢复语义保留;UTC overflow input 已实现,实际读取边界返回 None 而不抛异常。这些要求来自原合同和缺陷,不是为补丁新造验收。
对主干的风险
关键风险是修复崩溃时反而接受“没有有效到期边界”的监视,或把合法边界误拒绝。本轮用同一组独立输入在基线与新头执行:16项 codec 加10项真实 File/SQLite 写入和后续调度,总计基线18/26、新头26/26。两种越界偏移均在提交前拒绝,完整 authority 不变;合法最早、最晚 UTC 和普通过期值都能经真实列表读回并给出正确 expired/due。旧头的 Python-only 修复不再构成当前 blocker。
状态缓存也通过真实生产 status 生成带有效 projection envelope 的 payload,再写入和冷读取:合法记录在两端都命中;两种越界 generated_at 在基线抛 OverflowError、新头返回 missing_generated_at miss;合法旧日期仍为 expired miss,缓存文件均未被读取过程改写。四项基线2/4、新头4/4,补齐了上轮暖缓存 fixture 不足以证明命中的证据缺口。
本轮85项 Python、21项 TypeScript 检查通过;对已核实目标 main 运行 source premerge,6项直接检查和17项选定检查通过,两个提交都有 DCO。起初一次 pytest 路径拼错,退出4,按真实文件名修正后执行了上述85项;不把它算成产品失败。没有查询或等待 CI。验证范围为本机 Python3.12 与实际源码 runtime,不宣称其他平台已安装验收。
语义与 CI 对齐
沿用现有时间输入、结构化元数据、canonical writer 和错误通道;不新增状态名、权限或通用工作义务。这个边界没有 opt-in 开关,因而用合法/非法输入及实际缓存命中作为相反方向对照。没有改变 Agent 指令、调度频率或用户确认步骤,也不借此迁移历史损坏的 Monitor 数据。
我的整体评价
APPROVE。这是在现有两个语言边界内完成的、有真实消费者支持的修复。long_horizon 与 user_experience 在受影响的配置→读回→到期及缓存恢复路径上改善;没有测量更广泛的长程净收益。改动规模与问题相称,无需额外解析框架或当前必须拆出的重构。审批结论限于此 unchanged head,合并与既有阻塞意见的处理仍遵守独立权限和 readiness。
English verdict: APPROVE at 2d5432a. UTC-overflow handling now agrees across the Python reader and typed Monitor writer. Actual File/SQLite no-partial-write and scheduling controls pass, as do valid-envelope cache hit/miss checks; 85 Python tests,21 TypeScript tests and native source premerge pass. No CI or broader installed-platform qualification is claimed.
Goal And Delivered Outcome
Author Declaration
Implemented against
Scope And Continuation
Validation
Frontend / Visual Evidence
Type of Change
LoopX Area
Technical Direction
Shared-authority RFC fixture impact
Boundary Checklist