Skip to content

fix(chat): reject non-string queued input actions - #8112

Merged
huangruiteng merged 1 commit into
mainfrom
codex/queued-input-validation-20261105
Nov 6, 2026
Merged

huangruiteng merged 1 commit into
mainfrom
codex/queued-input-validation-20261105

Conversation

@loopx-agent

Copy link
Copy Markdown
Collaborator

Goal And Delivered Outcome

Fix a reproduced input-validation defect in the queue controls introduced by #8096. On base 12803354ab13a003de8620ab496250617224ff37, JSON action: ["edit"] and ["cancel"] pass stringified membership checks and select the cancellation branch. Both requests and persisted operation receipts now require the original "edit" or "cancel" literal.

Before → after: the same real HTTP counterexample changes a queued Turn to interrupted on the base; the fix returns the existing HTTP 409 refusal with Turn, transcript, events, Session and native I/O unchanged. Legal edit/cancel, original-operation replay, partial-write recovery and both queue-claim orderings remain covered.

Author Declaration

  • Written by: model_agent — OpenAI Codex (GPT-6 family).
  • Specification: this reproduced-defect request, against the existing queue planner and frontend literal schema at base 12803354ab13a003de8620ab496250617224ff37; intended base: main.
Criterion Disposition Owner / decisive check
Only original action literals can select a queue mutation implemented chat_queue_steering.ts; malformed requests through real HTTP on File/SQLite Goal sources
Pending and committed operation receipts reject the same malformed actions implemented Existing receipt decoder; planner and queue-selector negative cases
Preserve legal operations and recovery implemented Existing edit/cancel/replay, partial-write, attachment and queue-race tests

Scope And Continuation

Complete within the validation scope: two guards in the existing TypeScript owner, regressions in existing suites, and a reusable repair-pattern entry. No new configuration, vocabulary or validation framework. The existing frontend already sends literal strings; its controls and first viewport are unchanged.

Draft restoration after a queue entry starts remains an independent gap in the existing chat product work. This PR neither implements that recovery nor claims completion of the wider queue UI journey. The validation fix is independently testable and reversible. Existing malformed historical receipts are preserved, not coerced or rewritten.

Validation

  • Tested revision: ce0cff2abeb88c71a4ddba71e0f5ecd47718928f (source tests executed on these identical bytes before commit; wheel owner bytes compared with the commit).
  • Run state: finished (required premerge failures remain).
  • Input classes: synthetic, public_fixture.
Check kind Result Evidence / limitation
regression_parity passed New literal-action cases fail on the unmodified base through both TypeScript and real HTTP, then pass on the fix; 10 malformed JSON values covered
real_entrypoint / real_backend passed python -m pytest -q tests/presentation/test_chat_turn_queue.py tests/test_chat_queue_preparation.py: 68 passed, isolated real filesystem and SQLite 3.53.4 Goal sources (Node.js 24.21.0), real HTTP server and scripted native stdio
real_entrypoint passed Built and isolated-installed wheel: all 25 queue HTTP tests pass, including refusals and recovery; packaged owner bytes match the commit
unit passed tests/control_plane_ts/chat_queue_steering.test.ts: 6 passed
unit passed npm run test:control-plane: 6,122 passed, 77 skipped; zero failures. Skips remain unqualified.
static passed Configured Ruff and mypy, control-plane typecheck, semantic vocabulary check/advisory, RFC-pair check, diff hygiene, four-file public-boundary scan
integration failed Standard premerge: 5 direct checks pass; 13 smokes execute, with 2 required failures and 1 inherited maintainability advisory

The required failures are autonomous-replan-obligation-smoke.py (missing autonomous_replan_obligation) and refresh-state-write-correctness-smoke.py (missing interaction_contract). Separate clean immutable-base and candidate runs reproduce identical full diagnostics after repository-path normalization. Premerge and change-quality remain blocked; the comparison does not waive either failure. Independent exact-head review and maintainer merge authority remain outstanding.

The wheel build initially refused stale generated assets; rebuilding through the existing build:chat command restored package qualification. No installed App, live model/Host adoption, Windows or Lark qualification is claimed. No UI presentation changed; visual evidence is N/A.

Boundary Checklist

  • Public-safe diff and description; no private state, credentials, raw traces or local paths.
  • One cohesive bug fix; existing tests and owners reused.
  • No RFC pair changed; staged bilingual check passed.
  • UI impact: none.
  • DCO sign-off present.

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; effort=max

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

动机 / Motivation

普通 Chat 用户编辑或取消尚未开始的排队消息。当客户端误发 action: ["edit"] 时,基线会接受请求并取消消息;补丁拒绝请求且保留原输入,随后合法编辑仍能继续。已在真实 HTTP、File/SQLite Goal 数据源上验证拒绝、完整状态保留及后续合法编辑。本次不改变队列 UI、原生模型行为、权限范围或已开始消息的草稿恢复。

这是会丢失排队执行意图的实际错误。我用同一脚本在不可变基线和当前 head 发送相同 JSON,确认问题发生在服务端,不依赖浏览器能否构造异常值。修复的可用结果是保留请求供用户纠正,未要求额外配置或确认。

改动思路 / Approach

原始 JSON 类型应在现有 TypeScript 决策 owner 内校验;两个直接 literal 判断即可同时保护请求与回执,另加 Python 校验器或协议版本会重复规则。当前 PR 只修复 action 类型强制转换,并在既有套件和按需修复索引中保留回归证据。

HTTP 接口仍先检查 Session 的来源权限,Python store 仍持有 Session/Turn 锁,随后调用现有 typed planner。只有合法计划才写入待完成回执、原输入、事件和最终回执。补丁在两个原有校验点直接比较原始值,没有增加旁路、缓存、协议版本或执行权限。前端已使用 literal union;仅修前端无法保护直接 HTTP 调用和历史回执,因此需要同时修这两个既有 owner 入口。

具体改动 / Changes

关键代码讲解

  • mutation 同时服务于 pending mutation、committed operations 和队列选择。原来的 String(action) 会把数组伪装成合法动作;现在保留原 JSON 类型,异常回执被拒绝,不被自动转换或改写。
  • planQueuedChatMutation 在选择编辑/取消分支之前拒绝异常类型。来源、revision、operation identity、已开始 Turn 和原生 steering reservation 的既有检查顺序不变。

完整差异为四个文件:两处 production 条件替换,既有 TypeScript/HTTP 套件增加请求及回执回归,以及按需修复索引的一行诊断规则。未发现这项独立缺陷已有书面规范;判断依据是基线 12803354ab13a003de8620ab496250617224ff37 的前端 action literal schema、原有队列语义及独立复现,未把作者声明当作规范。现有 edit/cancel 词汇和持久化格式被复用。

对照 基线 → 当前 head
三类数组动作 × File/SQLite Goal 来源 6 次 HTTP200 并把 queued 改成 interrupted → 6 次409,完整状态不变
其余 9 类异常输入 × 两种来源 18 次既有拒绝结果完全一致;当前全部24例随后合法编辑成功
pending/committed/selector 回执矩阵 33 个观察中9个数组位置改为拒绝,其余24个结果或错误一致
正常 HTTP、恢复及领取竞争 基线和 head 各68项通过

同一 HTTP 脚本 SHA-256 为 5f9a6a05e9fa8a4589ab655adbc47a654a5c15b9e003be34a9cc084cc7bbfe25。比较只规范化响应里生成的 Session/Turn ID,未删除状态、动作、错误或 revision;每个拒绝用例直接比较完整 Turn、Session、transcript、events 和 native IO 快照。

对主干的风险 / Risks

最强反例是校验变严后破坏合法历史回执、原操作重试或部分写入恢复。两版真实 HTTP 套件均覆盖这些路径,包括两种领取顺序、撤回授权、其他 Session、附件与既有原生 stdio 流程;本次 TypeScript6项测试和额外回执矩阵也通过。原生进程使用脚本夹具,未据此声称真实模型行为已验证。服务端仍返回既有通用错误文本;旧的非法数组回执会 fail closed,证据保持原样,不能把拒绝本身宣称为修复历史数据。

控制面 typecheck、配置内17文件 mypy、变更 Python 的 Ruff、四文件公开边界扫描和 diff hygiene 通过。完整 TypeScript 控制面套件实际执行:6,122 passed、77 skipped、0 failed;跳过项不冒充通过。不查询或等待 CI,不把未咨询的远端检查说成绿色。

当前 quick premerge FAIL:5项直接检查通过,语义词汇检查通过;autonomous-replan-obligation-smoke.py 在未改动的第290行因缺少 autonomous_replan_obligation 失败,维护性检查保留既有 _admitted_interaction_next_cli_actions advisory。两项独立基线/head执行的完整失败诊断一致:replan只规范化 checkout 路径(SHA-256 80e3815eee76484bce0a05917e579cbb3570ea16316e7cb05141fce85d7907db),ratchet原始输出逐字相同。CQR cqr_d938d62636933a75e58d 对当前四文件 scope 验证有效。这些比较用于归因,premerge 仍未通过,不能据此合并。

我的整体评价 / Verdict

未发现阻断性问题。这个独立缺陷的目标已达成:长程工作方面,错误输入不再取消待执行承诺,合法重试和恢复保持可用;用户体验方面,保留原输入且不增加任何必需的人机交互轮次。检查了 conversation-queue.tsx 与 data/chat.ts 的既有编辑、取消、错误恢复调用;本次无渲染或布局改动。未验证安装 App、Windows 或真实模型;这些不属于本次类型修复的已验证范围。

原始 JSON 类型应在现有 TypeScript 决策 owner 内校验;两个直接 literal 判断即可同时保护请求与回执,另加 Python 校验器或协议版本会重复规则。当前 PR 只修复 action 类型强制转换,并在既有套件和按需修复索引中保留回归证据。进一步抽出新的 schema/helper 没有可见收益,当前直接控制流已经足够。APPROVE 是此 head 的代码评审结论;保留所有仓库验证、冲突、独立评审和维护者授权的合并门。

English verdict: APPROVE — ce0cff2; literal-action validation fixes reproduced unintended cancellation while preserving legal queue recovery. Merge qualification is separate.

@huangruiteng
huangruiteng merged commit e8dc9e1 into main Nov 6, 2026
32 of 45 checks passed
@huangruiteng
huangruiteng deleted the codex/queued-input-validation-20261105 branch November 6, 2026 04:25
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.

2 participants