session-mechanism:闸④ 不再被周期排期卡死 + --report 漏 id 当场拒收
① 闸④ 收窄 recurring(用户选 A 案)
改 ws_pending_schedules():schedule_type='recurring' 的行一律不算「待执行」,
once 的过期排除与拦截照旧。
症状=周期排期永远有下一次 ⇒ 拿「在册 + 没跑过」判它恒真 ⇒ 闸④ 恒拦
⇒ 目标检查会话一条都建不出来(aigc-idea-impression 被「AI变现日报 每日 06:00」
挡了一整天,日志每 20 秒重复「还有 1 条待执行排期 ⇒ 不建」)。
变异对照:同一工作区、同一时刻,旧版返回 ['AI变现日报 · 每日自动生成']、新版返回 []。
② --report 拒收闸(用户选 B 案)
--report 后面必须跟真的需求 id;id 为空或以 -- 开头 ⇒ 打用法行 + 退出码 2,台账一字不写。
症状=漏 id 时 _arg("--report") 取到的是下一个 token(--state)
⇒ 台账多一条键名写坏的记录,还被算进队列计数(本区 queue_n 2→3;vibe-product 也有一条)。
变异对照:把闸删掉 ⇒ 同一条命令 rc=0 且真写出 "--state";加回去 ⇒ rc=2 且台账为空。
自检:闸④ 用例 5 → 6 项;新增「拒收闸」用例 6 项(含正例对照,防「未找到部署配置 ⇒ 拒跑」
把 rc=2 混成假绿);全量 PASS 110 / FAIL 0。
文档:references/pitfalls.md —— P0-65 补 recurring 口径(含实测症状与反例库);
新增 P0-96(漏写 --report id 的静默污染 + 判据自身两处假绿坑)。
This commit is contained in:
1 parent
e12e985808
commit
24040ee171
3 files changed
+156
-3
No files matched your search
@@ -3891,6 +3891,79 @@ def t_check_prompt_no_wild_hunt():
|
||||
|
||||
|
||||
|
||||
@case("🔴 `--report` 拒收闸:漏写需求 id ⇒ 报错退出 + ⛔ 不写台账(2026-10-12 用户选 B 案)")
|
||||
def t_report_requires_id():
|
||||
"""🔴 2026-10-12 实测缺陷(本区 + `vibe-product` 都中过)。
|
||||
|
||||
🔴 **现象**:把收报写成 `--report --state done --artifact …`(**漏了 id**)⇒
|
||||
`_arg("--report")` 取到的是**下一个 token**(实为 `--state`)
|
||||
⇒ 台账里多出一条**键名写坏**的记录,而且它**被算进队列计数**
|
||||
(本区实测 `queue_n` 2 → 3;`vibe-product` 的 `q_sig` 里也有一条 `--state=done`)。
|
||||
🔴 **为什么必须拦**:这类污染是**静默**的 —— 命令看着像成功、台账看着像有记录;
|
||||
更糟的是它可能落在 `running`/`blocked` 态 ⇒ 闸① 误判「有活在跑」⇒ 去建结果检查会话。
|
||||
✅ **正解**=`--report` 分支加拒收闸:id 为空或**以 `--` 开头** ⇒ 打用法行 + 退出码 2,
|
||||
台账一个字都不写。
|
||||
|
||||
⚠️ 判据自身的坑(两个都得堵,否则**假绿**):
|
||||
① **正例探针⛔绝不能指向真工作区** —— 我 2026-10-12 09:43 就是这么往真台账里
|
||||
敲进一条 `ZZZ-假id`(`running`)的,只能手工撤回。⇒ 一律用临时工作区。
|
||||
② **临时工作区必须自带 `collabd.config.json`** —— 不然 `collabd.py` 会先
|
||||
「未找到部署配置 ⇒ 拒跑」并返回 **rc=2** ⇒ 负例断言**因为错的理由**变绿。
|
||||
⇒ 夹具里现造一份最小配置,并加一条**正例**(形态正确的 id ⇒ rc=0)当对照。
|
||||
"""
|
||||
import json as _j
|
||||
import os as _os
|
||||
import shutil as _sh
|
||||
import subprocess as _sp
|
||||
import tempfile as _tf
|
||||
import pathlib as _pl
|
||||
m = imp()
|
||||
_src = (HERE / "collabd.py").read_text(encoding="utf-8")
|
||||
_own = (HERE / "selftest.py").read_text(encoding="utf-8")
|
||||
has_guard = ('if (not _rid) or _rid.startswith("--"):' in _src
|
||||
and '✗ `--report` 后面必须跟' in _src)
|
||||
uses_tmp = 'env["COLLABD_WORKSPACE"] = str(tb)' in _own
|
||||
tb = _pl.Path(_tf.mkdtemp(prefix="dsh-report-id-"))
|
||||
try:
|
||||
cfg = tb / ".workbuddy" / "collab" / "collabd.config.json"
|
||||
cfg.parent.mkdir(parents=True, exist_ok=True)
|
||||
cfg.write_text(_j.dumps({"workspace": str(tb).replace("\\", "/"),
|
||||
"inbox": "tmp/supervise-inbox",
|
||||
"live": "交付物/实时状态.md",
|
||||
"singleton_port": 20999}, ensure_ascii=False),
|
||||
encoding="utf-8")
|
||||
(tb / "y.md").write_text("夹具产物", encoding="utf-8") # ⛔ 别让 artifact 缺失来抢答
|
||||
env = dict(_os.environ)
|
||||
env["COLLABD_WORKSPACE"] = str(tb)
|
||||
env["COLLABD_CONFIG"] = str(cfg)
|
||||
env["COLLABD_NO_ENSURE"] = "1"
|
||||
_cmd = [sys.executable, str(HERE / "collabd.py"), "--report"]
|
||||
# 正例(对照):形态正确的 id ⇒ 必须被受理,证明配置可用、确实走到了 --report 分支
|
||||
r_ok = _sp.run(_cmd + ["T-正常id", "--state", "running", "--by", "b"],
|
||||
cwd=str(tb), env=env, capture_output=True, text=True, timeout=120)
|
||||
# 负例:漏 id(=本轮真出事的写法)
|
||||
r_bad = _sp.run(_cmd + ["--state", "done", "--by", "b", "--artifact", "y.md"],
|
||||
cwd=str(tb), env=env, capture_output=True, text=True, timeout=120)
|
||||
rc_ok, rc = r_ok.returncode, r_bad.returncode
|
||||
out = (r_bad.stdout or "") + (r_bad.stderr or "")
|
||||
said = ("必须跟" in out) and ("需求 id" in out)
|
||||
led = list(tb.rglob("tasks.json"))
|
||||
blob = "".join(p.read_text(encoding="utf-8", errors="replace") for p in led)
|
||||
no_bad_key = '"--state"' not in blob
|
||||
wrote_ok = '"T-正常id"' in blob
|
||||
finally:
|
||||
_sh.rmtree(str(tb), ignore_errors=True)
|
||||
return [
|
||||
("① 源码里有拒收闸(id 为空或以 `--` 开头 ⇒ 拒收)", has_guard),
|
||||
("② 正例对照:形态正确的 id ⇒ 受理(rc=%s;⚠️ 拿掉它,③ 就可能因"
|
||||
"「未找到部署配置」而假绿)" % rc_ok, rc_ok == 0 and wrote_ok),
|
||||
("③ 漏写 id ⇒ **退出码 2**(实得 %s)" % rc, rc == 2),
|
||||
("④ 报错说清缺什么(含「需求 id」与正确用法)", said),
|
||||
("⑤ ⛔ 一个字都没写进台账(无坏键 `\"--state\"`)", no_bad_key),
|
||||
("⑥ 判据本身用**临时工作区**起子进程(变异:拿掉那行 ⇒ 本项变红)", uses_tmp),
|
||||
]
|
||||
|
||||
|
||||
@case("台账纪律:`done` 必须带产物文档 + `blocked` 必须带原因(否则检查会话只能去翻)")
|
||||
def t_done_requires_artifact():
|
||||
"""🔴 2026-10-03 10:5x(用户要求逐字):
|
||||
@@ -4355,11 +4428,14 @@ def t_gate4_stale_once():
|
||||
⇒ ⛔ 靠 `last_run_at` 收口**永远收不了**。
|
||||
✅ **正解**=排除「**已过期的一次性**排期」,宽限 **15 分钟**
|
||||
(宿主扫描周期 ≤30 s、实测到点延迟 20~35 s ⇒ 15 min 是安全余量)。
|
||||
⚠️ 四条边界(缺一条就误杀或漏杀):
|
||||
⚠️ 五条边界(缺一条就误杀或漏杀):
|
||||
① 刚过期 ⇒ **仍算待执行**(马上要开 ⇒ 闸④ 该拦)
|
||||
② 过期 >15 min ⇒ **排除**
|
||||
③ `next_run_at` 为 NULL ⇒ **仍算待执行**(⛔ 别误杀)
|
||||
④ `recurring` 过期 ⇒ **仍算待执行**(它会再触发)
|
||||
④ `recurring` ⇒ **一律排除**(2026-10-12 用户选 A 案)
|
||||
🔴 **2026-10-12 新增边界 ④(用户选 A 案)**:`recurring` 周期排期**不算"待执行"**。
|
||||
症状:周期排期永远有下一次 ⇒ 老口径下闸④ **恒拦** ⇒ 本区目标检查会话**一条都建不出来**
|
||||
(实测被「AI变现日报 每日 06:00」挡了一整天,日志每 20 秒重复「还有 1 条待执行排期 ⇒ 不建」)。
|
||||
"""
|
||||
import re as _re
|
||||
import time as _tm
|
||||
@@ -4383,6 +4459,12 @@ def t_gate4_stale_once():
|
||||
# ⇒ 我上一版写"NULL ⇒ 仍算待执行(别误杀)"**是错的** ⇒ 那样它照样永��卡闸④。
|
||||
# (第五种情形:(a) 已过宽限 / (b) 已被消费 ⇒ 两者都排除。)
|
||||
null_excluded = "if str(_st or \"\").lower() == \"once\":" in _c and "_gone = (not _na)" in _c
|
||||
# ⑥ 🔴 2026-10-12(用户选 A 案):`recurring` 一律排除 —— 周期排期永远有下一次,
|
||||
# 拿"在册+没跑过"判它恒真 ⇒ 闸④ 恒拦 ⇒ 目标检查会话一条都建不出来。
|
||||
recurring_excluded = (
|
||||
'if str(_st or "").lower() == "recurring":' in _c
|
||||
and "_rec.append(str(_nm))" in _c
|
||||
and "continue" in _c)
|
||||
# ⑤ 真库现读:造一条过期 once 也不能把**别人**的排期算进来(⛔ 只验判据形状,不动生产)
|
||||
live = m.ws_pending_schedules()
|
||||
return [
|
||||
@@ -4391,7 +4473,8 @@ def t_gate4_stale_once():
|
||||
% limit_min, 10 <= limit_min <= 30),
|
||||
("③ 只排 `once`(⛔ `recurring` 会再触发,永不按过期排除)", only_once),
|
||||
("④ `once` 且 `next_run_at` 为 NULL(已被宿主消费)⇒ **也排除**", null_excluded),
|
||||
("⑤ 判据现读不抛(ws_pending_schedules() = %s)" % (live if live else "[]"), True),
|
||||
("⑤ `recurring`(周期排期)**一律排除** ⇒ 闸④ 不再恒拦", recurring_excluded),
|
||||
("⑥ 判据现读不抛(ws_pending_schedules() = %s)" % (live if live else "[]"), True),
|
||||
]
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user