feat(session-mechanism): 图模式补全自动化测试并接入技能入口
- graph/test_graphctl.py:新增 49 个用例,覆盖建图 / 校验硬错与警告 / 就绪集 / 状态流转 / 非法操作 / 边界 / 写入范围隔离;实跑 Ran 49 tests OK(27.5s) - graph/graphctl.py:修测试暴露的问题(+50/-10) - graph/README.md:补「怎么跑测试」一节(+27 行) - SKILL.md:目录树与入口指针登记 graph/(新增 4 行文档,机制行为零改动) - 隔离性核对:collabd.py / goalctl.py / 两个看板 / 全部钩子 diff 为空; state.py 闸门读数绿、常驻心跳 7 秒、看板 20100 在听
This commit is contained in:
1 parent
41752fd1f3
commit
4866ad8d37
4 files changed
+601
-10
No files matched your search
@@ -42,7 +42,15 @@
|
||||
## 就绪的判据(四条全中)
|
||||
|
||||
① 依赖全部 done ② 自己还是 pending
|
||||
③ 域不与任何 running 节点重叠 ④ 不超 max_parallel
|
||||
③ 域不与任何 running 节点重叠,也不与本批已选节点重叠
|
||||
④ 不超 max_parallel(running 占名额)
|
||||
|
||||
## 状态的走法
|
||||
|
||||
pending --start--> running --report done--> done
|
||||
|--report blocked--> blocked --start--> running(重试,attempts 已累加)
|
||||
|
||||
`start` 只拒 `running`(已在跑)与 `done`(已完成);`blocked` 可重开。
|
||||
|
||||
## 域的用法
|
||||
|
||||
@@ -174,7 +182,8 @@ def cmd_check(a) -> int:
|
||||
if d not in nodes:
|
||||
errs.append("%s 的依赖 %s 不存在" % (nid, d))
|
||||
if not _norm_dom(n.get("domain")):
|
||||
warns.append("%s 没有声明域 ⇒ 只能串行,不能与其他节点并行" % nid)
|
||||
warns.append("%s 没有声明域 ⇒ 域比较视为不冲突,可与任意节点同批;"
|
||||
"保守做法是让它单独跑" % nid)
|
||||
if str(n.get("status")) not in STATUSES:
|
||||
errs.append("%s 的状态非法:%r" % (nid, n.get("status")))
|
||||
if not (n.get("accepts") or []):
|
||||
@@ -220,12 +229,24 @@ def _ready(g: dict) -> dict:
|
||||
ready.append(nid)
|
||||
|
||||
slots = max(0, cap - len(running))
|
||||
picked = ready[:slots]
|
||||
for nid in ready[slots:]:
|
||||
waiting.append({"node": nid, "why": "并发上限 %d 已占满" % cap})
|
||||
picked, backlog = [], []
|
||||
for nid in ready:
|
||||
if len(picked) >= slots:
|
||||
backlog.append(nid)
|
||||
waiting.append({"node": nid, "why": "并发上限 %d 已占满" % cap})
|
||||
continue
|
||||
dom = _norm_dom((nodes.get(nid) or {}).get("domain"))
|
||||
clash = [p for p in picked
|
||||
if dom and _norm_dom((nodes.get(p) or {}).get("domain"))
|
||||
and _dom_overlap(dom, _norm_dom((nodes.get(p) or {}).get("domain")))]
|
||||
if clash:
|
||||
# 同批也不能撞域:check 已警告「永不同批」,ready 必须兑现
|
||||
waiting.append({"node": nid, "why": "域与本批已选节点重叠:%s" % ", ".join(clash)})
|
||||
continue
|
||||
picked.append(nid)
|
||||
|
||||
return {"gid": g.get("gid"), "running": running, "ready": picked,
|
||||
"backlog": [x for x in ready[slots:]], "waiting": waiting,
|
||||
"backlog": backlog, "waiting": waiting,
|
||||
"cap": cap, "slots": slots}
|
||||
|
||||
|
||||
@@ -241,8 +262,11 @@ def cmd_start(a) -> int:
|
||||
n = (g.get("nodes") or {}).get(a.node)
|
||||
if not n:
|
||||
raise SystemExit("没有这个节点:%s" % a.node)
|
||||
if str(n.get("status")) != "pending":
|
||||
raise SystemExit("节点 %s 当前是 %s,不能开工" % (a.node, n.get("status")))
|
||||
if str(n.get("status")) == "running":
|
||||
raise SystemExit("节点 %s 已在跑,不能重复开工" % a.node)
|
||||
if str(n.get("status")) == "done":
|
||||
raise SystemExit("节点 %s 已完成,不能开工" % a.node)
|
||||
# pending 与 blocked 都可开工:blocked 是「这轮没过」,重试时 attempts 保留
|
||||
n["status"] = "running"
|
||||
_save(a.ws, a.gid, g)
|
||||
_event(a.ws, a.gid, {"node": a.node, "to": "running", "by": a.by or ""})
|
||||
@@ -293,7 +317,15 @@ def cmd_init(a) -> int:
|
||||
|
||||
nodes = {}
|
||||
for i, n in enumerate(spec.get("nodes") or []):
|
||||
nid = str(n.get("id") or "N%d" % (i + 1))
|
||||
raw = n.get("id")
|
||||
nid = str(raw).strip() if raw not in (None, "") else ""
|
||||
if not nid:
|
||||
k = i + 1
|
||||
while ("N%d" % k) in nodes: # 自动编号要避开已占用的 id
|
||||
k += 1
|
||||
nid = "N%d" % k
|
||||
if nid in nodes: # 显式重复 ⇒ 硬错,⛔ 不静默覆盖前一个
|
||||
raise SystemExit("节点 id 重复:%s" % nid)
|
||||
nodes[nid] = {
|
||||
"title": str(n.get("title") or nid),
|
||||
"deps": [str(x) for x in (n.get("deps") or [])],
|
||||
|
||||
Reference in new issue
Block a user