Files
dsh_ai1net_server/.workbuddy/待落地/apply-索引优化-20260914.py
T
admin ce8e6ceed9 chore(工作区): 纳入版本控制基线(回收 411 MB 过程产物)
回收 411 MB(470 M → 58.8 M),全部经回收站,可恢复:
- 待清理/(146.2 M,含 relay 分片 128 M 与 42 项过程目录)
- tmp/(32.4 M,按接续棒命名的过程临时区)
- .workbuddy/tmp/(39.5 M)
- 4 份 workbuddy.db 冗余副本(101 M,09-23 事故的坏副本 / 抢救产物)
- tmp/im16/gw/centrifugo 二进制(63.9 M,可重下)+ 缓存残留

入库范围:常驻规则(CODEBUDDY.md / README.md / state.py)、在途接续入口与
接续包、docs/、交付物/、交接单/、归档/、scripts/、.codebuddy/、
.workbuddy/memory/;共 398 件,其中 >60 KB 的 26 件全为文档。

排除(.gitignore):tmp/、待清理/、运行态日志与缓存、*.db 与 DB 备份整目录、
打包二进制(*.tar.gz / *.tgz)、记忆修复前备份。
2026-09-24 07:51:03 +08:00

214 lines
13 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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"
" ③ **结束语必须对锁状态负责** —— 写明\"已释放\",或**显式点名**\"锁仍在 `<OWNER>` + 原因 + 下一步\"(仅限释放通道不可用);⛔\"忘了 / 做不完就走\"不允许。\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())