session-mechanism:加「锁根不得落进技能包」红线判据
按决策方法论自决(加判据属"实现路径",⛔ 不列入待拍板)。 新增 `session-rules-check.py::check_lockroot_in_pkg()`,注册进体检 A 组: 查 `<技能包>/05-交接单/` 与 `<技能包>/scripts/05-交接单/` 是否被创建 (三个根都查:脚本所在包根、全局技能根下的 session-mechanism、本区技能根下的同名目录)。 为什么是红线:`lock/handoff-guard.sh` 与 `lock/preflight-lock.sh` 的 `DOCS_ROOT` 原先回落到 `$_SM_HERE/..`(=技能包自己的 `scripts/`)⇒ 取不到文档库根时锁目录长在包里 (另一环境实测已出现 `<技能包>/scripts/05-交接单/.locks/`)。后果两条:污染只读发布物; 换工作区后锁跟错地方 ⇒ 域锁静默失效。 两处回落实已改为「取不到就报错 + 留痕 + 退出码 2」(9b839e7 / 22799fc);本条是兜底判据。 变异对照(⛔ 证明它抓得住、不是恒绿): · 常态 ⇒ `✓ [A] 锁根未落进技能包` · `mkdir -p <技能包>/scripts/05-交接单` 后 ⇒ `✗ [A] 锁根落进了技能包 ⇒ …` · `rmdir` 复原 ⇒ 回到 ✓ ⛔ 未做成 selftest 自动化用例:那要求真在技能包里造目录,用例中断会把 `05-交接单` 永久留在包里(不可逆污染)。⇒ 保留为判据 docstring 里可手工复现的步骤。 验收:语法 OK;全量自测 PASS 110 / FAIL 0。
This commit is contained in:
1 parent
9b839e7a02
commit
74b407ccba
1 file changed
+46
@@ -532,6 +532,51 @@ def _once_zombie(mine, ran, grace_min=30.0):
|
|||||||
return never, consumed, unver, in_grace
|
return never, consumed, unver, in_grace
|
||||||
|
|
||||||
|
|
||||||
|
def check_lockroot_in_pkg():
|
||||||
|
"""🔴 锁根**不得落进技能包**(2026-10-15 立 · 红线)。
|
||||||
|
|
||||||
|
为什么是红线:`lock/handoff-guard.sh` 与 `lock/preflight-lock.sh` 的 `DOCS_ROOT`
|
||||||
|
原先回落到 `$_SM_HERE/..`(=**技能包自己的 `scripts/`**)⇒ 取不到文档库根时,
|
||||||
|
锁目录直接长在技能包里(另一环境实测已出现 `<技能包>/scripts/05-交接单/.locks/`)。
|
||||||
|
后果两条:① 污染技能包(它是只读发布物);② 换工作区后锁跟着错地方 ⇒ **域锁静默失效**。
|
||||||
|
|
||||||
|
✅ 现行:两处回落实已改成「取不到就如实报错 + 留痕 + 退出码 2」;
|
||||||
|
本条是**兜底判据** —— 万一将来又有人改回"包内回落",这里要能报出来。
|
||||||
|
|
||||||
|
✅ 变异对照(2026-10-15 实测,⛔ 证明它抓得住,不是恒绿):
|
||||||
|
1. `mkdir -p <技能包>/scripts/05-交接单`
|
||||||
|
2. 跑 `python session-rules-check.py --ws <本区> --all` ⇒ 输出
|
||||||
|
`✗ [A] 锁根落进了技能包 ⇒ … —— <技能包>/scripts/05-交接单`
|
||||||
|
3. `rmdir <技能包>/scripts/05-交接单` 复原 ⇒ 回到 `✓ [A] 锁根未落进技能包`
|
||||||
|
⚠️ 为什么不把它做成 `selftest.py` 的自动化用例:那样必须**真在技能包里造目录**,
|
||||||
|
用例中途被打断就会**把一个 `05-交接单` 永久留在包里**(不可逆污染)——
|
||||||
|
风险大于收益。⇒ 保留为上面这段**可手工复现**的步骤。
|
||||||
|
"""
|
||||||
|
roots = []
|
||||||
|
seen = set()
|
||||||
|
for r in (os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
||||||
|
os.path.join(SKILLS, "session-mechanism") if SKILLS else "",
|
||||||
|
os.path.join(WS, ".workbuddy", "skills", "session-mechanism") if WS else ""):
|
||||||
|
if not r:
|
||||||
|
continue
|
||||||
|
r = os.path.normpath(r)
|
||||||
|
if r not in seen:
|
||||||
|
seen.add(r)
|
||||||
|
roots.append(r)
|
||||||
|
bad = []
|
||||||
|
for r in roots:
|
||||||
|
for rel in (("05-交接单",), ("scripts", "05-交接单")):
|
||||||
|
if os.path.isdir(os.path.join(r, *rel)):
|
||||||
|
bad.append("%s/%s" % (r, "/".join(rel)))
|
||||||
|
if bad:
|
||||||
|
rec("lockroot_in_pkg", "A", "fail",
|
||||||
|
"锁根落进了技能包 ⇒ 锁长在只读发布物里,且换工作区后会跟错地方(域锁静默失效)",
|
||||||
|
"、".join(sorted(set(bad))))
|
||||||
|
else:
|
||||||
|
rec("lockroot_in_pkg", "A", "ok", "锁根未落进技能包", "")
|
||||||
|
return bad
|
||||||
|
|
||||||
|
|
||||||
def check_orchestration():
|
def check_orchestration():
|
||||||
if not os.path.isfile(DB):
|
if not os.path.isfile(DB):
|
||||||
rec("clock", "C", "fail", "读不到宿主库", DB)
|
rec("clock", "C", "fail", "读不到宿主库", DB)
|
||||||
@@ -786,6 +831,7 @@ def main():
|
|||||||
check_memory_pointers()
|
check_memory_pointers()
|
||||||
check_snapshot_stale()
|
check_snapshot_stale()
|
||||||
check_orchestration()
|
check_orchestration()
|
||||||
|
check_lockroot_in_pkg()
|
||||||
return finish()
|
return finish()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user