diff --git a/session-mechanism/scripts/session-rules-check.py b/session-mechanism/scripts/session-rules-check.py index 038d2d2..66ca8d3 100644 --- a/session-mechanism/scripts/session-rules-check.py +++ b/session-mechanism/scripts/session-rules-check.py @@ -532,6 +532,51 @@ def _once_zombie(mine, ran, grace_min=30.0): 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(): if not os.path.isfile(DB): rec("clock", "C", "fail", "读不到宿主库", DB) @@ -786,6 +831,7 @@ def main(): check_memory_pointers() check_snapshot_stale() check_orchestration() + check_lockroot_in_pkg() return finish()