Skip to content

fix(automation): reject stale cadence confirmations - #6201

Merged
huangruiteng merged 1 commit into
loopx-project:mainfrom
Duang777:codex/fix-cadence-stale-confirmation-20261011
Oct 11, 2026
Merged

huangruiteng merged 1 commit into
loopx-project:mainfrom
Duang777:codex/fix-cadence-stale-confirmation-20261011

Conversation

@Duang777

Copy link
Copy Markdown
Collaborator

Goal And Delivered Outcome

  • Outcome basis / optional anchor: deterministic race reproduction against main@8d5a6fc17; no public issue exists for this defect.
  • Goal/source and gap: confirmAutomationStart used a pre-lock reservation hit to enter the mutation path, but its locked read reported success even when that request had been replaced.
  • Observable before → after, with the validation row that proves it: the stale request previously returned confirmed: true and could start a second managed Turn; it now returns confirmed: false with reservation_missing and leaves the replacement reservation unchanged.
  • Issue/task and intended base: self-contained bug fix for main.

Author Declaration

  • Written by: model_agent (OpenAI Codex), reviewed and submitted by the repository contributor.

Implemented against

  • Specification and revision: no written specification; the reproduced request-replacement race in this PR is the basis.
  • Criteria:
Criterion (spec clause) Disposition Symbol / path Test or command
The locked cadence store is authoritative for confirmation identity implemented confirmAutomationStart node --test tests/control_plane_ts/automation_cadence.test.ts
A missing locked request fails closed without mutating its replacement implemented record in automation_cadence.ts confirmation rejects a reservation replaced before its locked read
An exact already-started request remains idempotent implemented confirmAutomationStart existing confirmation replay test
  • Self-check before submission: read the TypeScript cadence owner, Python managed-Turn consumer, and host-start ordering; ran red/green regression parity, targeted and full control-plane tests, Python integration tests, type checking, diff checks, and a same-shape lock/read scan. No UI, generated files, dependency locks, or unrelated refactors are included.

Scope And Continuation

  • Completed scope and remaining work: complete within this scope. Confirmation now distinguishes an exact replay from a request that disappeared before the locked read.
  • Slice boundary / successor: N/A; other audit findings are separate behaviors and are not required for this fix.

Validation

  • Tested revision: f53858e9e8d2bbe0365dc2dada9914dc50c36280
  • Run state: finished
  • Input classes: synthetic
Check kind Result Public-safe evidence / limitation
regression_parity passed New race test failed on the unfixed tree with true !== false, then passed after the locked missing-request branch was added.
unit passed node --test tests/control_plane_ts/automation_cadence.test.ts: 10 passed, including replacement preservation and exact replay.
integration passed Three focused tests/test_loopx_turn_executor.py managed-start tests passed through the real Python adapter and TypeScript cadence store.
static passed npm run typecheck:control-plane completed successfully.
unit passed npm run test:control-plane: 4,396 passed, 37 skipped, 0 failed.
  • Coverage and gaps: the regression controls the exact stale-snapshot/locked-read ordering and inspects the persisted replacement row. Existing tests cover exact confirmation replay and Python host-start ordering. No real backend is involved; the affected owner is the local file-backed cadence store.

Frontend / Visual Evidence

  • UI impact: none
  • Before: N/A
  • After: N/A
  • States and viewports shown: N/A
  • Source data: none
  • Attention review: N/A; no visual surface changed.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Refactoring (no functional changes)
  • Documentation update
  • Test update

LoopX Area

  • Control plane (goals, todos, quota, scheduler, registry, runtime)
  • Benchmark boundary (adapters, runners, verifiers, scoring, evidence)
  • Capability or extension (providers, adapters, skills)
  • Public docs or presentation surface (README, protocols, dashboard)
  • Build, packaging, installer, or CI
  • Host or runtime integration

Technical Direction

  • Direction / acceptance reference, when applicable: Core control-plane hardening.

Shared-authority RFC fixture impact

N/A. This change does not modify shared Goal Authority providers, schemas, routing, promotion, or compatibility projections.

Boundary Checklist

  • Neither the diff nor this PR body/comments/attachments disclose private state, credentials, raw traces or verifier output, internal links, or local machine paths (including .loopx/, .codex/goals/, and live ACTIVE_GOAL_STATE.md).
  • I did not duplicate maintainer-owned benchmark work unless a maintainer split out a public issue for it.
  • I kept the change scoped to the linked issue/task.
  • I completed the visual evidence section for UI changes, or marked UI impact none.
  • Every commit includes a DCO Signed-off-by trailer (git commit -s).

A confirmation could observe its reservation before taking the store lock, then find that a newer manual admission had replaced it. The locked path treated the missing request as an already-confirmed replay and allowed the stale Turn to start.

Make the locked store authoritative: only a matching started record is an idempotent confirmation, while a missing request fails closed. Cover the replacement interleaving with a deterministic regression test.

Signed-off-by: Duang777 <duangjl007@gmail.com>
@Duang777

Copy link
Copy Markdown
Collaborator Author

@loopx-agent review

@loopx-agent loopx-agent left a comment

Copy link
Copy Markdown
Collaborator

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; xhigh

Exact head: f53858e9e8d2bbe0365dc2dada9914dc50c36280; immutable base: 8d5a6fc170422ff4ff78818898b172064b1e1591. 全量2文件 +39/-0,未查询、轮询或等待CI。

动机

使用受最小调度间隔约束的 managed Turn(受管执行轮次)的操作者。 旧轮次先读到自己的预约,再等写锁时预约被新轮次替换;旧版本仍报告确认成功,新版本明确拒绝旧请求并保留新预约。 真实文件锁和实际 owner 的两次读取复现了旧版 confirmed=true、新版 confirmed=false/reservation_missing;新预约字节不变,合法确认和重放仍成功。 本 PR 只修复现有调度确认的竞态;不增加设置、权限、UI步骤或新状态格式,也不宣称所有宿主醒来前已阻止模型成本。

没有针对本竞态的书面spec或关联issue;先读变更前的预约/确认合同与managed调用方,再独立复现。已有缺请求时拒绝、合法相同请求可重放的语义是判断依据,未以新增测试输出定义正确行为。召回经验仅用于检查真实owner与后续调用者;不继承其他案例结论,不增加本修复无关的UI或付费模型前置。

改动思路

在已有锁内重新核对同一 Agent/request 的匹配集合即可修复误确认;无需第二调度器、Python规则或额外配置开关。 本 PR 只修复现有调度确认的竞态;不增加设置、权限、UI步骤或新状态格式,也不宣称所有宿主醒来前已阻止模型成本。

仍由 TypeScript cadence owner 决定准入,Python只发送已准入请求身份并检查 confirmed。确认观察成功的 ok:true 不等于确认获准:confirmed:false 会让既有调用方在启动host前抛出错误。没有复制Python规则或新增状态/配置。相邻未来维护检查覆盖快读与锁内读:它们已在同一个函数,四行修复足够,无需额外抽象。

具体改动

  • confirmAutomationStart 的锁内 record 先检查同一Agent/request匹配集合;为空直接返回既有 reservation_missing,在任何 started 写入之前结束。
  • replacement regression 通过实际A/B预约与第一读的旧快照,覆盖原先false-success;检查新预约仍为reserved,并恢复builtin读钩子。两处生产/测试改动均完整读过。
  • 不变的 managed_cadence_start 保留request_id,拒绝false确认;现有journal恢复不受本补丁另造规则。

对主干的风险

独立反例通过真实文件、withFileMutationLock、实际load/decode及atomic writer安排“快读A→锁持有者写B→锁内读B”。读钩子只在真实读取完成后发屏障信号,返回原始实际字节,不伪造旧读取结果。B的fixture字节由真实admit owner生成,整个实验使用隔离合成Goal,未触碰活动Goal。

相同输入:base错误 true/already_confirmed,head正确 false/reservation_missing;B预约前后SHA一致,仍reserved。无配置/无文件、缺请求、外来Agent、精确确认与重放的全部响应相同;合法新请求随后 start_confirmed,重放 already_confirmed 且无字节写入。旧9个TS测试全部绿仍会漏掉此反例,因此并非只跑新增测试。

当前head 10 TS tests / 3 Python managed-start recovery tests通过;不可变base对应 9 TS / 3 Python通过。changed-source语义advisory先跑,0支持的新词表载体只表示扫描能力范围;完整semantic、typecheck、diff-check及当前native风险canary(3个直接检查、16个风险检查,0失败/警告/人工hold)通过。legacy store decode、90分钟floor、相同request重放、自动化scope与其它Agent拒绝保留。没有新frontend旅程或现有控制变化需要补UI;实际付费host、长期并发频率和fleet成本未测,不以合成consumer callback冒充真实模型执行。

我的整体评价

APPROVE。 当前有界身份修复有正向价值:关闭被替换旧请求的误确认路径,避免错误后续host尝试;新请求和合法恢复继续可用。增加一次已有锁内数组长度检查,没有额外IO/锁/状态或用户操作,因此机制成本很小。长期重复执行的可靠性预期正向;未测净延迟、token或收益比例,不能宣称全体Agent的长程效率已验证。合并仍由维护者执行。

English verdict: APPROVE - f53858e. An independent real-file/real-lock base/head counterexample reproduces false confirmation after request replacement and verifies the corrected refusal with unchanged replacement bytes. All non-race outputs and effects, default-off no-write behavior, exact replay and recovery are preserved; head10/base9 TS tests and3 Python managed-start tests per revision, typecheck/semantic/risk checks are recorded. No CI, paid host execution or fleet utility claim; maintainer merge remains separate.

@huangruiteng
huangruiteng merged commit a4c072f into loopx-project:main Oct 11, 2026
11 checks passed
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.

3 participants