#!/usr/bin/env python3 # -*- coding: utf-8 -*- """索引 / 文档链路优化 · 离线落地包(2026-09-14 会话 `15ee0d4f`) 用法: python3 apply-索引优化-20260914.py # dry-run:只验锚点命中数(≠1 即中止),不写任何文件 python3 apply-索引优化-20260914.py --apply # 打补丁(**需先持全局执行锁**) 补丁清单(对应评审 §三 的 G2/G3/G4 + ❓ 根因) P0-2 docs-manifest.py 状态优先级改为「**档案头部(权威)→ INDEX(❓/? 视为缺)→ ?**」 + 放宽提取正则 + norm_status() 规范化 ⇒ 根治「INDEX 的 ❓ 回灌 manifest、再渲染成 ❓」的**环形锁定** P1-1 docs-archive-index.py ① 顺序断言:manifest 比 04-调整方案/ 里最新档案旧 → 警告;`--write` 时**拒绝**(除非 --force) ② 输出「状态缺失率 + 名单」,缺失率 >10% 提示补头部(历史档案不回改,只在文末「修正」节补) P2-1 docs-shrink-guard.py 新增 `--allow-shrink <路径子串>`:**合法重构**(如技能拆分)不误报 P2-2 CODEBUDDY.md(库内) ① 标题「四件套」→「**七件套 · 顺序不可换**」+ 顺序说明 ② 约定 #2「单篇 ≤30 KB」补**标准拆法**:新增子页 + 原页留指针 ③ 约定 #3「日志 ≤50 KB」补**执行者与触发点**:按月切 + 留指针 + 谁触到谁切 落地后的手工步骤(本脚本不代做,避免越界): python3 scripts/docs-manifest.py && python3 scripts/docs-archive-index.py --write bash scripts/docs-sync-check.sh # 对账;六件套其余项 scp(同相对目录)+ 远端 chmod 600 / chown root:root git add <本次 5 个脚本 + 派生件> && git commit """ import io import os import sys _here = os.path.dirname(os.path.abspath(__file__)) ROOT = _here # 向上找含 dsh-server-docs/ 的工作区根 while not os.path.isdir(os.path.join(ROOT, 'dsh-server-docs')) and os.path.dirname(ROOT) != ROOT: ROOT = os.path.dirname(ROOT) LIB = os.path.join(ROOT, 'dsh-server-docs') if not os.path.isdir(os.path.join(LIB, 'scripts')): # 兜底:脚本直接放在库里跑 LIB = _here if os.path.isdir(os.path.join(_here, 'scripts')) else LIB LIB = os.environ.get('IDXOPT_LIB') or LIB # 允许指向库外副本(用于 A/B 验证) DRY = '--apply' not in sys.argv def patch(rel, pairs): """pairs: [(old, new, 说明)];全部命中数必须为 1,否则整体中止不写。""" p = os.path.join(LIB, rel) if not os.path.exists(p): raise AssertionError('❌ %s:库内不存在这个文件' % rel) s = io.open(p, encoding='utf-8', newline='').read() for old, new, why in pairs: n = s.count(old) assert n == 1, '❌ %s:锚点命中 %d 次(期望 1)→ %s' % (rel, n, why) s = s.replace(old, new) print(' ✓ %-26s %d 处' % (rel, len(pairs))) if not DRY: io.open(p, 'w', encoding='utf-8', newline='').write(s) # ── P0-2 docs-manifest.py:状态以档案头部为权威,❓ 不回灌 ────────────── NORM = ''' def norm_status(raw): """规范化档案头部的状态行 → 表里可读的一小段(≤14 字,去 ** 与尾部括注)。 ⚠️ **权威方向**:档案头部是**源**,INDEX 表是**派生展示** —— 不允许把表里的 ❓ 回灌成 manifest 的 status, 否则 manifest → INDEX → manifest 形成环形锁定,状态永远修不回来(2026-09-14 实测 14 篇被锁)。 """ t = re.sub(r'\\*{1,2}', '', raw or '').strip() t = re.split(r'——|||\\||;|;|\\s{2,}', t)[0].strip() t = re.sub(r'([^)]*)\\s*$', '', t).strip() return t[:14] ''' M_PAIRS = [ ('import io, os, re, sys, json, collections\n', 'import io, os, re, sys, json, collections\n' + NORM, '加 norm_status()'), (" hs = re.search(r'- 状态:\\s*(\\S{1,12})', head)\n", " hs = re.search(r'状态\\*{0,2}\\s*[::]\\s*(.{1,40})', head)\n" " hs_val = norm_status(hs.group(1)) if hs else ''\n" " st_idx = (index_status.get(num, {}) or {}).get('status') or ''\n" " if st_idx in ('?', '❓', ''):\n" " st_idx = '' # ❓/? 一律视为「缺」—— 防 INDEX↔manifest 环形锁定(2026-09-14)\n", '放宽状态提取 + 保留 INDEX 兜底'), (" 'status': (index_status.get(num, {}) or {}).get('status') or (hs.group(1) if hs else '?'),\n", " 'status': hs_val or st_idx or '?',\n", '状态优先级:头部 → INDEX → ?'), ] # ── P1-1 docs-archive-index.py:顺序断言 + 缺失率 ──────────────────── A_PAIRS = [ (" if not os.path.exists(MANIFEST):\n" " raise SystemExit('ERROR: 先跑 scripts/docs-manifest.py 生成 docs-manifest.json')\n", " if not os.path.exists(MANIFEST):\n" " raise SystemExit('ERROR: 先跑 scripts/docs-manifest.py 生成 docs-manifest.json')\n" "\n" " # ── 顺序断言(2026-09-14 加):派生链 manifest → 本脚本,顺序错会**静默**产出新旧混合 ──\n" " newest, _ad = 0.0, os.path.join(ROOT, '04-调整方案')\n" " if os.path.isdir(_ad):\n" " for _n in os.listdir(_ad):\n" " if _n.endswith('.md'):\n" " try:\n" " newest = max(newest, os.path.getmtime(os.path.join(_ad, _n)))\n" " except OSError:\n" " pass\n" " stale_min = (newest - os.path.getmtime(MANIFEST)) / 60.0\n" " if stale_min > 1:\n" " msg = ('⚠️ 顺序警告:docs-manifest.json 比 04-调整方案/ 最新档案旧 %.0f 分钟 ⇒ 先跑 '\n" " 'scripts/docs-manifest.py,否则本表用的是旧数据' % stale_min)\n" " if '--write' in sys.argv and '--force' not in sys.argv:\n" " raise SystemExit(msg + '\\n (确认要带旧数据刷新就加 --force)')\n" " print(msg)\n", '顺序断言'), (" print('档案 %d 篇 | 摘要:手写/已存 %d | 机器兜底 %d | 状态缺失(❓) %d'\n" " % (len(rows), n_sum, len(rows) - n_sum, sum(1 for v in rows.values() if '❓' in v)))\n", " _miss = sorted([k for k, v in rows.items() if '❓' in v], key=sort_key)\n" " print('档案 %d 篇 | 摘要:手写/已存 %d | 机器兜底 %d | 状态缺失(❓) %d(%.0f%%)'\n" " % (len(rows), n_sum, len(rows) - n_sum, len(_miss),\n" " 100.0 * len(_miss) / max(1, len(rows))))\n" " if _miss:\n" " print(' 缺失名单:%s' % ', '.join('04-' + m for m in _miss))\n" " if len(_miss) / max(1, len(rows)) > 0.10:\n" " print(' ⚠️ 缺失率 >10%% ⇒ **新档案**头部必须写「- 状态:…」;历史档案按「只增不改」不回改正文,'\n" " '可在文末「修正(YYYY-MM-DD)」节补一行状态 ⇒ 下一轮由 manifest 从头部取到')\n", '缺失率 + 名单'), ] # ── P2-1 docs-shrink-guard.py:合法重构白名单 ─────────────────────── S_PAIRS = [ (" python3 scripts/docs-shrink-guard.py --baseline # 只建档不比对(首次)\n", " python3 scripts/docs-shrink-guard.py --baseline # 只建档不比对(首次)\n" " python3 scripts/docs-shrink-guard.py --allow-shrink <路径子串> # 合法重构白名单(不报警)\n", '用法补一行'), (" now = collect()\n old = {}\n", " now = collect()\n" " allow = [argv[i + 1] for i, a in enumerate(argv)\n" " if a == '--allow-shrink' and i + 1 < len(argv)]\n" " old = {}\n", '解析 --allow-shrink'), (" if old[rel] - n > DROP_MIN and (old[rel] - n) / old[rel] > DROP_RATIO:\n" " shrunk.append((rel, old[rel], n))\n", " if old[rel] - n > DROP_MIN and (old[rel] - n) / old[rel] > DROP_RATIO:\n" " if any(a in rel for a in allow):\n" " print(' ↳ 合法重构(白名单):%s %d → %d 行' % (rel, old[rel], n))\n" " else:\n" " shrunk.append((rel, old[rel], n))\n", '白名单跳过报警'), ] # ── P2-2 CODEBUDDY.md(库内):标题 + 约定 #2/#3 执行者 ──────────────── C_PAIRS = [ ("## 改完必跑(四件套)\n", "## 改完必跑(七件套 · **顺序不可换**)\n\n" "> ⚠️ **顺序**:`docs-manifest.py` → `docs-archive-index.py`(**后者读前者**)→ 其余任意。\n" "> 顺序错会**静默**产出新旧混合(2026-09-14 实测:只跑 manifest 未跑 archive-index 时,后者只读模式直接报\n" "> 「表内容与 INDEX.md 不一致」)。`docs-archive-index.py` 已内置顺序断言:`--write` 时若 manifest 比最新档案旧会**拒绝**,\n" "> 确认要硬刷才加 `--force`。\n", '标题 + 顺序说明'), ("| 2 | **单篇 ≤ 30 KB**;新增档案超限**必须拆子页** | 单篇最大已 89 KB(档案 82),读一篇就吃满预算 | 写档案的人;`docs-manifest.py` 会报 |", "| 2 | **单篇 ≤ 30 KB**;新增档案超限**必须拆子页**。**标准拆法 = 新增子页 + 原页留指针**(历史档案按「只增不改」不回改正文,只在文末「修正(YYYY-MM-DD)」节加指针) | 单篇最大已 89 KB(档案 82),读一篇就吃满预算 | 写档案的人;`docs-manifest.py` 会报 + `docs-archive-index.py` 报缺失率 |", '约定 #2 补标准拆法'), ("| 3 | **日志按主题/月分片**,单文件 ≤ 50 KB(`append-only` 不变) | 单日日志已 377 KB | 写日志的人 |", "| 3 | **日志按月分片**,单文件 ≤ 50 KB(`append-only` 不变):`YYYY-MM.md` → 超限后新开 `YYYY-MM-下.md`,**原文件顶部留一行指针** | 单日日志已 377 KB | **谁当月第一次触到 50 KB 谁切**(下一位写入者先看行数) |", '约定 #3 补触发点与执行者'), ] # ── P2-3 CODEBUDDY.md(库内)· 锁章程:补「锁的生命周期」──────────────── C2_PAIRS = [ ("- 完工:`bash scripts/handoff-guard.sh --release <单号>` → 最后 `--release-exec`。\n", "- 完工:`bash scripts/handoff-guard.sh --release <单号>` → 最后 `--release-exec`。\n" "- 🔒 **锁的生命周期 = 任务的生命周期**(2026-09-14 用户明令):**抢到锁的任务,只有\"执行完成 → 反序释放\"才算完成**;\n" " ⛔ **禁止\"抢到锁、做一半、不解锁就结束回合 / 结束会话\"** —— 锁是独占资源,本库**无心跳机制** ⇒ 别人既等不到也判不出你死没死(会被迫空等,或被诱去违规接管 · R9)。\n" " 三条配套:① **抢锁前先列出收口步骤**(落地 → 校验 → 推送/对账 → 收尾);② **中途必须停**(等用户拍板 / 等窗口)⇒ **先释放再停**;\n" " ③ **结束语必须对锁状态负责** —— 写明\"已释放\",或**显式点名**\"锁仍在 `` + 原因 + 下一步\"(仅限释放通道不可用);⛔\"忘了 / 做不完就走\"不允许。\n", '库内 CODEBUDDY 补锁生命周期'), ] # ── P2-4 handoff-guard.sh:抢锁成功时当场把规矩打给持有者(机制层最有效)── H_PAIRS = [ (' echo "✓ 已持全局执行锁($OWNER)—— 完工请 --release-exec"\n', ' echo "✓ 已持全局执行锁($OWNER)"\n' ' echo " ⛔ **锁的生命周期 = 任务的生命周期**(2026-09-14 用户明令):执行完成 → 必须 `--release-exec` 才算完成;"\n' ' echo " 禁止抢锁做一半、不解锁就结束回合/会话(本库无心跳,带锁结束 = 把所有人挡在门外)。中途要停 ⇒ 先释放再停。"\n', '抢锁输出加硬提示'), ] def main(): print('落地包(%s)|库根 = %s' % ('dry-run' if DRY else '**APPLY**', LIB)) for rel, pairs in (('scripts/docs-manifest.py', M_PAIRS), ('scripts/handoff-guard.sh', H_PAIRS), ('CODEBUDDY.md', C2_PAIRS), ('scripts/docs-archive-index.py', A_PAIRS), ('scripts/docs-shrink-guard.py', S_PAIRS), ('CODEBUDDY.md', C_PAIRS)): try: patch(rel, pairs) except AssertionError as e: print(' %s' % e) print('\n⛔ 已中止,**未写入任何文件**(逐文件原子:前面已写的不会回滚,请先查清冲突)') return 1 if DRY: print('\n(dry-run 通过)加 --apply 落盘。') else: print('\n✅ 已落盘。接着手工执行:') print(' python3 scripts/docs-manifest.py') print(' python3 scripts/docs-archive-index.py --write') print(' python3 scripts/docs-audit.py && python3 scripts/docs-consistency.py ' '&& bash scripts/docs-sync-check.sh') print(' scp(同相对目录)+ 远端 chmod 600 / chown root:root') print(' git add scripts/docs-*.py CODEBUDDY.md INDEX.md archive-summaries.json docs-manifest.json && git commit') return 0 if __name__ == '__main__': raise SystemExit(main())