From 5c862e400501304a2b875d58863eae32f71f46a1 Mon Sep 17 00:00:00 2001 From: maogeigei Date: Tue, 20 Oct 2026 15:49:38 +0800 Subject: [PATCH] =?UTF-8?q?chore(session-mechanism):=20=E4=BA=A4=E6=8E=A5?= =?UTF-8?q?=E5=8D=95=E7=9B=AE=E5=BD=95=E6=94=B9=E5=90=8D=EF=BC=88=E5=8E=BB?= =?UTF-8?q?=2005-=20=E5=89=8D=E7=BC=80=EF=BC=89=EF=BC=8B=E5=9B=BE=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E8=A7=A6=E5=8F=91=E8=AF=8D=E6=94=B9=E4=B8=BA=E3=80=8C?= =?UTF-8?q?=E5=9B=BE=E5=AE=8C=E6=88=90=E7=9B=AE=E6=A0=87=E3=80=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- session-mechanism/SKILL.md | 4 +-- session-mechanism/env/roots.env.example | 2 +- session-mechanism/install.py | 8 ++--- session-mechanism/references/04-决策方法论.md | 6 ++-- session-mechanism/references/pitfalls.md | 2 +- .../references/作业规矩/03-多棒接力编排.md | 2 +- session-mechanism/scripts/collabd.py | 8 ++--- .../scripts/hooks/lock-guard-hook.py | 12 +++---- .../scripts/hooks/skill-load-guard.py | 20 +++++------ .../scripts/lock/handoff-guard.sh | 34 +++++++++---------- .../scripts/lock/handoff-status.py | 8 ++--- session-mechanism/scripts/lock/op-lock.sh | 2 +- session-mechanism/scripts/lock/orphan-lock.py | 2 +- .../scripts/lock/preflight-lock.sh | 2 +- .../scripts/session-rules-check.py | 12 +++---- 15 files changed, 62 insertions(+), 62 deletions(-) diff --git a/session-mechanism/SKILL.md b/session-mechanism/SKILL.md index 4fb2b36..5be99e8 100644 --- a/session-mechanism/SKILL.md +++ b/session-mechanism/SKILL.md @@ -78,9 +78,9 @@ agent_created: true `使用任务会话完成 X 目标` / `使用执行会话完成 X 目标` / `继续 X 目标` / `用执行会话完成 X` / `派任务会话` / `建任务会话` / 任何"让我去把某个目标做完/接着做"的表述。 -🔴🔴 **两句口令分流哪套机制(2026-10-19 用户令,逐字「完成复杂目标 走graph , 完成目标 走 loop」)**: +🔴🔴 **两句口令分流哪套机制(2026-10-19 用户令,逐字「图完成目标 走graph , 完成目标 走 loop」)**: -- `完成复杂目标:XXXX` ⇒ **走 graph(图模式)**:`graph/graphctl.py` 建图 → `check` → `tick` 派活 → `report` 回报 → `progress`;看板 20100 的「图模式」块会显示。 +- `图完成目标:XXXX` ⇒ **走 graph(图模式)**:`graph/graphctl.py` 建图 → `check` → `tick` 派活 → `report` 回报 → `progress`;看板 20100 的「图模式」块会显示。 - `完成目标:XXXX`(含 `使用项目机制完成目标`)⇒ **走 loop(现有项目机制)**:`goalctl` 立目标 → `collabd` 建排期/拉执行会话 → 检查会话判据。 - 两条互不覆盖:要图就明说「复杂目标」;只说「完成目标」按 loop 走,⛔ 不擅自改道。 diff --git a/session-mechanism/env/roots.env.example b/session-mechanism/env/roots.env.example index 6c6bbac..d4ccb40 100644 --- a/session-mechanism/env/roots.env.example +++ b/session-mechanism/env/roots.env.example @@ -27,5 +27,5 @@ COLLABD_PROD_CONFIG=<工作区根>/.workbuddy/collab/collabd.config.json # ④ WorkBuddy 的配置目录(宿主库 workbuddy.db 在它下面) CODEBUDDY_CONFIG_DIR=,例:<用户目录>/.workbuddy -# ⑤ 文档库根(锁目录 `05-交接单/.locks` 在它下面) +# ⑤ 文档库根(锁目录 `交接单/.locks` 在它下面) DSH_DOCS_ROOT=<文档库根> diff --git a/session-mechanism/install.py b/session-mechanism/install.py index a9f32ea..0244a3e 100644 --- a/session-mechanism/install.py +++ b/session-mechanism/install.py @@ -228,7 +228,7 @@ def detect_docs_root(explicit: str | None) -> Path | None: prev = read_roots().get("DSH_DOCS_ROOT") if prev and Path(prev).is_dir(): return Path(prev) - # 启发式:文档库必须同时具备「05-交接单」与「07-scripts」两个标志目录 + # 启发式:文档库必须同时具备「交接单」与「07-scripts」两个标志目录 # ⚠️ 只看同名会误选到工作区里的 `dsh-server-docs/` 副本 ⇒ 两个条件都要满足。 seeds = [Path.cwd(), *Path.cwd().parents] for k in ("DSH_CODE_REPO",): @@ -236,7 +236,7 @@ def detect_docs_root(explicit: str | None) -> Path | None: seeds.insert(0, Path(os.environ[k])) for s in seeds[:6]: for cand in (s / "dsh-server-docs", s.parent / "dsh-server-docs"): - if (cand / "05-交接单").is_dir() and (cand / "07-scripts").is_dir(): + if (cand / "交接单").is_dir() and (cand / "07-scripts").is_dir(): return cand.resolve() return None @@ -363,10 +363,10 @@ def write_roots(ws: Path, docs: Path | None, code: str | None, dry: bool) -> Non # 🔴 2026-10-15 修(第 11 条禁令):解析不到文档库时**如实报错 + 留痕**。 # 原行为是**整行不生成、不报错、不留痕** ⇒ 锁类脚本读不到该键 ⇒ # `handoff-guard.sh` 回落到**技能包自己的 `scripts/`** ⇒ 锁根落进技能包 - # (D 盘那台实测已在 `<技能包>/scripts/05-交接单/.locks` 长出一把锁)。 + # (D 盘那台实测已在 `<技能包>/scripts/交接单/.locks` 长出一把锁)。 # ⛔ 不许静默省略整行 —— 静默与"本机真没有文档库"表现完全一样,最难查。 log("⛔ 解析不到文档库根:`DSH_DOCS_ROOT` 未设、`roots.env` 里也没有、启发式也没找到" - "「同层同时有 05-交接单 与 07-scripts」的目录 ⇒ **本次不写** `DSH_DOCS_ROOT`。" + "「同层同时有 交接单 与 07-scripts」的目录 ⇒ **本次不写** `DSH_DOCS_ROOT`。" "⚠️ 后果:锁类脚本(`lock/handoff-guard.sh` / `hooks/lock-guard-hook.py` / " "`collabd.py` 的域锁)取不到锁根。" "✅ 本区确实没有 dsh 文档库时 ⇒ 把**本工作区根**写进 `env/roots.env` 的 `DSH_DOCS_ROOT`。") diff --git a/session-mechanism/references/04-决策方法论.md b/session-mechanism/references/04-决策方法论.md index dda95d3..869d23f 100644 --- a/session-mechanism/references/04-决策方法论.md +++ b/session-mechanism/references/04-决策方法论.md @@ -49,7 +49,7 @@ | 什么时候用 | 用户刚提需求、问"最优方案"、要在选项间选、要判"该不该做" | 决策已定,开始改码 / 验证 / 归档 | | 产出 | **选项表 + 决策点 + 判定依据** | 代码改动 + 验证记录 + 档案 | -> 顺序:**先本技能定案 → 再 dsh-change-workflow 执行**。规划会话只做前者,执行会话只做后者(交接载体 = `dsh-server-docs/05-交接单/`)。 +> 顺序:**先本技能定案 → 再 dsh-change-workflow 执行**。规划会话只做前者,执行会话只做后者(交接载体 = `dsh-server-docs/交接单/`)。 ### 0.1 素材来源与复跑方式(**honest provenance,勿含糊**) @@ -57,7 +57,7 @@ |---|---|---| | 改造档案 | `dsh-server-docs/04-调整方案/NN-*.md` | 每份含「触发 / 用户裁定 / 方案对比 / 事故踩坑」——**会话的结论层** | | 工作日志 | `.workbuddy/memory/YYYY-MM-DD.md` | 五天过程日志,含用户原话引用——**会话的过程层** | -| 现行事实层 | `BRIEF.md` / `INDEX.md` / `README.md` / `01-规范/06-工作台UI规范.md` / `05-交接单/README.md` | 约定与红线 | +| 现行事实层 | `BRIEF.md` / `INDEX.md` / `README.md` / `01-规范/06-工作台UI规范.md` / `交接单/README.md` | 约定与红线 | | **原始会话转录** | `~/.workbuddy/projects/<工作区目录名>/*.jsonl` | **用户真实发言的原始记录**(含被否决、被纠正的内容) | **复跑命令(只读)**: @@ -363,7 +363,7 @@ R7-边界② 的"只报告不动手"**只针对「别人的 / 归属不明」的 - **红线优先级高于本技能**:R1 不自动升级 dsh|R2 不改官方主程序与缓存|R3 client bundle 禁 `exports.default`|R4 不用真实账号做登录测试|**R5 权限只准收窄**|**R7 禁未经确认的批量/全仓写入**|**R8 中断在线用户须先知会** —— 任一条命中,**先停手**,本技能的效率论证不构成豁免。 - **执行层**:决策定了之后走 `dsh-change-workflow`(六阶段 + 档案模板 + 并行调度协议)。 -- **规划/执行分离**:本技能属**规划侧**;产出交接单(`dsh-server-docs/05-交接单/`,8 段必填),规划会话**不 ssh、不改码、不重启、不 scp**。 +- **规划/执行分离**:本技能属**规划侧**;产出交接单(`dsh-server-docs/交接单/`,8 段必填),规划会话**不 ssh、不改码、不重启、不 scp**。 - **单一来源**:清单与状态 = `INDEX.md §二`;待办 = `01-规范/03-路线图与待办.md`;部署事实 = `DEPLOY-本部署.md`;UI 基线 = `01-规范/06-工作台UI规范.md`;现行事实 = `BRIEF.md`(首读)。 - **可复跑判定工具**:`docs-audit.py`(歧义/编号/悬空引用,退出码非 0 即需处理)、`docs-sync-check.sh`(双端对账)、`docs-manifest.py`(机读清单)、`handoff-guard.sh`(并发预检,推送前 `PUSH=1`)。 diff --git a/session-mechanism/references/pitfalls.md b/session-mechanism/references/pitfalls.md index 8b88df2..26cbafc 100644 --- a/session-mechanism/references/pitfalls.md +++ b/session-mechanism/references/pitfalls.md @@ -753,7 +753,7 @@ 读锁表判占用,被占 ⇒ **换域名**(⛔ 不是等),且**必须排除自己的锁**(否则锁主本人被自己挡住); 锁表读不到 ⇒ **fail-safe 判全部不可派**(这正是用户那句「别的只能干等」的机制面)。 - ⚠️ **顺带查出的既有不一致**:`handoff-guard.sh` 用 `scripts skills`, - `lock-guard-hook.py:58` 用 `07-scripts 08-skills 05-交接单`。 + `lock-guard-hook.py:58` 用 `07-scripts 08-skills 交接单`。 ⇒ **工作区下的路径两边一致**(都先命中工作区名)⇒ 不影响工作区内的判据; 但**代码仓根目录下**的路径两边会算出不同键 ⇒ **已存在假绿风险**。`collabd.py --domain-status` 会报这个差异。 diff --git a/session-mechanism/references/作业规矩/03-多棒接力编排.md b/session-mechanism/references/作业规矩/03-多棒接力编排.md index d08e7e7..eff16eb 100644 --- a/session-mechanism/references/作业规矩/03-多棒接力编排.md +++ b/session-mechanism/references/作业规矩/03-多棒接力编排.md @@ -103,7 +103,7 @@ ```text 已登记自动接续:一次性 automation ``,约 3~4 分钟后(08:37)自动开新会话,**不用你操作**; -接续点 = 序 ④「443/TCP 兜底」的规划棒(出 `05-交接单/交接单_xxx_20260917.md`)。 +接续点 = 序 ④「443/TCP 兜底」的规划棒(出 `交接单/交接单_xxx_20260917.md`)。 ``` ### 3.1.1 ⛔ 排期两条铁律 diff --git a/session-mechanism/scripts/collabd.py b/session-mechanism/scripts/collabd.py index bf227d5..7a6656c 100644 --- a/session-mechanism/scripts/collabd.py +++ b/session-mechanism/scripts/collabd.py @@ -3941,7 +3941,7 @@ def active_schedule_exists(name: str) -> bool: # handoff-guard.sh : dsh-server-docs ai1net-dsh-server aliyun-dsh-server src poc web test # docs scripts skills 交接单 # lock-guard-hook.py: dsh-server-docs ai1net-dsh-server aliyun-dsh-server src poc web test -# docs 07-scripts 08-skills 05-交接单 +# docs 07-scripts 08-skills 交接单 # 差异:`scripts`/`skills` vs `07-scripts`/`08-skills`。⇒ 命中「第一个锚点段」时, # **工作区下的路径两边结果一致**(都先命中工作区名)⇒ 本节的实际判据不受影响; # ⚠️ 但**代码仓根目录下的路径**(无工作区名那段)两边会算出不同键 ⇒ 已存在假绿风险。 @@ -3951,7 +3951,7 @@ def active_schedule_exists(name: str) -> bool: _DS_ANCHORS = ("dsh-server-docs", "ai1net-dsh-server", "aliyun-dsh-server", "src", "poc", "web", "test", "docs", "scripts", "skills", "交接单") # ⚠️ 另一份(`lock-guard-hook.py:83 _DOMAIN_SEGS`)—— 🔴 2026-10-03 10:0x **已对齐**: -# 原值是 `07-scripts/08-skills/05-交接单`(带数字前缀的**旧仓目录名**)⇒ 与本表不同 +# 原值是 `07-scripts/08-skills/交接单`(带数字前缀的**旧仓目录名**)⇒ 与本表不同 # ⇒ 那个文件里的 `domain_key()` 会跟抢锁侧算出**不同域键** ⇒ 域锁静默失效(假绿)。 # ✅ 已改成 `scripts/skills/交接单`,与 `_DS_ANCHORS` 及 shell 侧**三处逐字一致**。 # ⛔ 本变量**不参与任何计算**,只保留作"另一侧读到什么"的对照;自检会逐字比三处。 @@ -4019,7 +4019,7 @@ def ds_active_domain_locks() -> dict: """ out = {} _me = str(os.environ.get("DSH_SESSION_NAME") or "").strip() - # 🔴🔴 锁根=文档库的 `05-交接单/.locks`。**键名必须与写侧逐字同源:`DSH_DOCS_ROOT`** + # 🔴🔴 锁根=文档库的 `交接单/.locks`。**键名必须与写侧逐字同源:`DSH_DOCS_ROOT`** # (写侧六处:`lock/handoff-guard.sh:46`/`lock/preflight-lock.sh:30`/`lock/handoff-status.py:40`/ # `lock/orphan-lock.py:203`/`hooks/lock-guard-hook.py:74`/`session-rules-check.py:107`) # 2026-10-15 修:原读 `DSH_LOCKROOT`/`DSH_DOCS_REPO` —— 这两个键**全包只此一处**读, @@ -4030,7 +4030,7 @@ def ds_active_domain_locks() -> dict: _cands.append(os.environ["DSH_LOCKROOT"]) _dr = (os.environ.get("DSH_DOCS_ROOT") or "").strip() # ← 与写侧同源 if _dr: - _cands.append(os.path.join(_dr, "05-交接单", ".locks")) + _cands.append(os.path.join(_dr, "交接单", ".locks")) root = next((c for c in _cands if os.path.isdir(c)), "") if not root: # ⛔ 不再回落到"猜的盘符"(第 11 条禁令):如实报错 + 留痕。 diff --git a/session-mechanism/scripts/hooks/lock-guard-hook.py b/session-mechanism/scripts/hooks/lock-guard-hook.py index 7f5077f..6086cd9 100644 --- a/session-mechanism/scripts/hooks/lock-guard-hook.py +++ b/session-mechanism/scripts/hooks/lock-guard-hook.py @@ -6,7 +6,7 @@ lock-guard-hook.py — 「无锁不许改库」的强制钩子(WorkBuddy PreTo ──────────── 2026-09-12 实证:`handoff-guard.sh` 输出「无全局锁」被会话读成「环境干净,可以开工」 (正确读法是「你快去抢锁」),结果**两个会话同时改了本库**。措辞已在 guard 与 -`05-交接单/README.md` 里补正,但**约定拦不住不看的人** —— 本脚本是强制层。 +`交接单/README.md` 里补正,但**约定拦不住不看的人** —— 本脚本是强制层。 作用域(**刻意收窄:对其他项目零影响**) ──────────── @@ -17,7 +17,7 @@ lock-guard-hook.py — 「无锁不许改库」的强制钩子(WorkBuddy PreTo 判据 ──── -`<文档库根>/05-交接单/.exec-lock` 存在 = 有人持锁 → 放行(归属由台账 OWNER 承担); +`<文档库根>/交接单/.exec-lock` 存在 = 有人持锁 → 放行(归属由台账 OWNER 承担); 不存在 = **deny**,并把可直接粘贴的抢锁命令回给 Agent。 为什么**不拦 Bash** @@ -91,9 +91,9 @@ if not DOCS_ROOT: sys.exit(0) # ⛔ 不留盘符:代码库=文档库的上一级(`/dsh-server-docs` 的约定) CODE_REPO = os.environ.get("DSH_CODE_REPO") or (os.path.dirname(DOCS_ROOT) if DOCS_ROOT else "") -LOCK_DIR = os.path.join(DOCS_ROOT, "05-交接单", ".exec-lock") +LOCK_DIR = os.path.join(DOCS_ROOT, "交接单", ".exec-lock") OWNER_FILE = os.path.join(LOCK_DIR, "OWNER") -LOCKS_ROOT = os.path.join(DOCS_ROOT, "05-交接单", ".locks") +LOCKS_ROOT = os.path.join(DOCS_ROOT, "交接单", ".locks") GUARD_CMD = 'bash 07-scripts/handoff-guard.sh --claim-exec "<你的会话名>"' GUARD_DOMAIN_CMD = 'bash 07-scripts/handoff-guard.sh --claim-exec "<你的会话名>" --domains <域...>' @@ -101,7 +101,7 @@ GUARD_DOMAIN_CMD = 'bash 07-scripts/handoff-guard.sh --claim-exec "<你的会话 # ⚠️ 顺序有意义:**先长后短**,与 handoff-guard.sh 的 _ANCHOR_SEGS 逐字一致。 # 两侧只要顺序或集合不同 ⇒ 同一文件算出不同域键 ⇒ 域锁静默失效(假绿)。 # 🔴🔴 2026-10-03 10:0x **修一处真缺陷(不是文档漂移)**:本行原为 -# `… "docs", "07-scripts", "08-skills", "05-交接单"`, +# `… "docs", "07-scripts", "08-skills", "交接单"`, # 而真源 `handoff-guard.sh:66 _ANCHOR_SEGS` 是 `… "docs", "scripts", "skills", "交接单"`。 # ⚠️ 后果不是"看起来不一致":`domain_key()` **拿本表参与实际计算** ⇒ 同一个文件 # 在 hook 侧与抢锁侧会算出**不同域键** ⇒ 两边各抢各的 ⇒ **域锁等于没有(假绿)**。 @@ -339,7 +339,7 @@ def main() -> None: who = lock_owner() msg = f"🔐 全局执行锁【已被占用】:{who} —— 但**域锁可绕开它**:不重叠的域仍能并行。" hook_log("SessionStart", f"旧全局锁被占 owner={who} source={src}") - elif os.path.isdir(DOCKS := os.path.join(DOCS_ROOT, "05-交接单", ".locks")) and os.listdir(DOCKS): + elif os.path.isdir(DOCKS := os.path.join(DOCS_ROOT, "交接单", ".locks")) and os.listdir(DOCKS): msg = "🔐 已有域锁在跑 —— 开工前先抢你要的域(不重叠即可并行):" + GUARD_DOMAIN_CMD hook_log("SessionStart", f"有域锁无全局锁 source={src}") elif os.path.isdir(DOCS_ROOT): diff --git a/session-mechanism/scripts/hooks/skill-load-guard.py b/session-mechanism/scripts/hooks/skill-load-guard.py index 961297e..4be5368 100644 --- a/session-mechanism/scripts/hooks/skill-load-guard.py +++ b/session-mechanism/scripts/hooks/skill-load-guard.py @@ -160,11 +160,11 @@ TRIGGERS = ( # 🔴 2026-10-19 用户令(逐字「把skill 改成 完成目标也能触发」):光写「项目机制完成目标」不够 —— # 用户口语里会把机制名字省掉,只说「完成目标」;词表里必须有**裸「完成目标」**这一条, # 否则那句同样是派活授权,却一条都命不中。 - # 🔴 2026-10-19 用户令(逐字「完成复杂目标 走graph , 完成目标 走 loop」): - # **两条口令分工** —— `完成复杂目标` ⇒ 图模式 graph;`完成目标` ⇒ 现有 loop 机制。 - # ⚠️ 两句不是包含关系("完成复杂目标"里没有连续的"完成目标"子串),所以两词都要进表, + # 🔴 2026-10-19 用户令(逐字「图完成目标 走graph , 完成目标 走 loop」): + # **两条口令分工** —— `图完成目标` ⇒ 图模式 graph;`完成目标` ⇒ 现有 loop 机制。 + # ⚠️ 两句不是包含关系("图完成目标"里没有连续的"完成目标"子串),所以两词都要进表, # 分流按"先看复杂"判,⛔ 不许靠子串顺序碰运气。 - '完成复杂目标', '复杂目标', + '图完成目标', '复杂目标', '完成目标', '使用任务会话', '使用执行会话', '使用协作会话', '任务会话完成', '执行会话完成', '继续完成目标', '继续目标', '继续完成之前', '继续之前的', @@ -188,7 +188,7 @@ _LOAD_RULES = ('按规则来', '按规则做', '按作业规则', '遵守规则' # 命中"使用任务会话完成目标"的人,要的是**会话机制本体**(怎么建排期/怎么派活/ # 自决策白名单在 `references/02-功能优先协作协议.md`),⛔ 不是决策方法论。 _LOAD_SESSION = ('使用项目机制', '项目机制完成', '项目机制', - '完成复杂目标', '复杂目标', '完成目标', + '图完成目标', '复杂目标', '完成目标', '使用任务会话', '使用执行会话', '使用协作会话', '任务会话完成', '执行会话完成', '继续完成目标', '继续目标', '继续完成之前', '继续之前的', '会话技能', '会话机制', '创建任务会话', '建任务会话', @@ -413,18 +413,18 @@ def main(): hit_rules = [w for w in hits if w in _LOAD_RULES] if hit_session: head = '检测到用户本轮**点名了会话机制 / 派活**(命中:%s)。' % '、'.join(hit_session) - # 🔴 2026-10-19 用户令(逐字「完成复杂目标 走graph , 完成目标 走 loop」): - # 两条口令分流。**先判"复杂"**("完成复杂目标"里没有连续的"完成目标"子串,但仍按先后判更稳)。 + # 🔴 2026-10-19 用户令(逐字「图完成目标 走graph , 完成目标 走 loop」): + # 两条口令分流。**先判"复杂"**("图完成目标"里没有连续的"完成目标"子串,但仍按先后判更稳)。 mode = '' - if '复杂目标' in prompt: - mode = ('🔴 **本轮走 graph(图模式)**:用户说「完成复杂目标」⇒ 用本包 `graph/graphctl.py`:\n' + if '图完成目标' in prompt or '复杂目标' in prompt: + mode = ('🔴 **本轮走 graph(图模式)**:用户说「图完成目标」⇒ 用本包 `graph/graphctl.py`:\n' ' `init` 建图 → `check` 校验 → `tick`(算完就开)→ 执行方 `report` 回报 → `progress` 看进度;\n' ' 看板 `http://127.0.0.1:20100/` 的「图模式」块会实时显示这张图。\n' ' ⛔ 不走 loop 排期、⛔ 不写 `goal.json`/`tasks.json`;图只负责编排,节点上的活仍由会话去干。\n\n') elif '完成目标' in prompt: mode = ('🔴 **本轮走 loop(现有项目机制)**:用户说「完成目标」⇒ 按目标 + 台账 + 队列那套推进' '(`goalctl`/`collabd`/检查会话)。\n' - ' ⛔ 不擅自改走 graph —— 要走图,用户会说「完成复杂目标」。\n\n') + ' ⛔ 不擅自改走 graph —— 要走图,用户会说「图完成目标」。\n\n') body = mode + ( '⛔ **不要凭记忆代替、也不要以"我已经知道这套机制"为由跳过**:\n' ' **先调用 Skill 工具加载 `session-mechanism`**(会话机制 + 多会话执行总入口 · 含**首屏第 0 步加载门槛**' diff --git a/session-mechanism/scripts/lock/handoff-guard.sh b/session-mechanism/scripts/lock/handoff-guard.sh index d5e6d47..df860e0 100644 --- a/session-mechanism/scripts/lock/handoff-guard.sh +++ b/session-mechanism/scripts/lock/handoff-guard.sh @@ -5,8 +5,8 @@ # 约定全部依赖“人记得做”。本脚本把判据变成**一条命令 + 退出码**,不靠记忆。 # # 关键设计:**mtime 只作提示、不作判定**(它分不清“谁改的”)。真正的判定来自四处: -# ① 全局执行锁(05-交接单/.exec-lock)—— **一粗**:同一时刻只允许一个任务会话动「文档/代码/服务器」 -# ① 单级占用锁(05-交接单/.doing-<单号>)—— **一细**:这个单归谁做(供台账 / 接管使用) +# ① 全局执行锁(交接单/.exec-lock)—— **一粗**:同一时刻只允许一个任务会话动「文档/代码/服务器」 +# ① 单级占用锁(交接单/.doing-<单号>)—— **一细**:这个单归谁做(供台账 / 接管使用) # ② 越界改动(不在我声明清单里的未提交文件)—— 推送前检查,防“顺手重放别人的半成品” # ④ 双端一致性(docs-sync-check.sh)—— 推送前检查,防“幽灵文件”(推回了别人已移走的文件) # @@ -24,7 +24,7 @@ # # ⛔ 不带名字 ⇒ 拒绝释放(R9,2026-09-25 修) # # 域锁侧:--claim-exec <会话> --domains <域…> / 同姿势 --release-exec <会话> # bash 07-scripts/handoff-guard.sh # 看全局状态(信息模式) -# ME="exec-session-B" MINE="05-交接单/T03-*.md" bash 07-scripts/handoff-guard.sh T03 # 开工检查(严格) +# ME="exec-session-B" MINE="交接单/T03-*.md" bash 07-scripts/handoff-guard.sh T03 # 开工检查(严格) # ME="exec-session-B" MINE="..." PUSH=1 bash 07-scripts/handoff-guard.sh T03 # 推送前检查 # # 环境变量:ME(我是谁 —— 用来判断锁是不是自己的;不设则一律按“别人的锁”处理) @@ -46,7 +46,7 @@ fi # 🔴 2026-10-15 修(第 11 条禁令 + D 盘实测事故): # 原写 `ROOT="${DSH_DOCS_ROOT:-$(cd "$_SM_HERE/.." && pwd)}"` —— 回落值是 # `$_SM_HERE/..` = **本技能包自己的 `scripts/`** ⇒ 锁根落进技能包 -# (实测已在 `<技能包>/scripts/05-交接单/.locks` 长出一把锁,本该落文档库)。 +# (实测已在 `<技能包>/scripts/交接单/.locks` 长出一把锁,本该落文档库)。 # ✅ 现行:取不到 ⇒ **如实报错 + 留痕 + 退出码 2**,⛔ 不回落到包内、⛔ 不猜路径。 if [ -z "${DSH_DOCS_ROOT:-}" ]; then _SM_ERR="${_SM_PKG}/logs/handoff-guard.err.log" @@ -59,9 +59,9 @@ if [ -z "${DSH_DOCS_ROOT:-}" ]; then fi ROOT="$DSH_DOCS_ROOT" cd "$ROOT" || exit 2 -LOCKDIR="$ROOT/05-交接单/.doing-" -LOCKEXEC="$ROOT/05-交接单/.exec-lock" -MELOCK="$ROOT/05-交接单/.me-lock" +LOCKDIR="$ROOT/交接单/.doing-" +LOCKEXEC="$ROOT/交接单/.exec-lock" +MELOCK="$ROOT/交接单/.me-lock" ME="${ME:-}" # ══════════════════════════════════════════════════════════════════════════ @@ -77,7 +77,7 @@ ME="${ME:-}" # # 兼容:`.exec-lock` 分支**原样保留** ⇒ 未升级的会话照旧可用(hook 是启动时快照)。 # ══════════════════════════════════════════════════════════════════════════ -LOCKSROOT="$ROOT/05-交接单/.locks" +LOCKSROOT="$ROOT/交接单/.locks" GATELOCK="$LOCKSROOT/.gate" # 秒级临界区:算交集时必须串行,否则两个会话可能同时判定"无冲突" # 域字符串规范化 ⇒ 域键。**必须与 lock-guard-hook.py 的 domain_key() 同规则**, @@ -418,15 +418,15 @@ if [ "${1:-}" = "--claim" ]; then [ -n "$T" ] || { echo "用法:--claim <单号> [占用者]" >&2; exit 2; } if mkdir "$LOCKDIR$T" 2>/dev/null; then printf '%s\n' "$OWNER" > "$LOCKDIR$T/OWNER" - echo "✓ 已占位:05-交接单/.doing-$T($OWNER)—— 完工请 --release $T" + echo "✓ 已占位:交接单/.doing-$T($OWNER)—— 完工请 --release $T" exit 0 fi - echo "✗ 占位失败:05-交接单/.doing-$T 已存在(占用者 $(cat "$LOCKDIR$T/OWNER" 2>/dev/null || echo '?')) → 停手" >&2 + echo "✗ 占位失败:交接单/.doing-$T 已存在(占用者 $(cat "$LOCKDIR$T/OWNER" 2>/dev/null || echo '?')) → 停手" >&2 exit 1 fi if [ "${1:-}" = "--release" ]; then T="${2:-}"; [ -n "$T" ] || { echo "用法:--release <单号>" >&2; exit 2; } - rm -rf "$LOCKDIR$T" && echo "✓ 已释放:05-交接单/.doing-$T" || { echo "✗ 释放失败" >&2; exit 1; } + rm -rf "$LOCKDIR$T" && echo "✓ 已释放:交接单/.doing-$T" || { echo "✗ 释放失败" >&2; exit 1; } exit 0 fi @@ -451,7 +451,7 @@ in_mine() { [ -z "$MINE" ] && return 1; for p in $MINE; do case "$1" in $p) retu # ── ① 占用锁(硬判定)──────────────────────────────────── echo -echo "【1】占用锁(05-交接单/.doing-*)" +echo "【1】占用锁(交接单/.doing-*)" shopt -s nullglob LOCKS=("$LOCKDIR"*) if [ ${#LOCKS[@]} -eq 0 ]; then @@ -475,9 +475,9 @@ fi if [ ${#LOCKS[@]} -gt 0 ]; then for L in "${LOCKS[@]}"; do n="$(basename "$L")"; T="${n#.doing-}" - row="$(grep -m1 "^| \`$T-" 05-交接单/README.md 2>/dev/null || true)" + row="$(grep -m1 "^| \`$T-" 交接单/README.md 2>/dev/null || true)" if [ -z "$row" ]; then - echo " ⚠ $T 有占用锁,但 \`05-交接单/README.md §一\` 里**没有这一行** → 补上(新增单)" + echo " ⚠ $T 有占用锁,但 \`交接单/README.md §一\` 里**没有这一行** → 补上(新增单)" VERDICT=1; HARD+=("①b $T 有锁但台账缺行") elif printf '%s' "$row" | grep -qE "执行中|已完成|已归档"; then echo " · $T 台账已标「执行中/已完成」✓" @@ -491,7 +491,7 @@ fi # ── ①c 全局执行锁(2026-09-12 新增:同一时刻只允许一个任务会话)──── echo -echo "【1c】全局执行锁(05-交接单/.exec-lock)" +echo "【1c】全局执行锁(交接单/.exec-lock)" if [ ! -d "$LOCKEXEC" ]; then echo " ✓ 无全局锁 —— ⚠️ 这**不是「可以开工」,是「你快去抢」**:" echo " 开工前必须先:bash 07-scripts/handoff-guard.sh --claim-exec \"<你的会话名>\"" @@ -544,7 +544,7 @@ fi # ── ② 越界改动(推送前硬判定)──────────────────────────── echo echo "【2】未提交改动(含未跟踪)" -CHANGED="$(git -c core.quotepath=false status --short 2>/dev/null | awk '{print $NF}' | grep -vE '^05-交接单/\.(doing-|exec-lock)' || true)" +CHANGED="$(git -c core.quotepath=false status --short 2>/dev/null | awk '{print $NF}' | grep -vE '^交接单/\.(doing-|exec-lock)' || true)" OUTSIDE=(); INSIDE=() if [ -z "$CHANGED" ]; then echo " ✓ 工作区干净" @@ -570,7 +570,7 @@ echo echo "【3】近 $WINDOW 分钟被改动的文件(**仅提示**:mtime 分不清谁改的)" HOT="$(find . -type f -mmin "-$WINDOW" \ -not -path './.git/*' -not -path '*/node_modules/*' -not -name '*.bak*' \ - -not -path './05-交接单/.doing-*' -not -path './05-交接单/.exec-lock*' 2>/dev/null | sed 's|^\./||' | LC_ALL=C sort)" + -not -path './交接单/.doing-*' -not -path './交接单/.exec-lock*' 2>/dev/null | sed 's|^\./||' | LC_ALL=C sort)" if [ -z "$HOT" ]; then echo " ✓ 无(这块是「冷」的)" else diff --git a/session-mechanism/scripts/lock/handoff-status.py b/session-mechanism/scripts/lock/handoff-status.py index 379148a..e75f1c9 100644 --- a/session-mechanism/scripts/lock/handoff-status.py +++ b/session-mechanism/scripts/lock/handoff-status.py @@ -1,10 +1,10 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -"""handoff-status.py — 一条命令看清 05-交接单 里哪些单子【待执行】/【已完成】/【无状态】。 +"""handoff-status.py — 一条命令看清 交接单 里哪些单子【待执行】/【已完成】/【无状态】。 -为什么需要它:`05-交接单/交接单-已完成/` 那层目录摊平后,**"目录位置"不再表达状态**。 +为什么需要它:`交接单/交接单-已完成/` 那层目录摊平后,**"目录位置"不再表达状态**。 状态改由**机读字段**承载(与布局解耦,摊平/搬家都不失效): - ① 单子头部 `- 状态:…` 行(模板已有)→ ② 回退取 `05-交接单/README.md` 台账表的「状态」列 + ① 单子头部 `- 状态:…` 行(模板已有)→ ② 回退取 `交接单/README.md` 台账表的「状态」列 用法:python 07-scripts/handoff-status.py """ @@ -38,7 +38,7 @@ import os import re ROOT = os.environ.get("DSH_DOCS_ROOT") or os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -D = os.path.join(ROOT, "05-交接单") +D = os.path.join(ROOT, "交接单") README = os.path.join(D, "README.md") HEAD_STATUS = re.compile(r"^[\s>*\-|#]*\*{0,2}状态\*{0,2}\s*[::]\s*(.+?)\s*$", re.M) diff --git a/session-mechanism/scripts/lock/op-lock.sh b/session-mechanism/scripts/lock/op-lock.sh index 7041a1e..b8e299f 100644 --- a/session-mechanism/scripts/lock/op-lock.sh +++ b/session-mechanism/scripts/lock/op-lock.sh @@ -7,7 +7,7 @@ # 文档冲突靠 git status / mtime 能看出来,**服务器态变更看不出来** # (systemctl 不会告诉你 10 分钟前谁重启过)→ 只能靠显式锁。 # -# 与文档库锁的关系:`05-交接单/.doing-<单>` + `.exec-lock` 管「文档与单子」;本锁管「生产态」。 +# 与文档库锁的关系:`交接单/.doing-<单>` + `.exec-lock` 管「文档与单子」;本锁管「生产态」。 # 顺序:**先抢全局执行锁 → 再占本锁**。 # # 用法: diff --git a/session-mechanism/scripts/lock/orphan-lock.py b/session-mechanism/scripts/lock/orphan-lock.py index 5fcff5b..470e2df 100644 --- a/session-mechanism/scripts/lock/orphan-lock.py +++ b/session-mechanism/scripts/lock/orphan-lock.py @@ -205,7 +205,7 @@ def main() -> int: root = Path(a.locks_root) exec_lock = root / ".exec-lock" elif docs and os.path.isdir(docs): - base = Path(docs) / "05-交接单" + base = Path(docs) / "交接单" root = base / ".locks" exec_lock = base / ".exec-lock" else: diff --git a/session-mechanism/scripts/lock/preflight-lock.sh b/session-mechanism/scripts/lock/preflight-lock.sh index 940a8a9..ada1509 100644 --- a/session-mechanism/scripts/lock/preflight-lock.sh +++ b/session-mechanism/scripts/lock/preflight-lock.sh @@ -43,7 +43,7 @@ fi DOCS_ROOT="$DSH_DOCS_ROOT" CODE_ROOT="${DSH_CODE_REPO:-${DOCS_ROOT%/*}}" # ⛔ 不留盘符:代码库=文档库的上一级(纯 bash 展开,⛔ 不调 dirname) WS_ROOT="${DSH_WS_ROOT:-$(pwd)}" # ⛔ 不留盘符:默认=当前工作目录(本文件内未被使用) -LOCKROOT="$DOCS_ROOT/05-交接单/.locks" +LOCKROOT="$DOCS_ROOT/交接单/.locks" # ── 机制层(⛔ 不受 architecture.md 的 src/** 分层管辖,但**全平台共用**)──────── # 判据:这些文件任一改动都会改变**所有会话**的行为 ⇒ 与层④同性质(全局影响)。 diff --git a/session-mechanism/scripts/session-rules-check.py b/session-mechanism/scripts/session-rules-check.py index 66ca8d3..20f8f11 100644 --- a/session-mechanism/scripts/session-rules-check.py +++ b/session-mechanism/scripts/session-rules-check.py @@ -537,19 +537,19 @@ def check_lockroot_in_pkg(): 为什么是红线:`lock/handoff-guard.sh` 与 `lock/preflight-lock.sh` 的 `DOCS_ROOT` 原先回落到 `$_SM_HERE/..`(=**技能包自己的 `scripts/`**)⇒ 取不到文档库根时, - 锁目录直接长在技能包里(另一环境实测已出现 `<技能包>/scripts/05-交接单/.locks/`)。 + 锁目录直接长在技能包里(另一环境实测已出现 `<技能包>/scripts/交接单/.locks/`)。 后果两条:① 污染技能包(它是只读发布物);② 换工作区后锁跟着错地方 ⇒ **域锁静默失效**。 ✅ 现行:两处回落实已改成「取不到就如实报错 + 留痕 + 退出码 2」; 本条是**兜底判据** —— 万一将来又有人改回"包内回落",这里要能报出来。 ✅ 变异对照(2026-10-15 实测,⛔ 证明它抓得住,不是恒绿): - 1. `mkdir -p <技能包>/scripts/05-交接单` + 1. `mkdir -p <技能包>/scripts/交接单` 2. 跑 `python session-rules-check.py --ws <本区> --all` ⇒ 输出 - `✗ [A] 锁根落进了技能包 ⇒ … —— <技能包>/scripts/05-交接单` - 3. `rmdir <技能包>/scripts/05-交接单` 复原 ⇒ 回到 `✓ [A] 锁根未落进技能包` + `✗ [A] 锁根落进了技能包 ⇒ … —— <技能包>/scripts/交接单` + 3. `rmdir <技能包>/scripts/交接单` 复原 ⇒ 回到 `✓ [A] 锁根未落进技能包` ⚠️ 为什么不把它做成 `selftest.py` 的自动化用例:那样必须**真在技能包里造目录**, - 用例中途被打断就会**把一个 `05-交接单` 永久留在包里**(不可逆污染)—— + 用例中途被打断就会**把一个 `交接单` 永久留在包里**(不可逆污染)—— 风险大于收益。⇒ 保留为上面这段**可手工复现**的步骤。 """ roots = [] @@ -565,7 +565,7 @@ def check_lockroot_in_pkg(): roots.append(r) bad = [] for r in roots: - for rel in (("05-交接单",), ("scripts", "05-交接单")): + for rel in (("交接单",), ("scripts", "交接单")): if os.path.isdir(os.path.join(r, *rel)): bad.append("%s/%s" % (r, "/".join(rel))) if bad: