fix(consult): accept a calculation that succeeds after transient failures
Independent Staging Quality Gate / validate (push) Failing after 12m9s
Independent Staging Quality Gate / publish (push) Has been skipped

The runtime contract gated on total consultation tool attempts, so a single
transient calculation failure raised the counter past one and made the run
unrecoverable even after a later attempt succeeded. The already-computed chart
was discarded and the request ended as runtime_contract_incomplete.

Count only successful workflow executions against the single-calculation
boundary. Failed attempts stay in the attempt counter for observability. The
request-scoped cache still retains the successful promise, so a request can
never run more than one billable calculation.

Also record the staging-first branch delivery convention in AGENTS.md.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-08-17 12:12:48 +08:00
co-authored by Cursor
parent bced1b9d57
commit 7886629b36
5 changed files with 100 additions and 9 deletions
+19
View File
@@ -3583,3 +3583,22 @@
- 防复发:对启用 RLS 的管理资源,table grant 与 RLS policy 必须成对验证;后台列表测试必须使用 `admin_runtime` 真实角色,不能只用 schema owner 绕过 RLS。
- 相关记录:BUG-156、BUG-209
- 修复版本:本地未提交候选
## BUG-214 | 计算重试成功后仍误报运行合同未完成并丢弃已算出的星盘
- 状态:resolved(本地修复,未提交、未发布)
- 首次发现:2026-08-17
- 最近更新:2026-08-17
- 影响面:`/api/consult` 个人咨询在 `run-jyotish-consultation` 出现任何一次瞬时失败后的全部后续行为。
- 用户现象:staging 事业类咨询运行约一分钟后返回 `run.failed` / `runtime_contract_incomplete`,提示“Agent 未完成必要的方法与计算步骤,本次不会扣点”。事件流显示 Skill 已加载、星盘实际已计算成功,用户却拿不到任何回答文本。
- 触发条件:`run-jyotish-consultation` 首次调用失败,模型据此重试,并在后续某次调用中成功。观测到的事件序列为两次 `tool.failed calculation_failed`、一次 `tool.completed`(20278ms),随后补跑命中缓存再次 `tool.completed`55ms)。
- 根因:合同门禁 `contractReady()` 要求 `consultationToolCallCount === 1`,而该计数在 `createConsultationTools()` 中对每次未命中缓存的执行递增,失败尝试同样计入。BUG-205 的修复让被拒绝的计算缓存可以释放、从而允许重试,但门禁仍按总尝试次数判定,导致只要发生一次瞬时失败,计数就永久大于 1,之后无论计算是否成功都不可能满足合同。服务端补跑无法降低计数,因此纯属浪费,最终以 `runtime_contract_incomplete` 结束并丢弃已经算出的结果。
- 修复:新增 `consultationToolSuccessCount`,仅在工作流真正成功时递增;门禁改为判定成功次数为 1。失败尝试继续记入 `consultationToolCallCount` 供观测使用,但不再影响合同。未放宽单次计算边界:请求级缓存保留成功 Promise,后续调用一律复用,因此每个请求仍最多执行一次计费计算;两次真实成功计算依然判定为违约。
- 验证:新增修复前失败的回归,复现“两次失败 + 一次成功 + 补跑命中缓存”序列并断言 `run.completed` 且回答正常输出;新增“两次成功仍然违约”边界回归以锁定单次计算约束;工具层补充断言失败重试后 `consultationToolCallCount=2``consultationToolSuccessCount=1`。回退门禁到旧实现可确认新回归失败。`frontend/tests/consultation-agentic-runtime.test.ts` 17/17 通过。
- 防复发:运行合同门禁只能依据成功语义的计数,不得用包含失败尝试的总调用次数;任何允许重试的缓存改动,必须同步检查下游门禁是否仍按尝试次数判定。合同类回归必须覆盖“失败后恢复”与“重复成功”两个方向。
- 相关记录:BUG-205、BUG-186、BUG-189
- 修复版本:本地未提交候选
### 待跟进
前两次 `calculation_failed` 的服务端原因尚未定位,需要 Python API 日志确认(工作流超时为 90s,两次失败均在 20s 内,可排除超时)。本条修复只保证瞬时失败可恢复,不替代对失败本身的排查。