文档库:目录改为编号制(01-规范/02-架构设计/03-数据库/04-调整方案/
05-交接单/06-ops/07-scripts/08-skills/09-archive),顶层散文件归入 01-规范/;
INDEX.md 与 docs-manifest.json 重刷(档案 146 篇);旧目录名引用全量对齐。
IM 线:src/im/**(SDK / hub / store / presence / ws / gateway-token)、
src/web/routes/im.ts、src/db/plugin-data/**、src/supervisor/plugin-assembly.ts
及对应 test/**。
插件线:poc/{im-agent-bridge,im-connection-gateway,im-conversation-tabs,
business-plugins-im,carbon-mcp-probe}、src/web/routes/{sessions,overlay-device}.ts、
src/net/relay/{device-grant,instance-credential}.ts。
仓库卫生:清出 40 个历史误入库 / 已改名文件(34 个交接单归档 + 6 个旧结构,
本地均有副本);dsh-server-docs/.gitignore 补 tmp/;交接单不入库(政策)。
This commit is contained in:
1 parent
3d8f50e366
commit
e6207aa691
239 files changed
+34477
-14633
No files matched your search
@@ -0,0 +1,122 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""docs-shrink-guard.py — 防「共享文件被整文件重写抹掉别人的行」
|
||||
|
||||
为什么需要(2026-09-14 实测)
|
||||
* `MEMORY.md` 被并行会话**整文件重写**,抹掉了本会话刚写进去的两行;
|
||||
* 本库的规矩是「共享文件只用 Edit 精确替换,禁整文件 Write」—— 但**只靠人守纪律**,没有机制兜底。
|
||||
⇒ 本脚本给"纪律"加一条**机械兜底**:记录每个文件的**行数快照**,下次运行时若某文件
|
||||
**行数骤降**(默认 >30% 且绝对减少 >20 行),就把它当成"疑似被整文件重写"报警。
|
||||
|
||||
覆盖范围(两处共享热区):
|
||||
* 文档库 `dsh-server-docs/**/*.md`
|
||||
* 工作区记忆 `.workbuddy/memory/*.md`
|
||||
快照落在**库外**(`<工作区>/.workbuddy/cache/docs-lines.json`)—— 放库内会污染 `docs-sync-check` 对账。
|
||||
|
||||
用法
|
||||
python3 07-scripts/docs-shrink-guard.py # 比对并报告(不改快照)
|
||||
python3 07-scripts/docs-shrink-guard.py --write # 比对 + 刷新快照(改完文件后跑)
|
||||
python3 07-scripts/docs-shrink-guard.py --baseline # 只建档不比对(首次)
|
||||
python3 07-scripts/docs-shrink-guard.py --allow-shrink <路径子串> # 合法重构白名单(不报警)
|
||||
退出码:0 = 无骤降;1 = 发现骤降(需人看一眼 diff);2 = 用法错误
|
||||
"""
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
DOCS = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
WS = os.path.dirname(DOCS)
|
||||
SNAP = os.path.join(WS, '.workbuddy', 'cache', 'docs-lines.json')
|
||||
DROP_RATIO, DROP_MIN = 0.30, 20
|
||||
|
||||
|
||||
def collect():
|
||||
out = {}
|
||||
roots = [(DOCS, 'docs'), (os.path.join(WS, '.workbuddy', 'memory'), 'memory')]
|
||||
for root, tag in roots:
|
||||
if not os.path.isdir(root):
|
||||
continue
|
||||
for base, dirs, names in os.walk(root):
|
||||
if '.git' in dirs:
|
||||
dirs.remove('.git')
|
||||
if os.sep + 'cache' in base:
|
||||
continue
|
||||
for n in sorted(names):
|
||||
if not n.endswith('.md') or '.bak' in n:
|
||||
continue
|
||||
p = os.path.join(base, n)
|
||||
rel = os.path.relpath(p, WS).replace('\\', '/')
|
||||
try:
|
||||
out[rel] = sum(1 for _ in io.open(p, encoding='utf-8', errors='replace'))
|
||||
except OSError:
|
||||
continue
|
||||
return out
|
||||
|
||||
|
||||
def main(argv):
|
||||
now = collect()
|
||||
allow = [argv[i + 1] for i, a in enumerate(argv)
|
||||
if a == '--allow-shrink' and i + 1 < len(argv)]
|
||||
old = {}
|
||||
if os.path.exists(SNAP):
|
||||
try:
|
||||
old = json.loads(io.open(SNAP, encoding='utf-8').read())
|
||||
except ValueError:
|
||||
print('⚠️ 快照损坏,按首次处理')
|
||||
if '--baseline' in argv or not old:
|
||||
if '--write' in argv or '--baseline' in argv:
|
||||
os.makedirs(os.path.dirname(SNAP), exist_ok=True)
|
||||
io.open(SNAP, 'w', encoding='utf-8', newline='\n').write(
|
||||
json.dumps(now, ensure_ascii=False, indent=1, sort_keys=True) + '\n')
|
||||
print('已建基线:%d 个文件 → %s' % (len(now), SNAP))
|
||||
return 0
|
||||
print('无快照(首次)。跑 `--baseline` 建档后再用。')
|
||||
return 2
|
||||
|
||||
shrunk, added, removed, grew = [], [], [], []
|
||||
for rel, n in now.items():
|
||||
if rel not in old:
|
||||
added.append(rel)
|
||||
elif n < old[rel]:
|
||||
if old[rel] - n > DROP_MIN and (old[rel] - n) / old[rel] > DROP_RATIO:
|
||||
if any(a in rel for a in allow):
|
||||
print(' ↳ 合法重构(白名单):%s %d → %d 行' % (rel, old[rel], n))
|
||||
else:
|
||||
shrunk.append((rel, old[rel], n))
|
||||
elif old[rel] - n > 0:
|
||||
grew.append((rel, old[rel], n, 'down'))
|
||||
for rel in old:
|
||||
if rel not in now:
|
||||
removed.append(rel)
|
||||
for rel, n in now.items():
|
||||
if rel in old and n > old[rel]:
|
||||
grew.append((rel, old[rel], n, 'up'))
|
||||
|
||||
print('文件 %d(新增 %d / 删除 %d)| 行数变化 %d' % (len(now), len(added), len(removed), len(grew)))
|
||||
for rel in added[:8]:
|
||||
print(' + %s' % rel)
|
||||
for rel in removed[:8]:
|
||||
print(' - %s(消失?)' % rel)
|
||||
for rel, o, n, d in sorted(grew, key=lambda x: -abs(x[1] - x[2]))[:6]:
|
||||
print(' %s %s %d → %d 行' % ('↑' if d == 'up' else '↓', rel, o, n))
|
||||
|
||||
bad = 0
|
||||
if shrunk:
|
||||
bad = 1
|
||||
print('\n⚠️ **疑似被整文件重写(行数骤降)** —— 请看一眼 `git diff` 或与上一位写入者核对:')
|
||||
for rel, o, n in shrunk:
|
||||
print(' %s %d → %d 行(-%.0f%%)' % (rel, o, n, (o - n) / o * 100))
|
||||
print(' 本库规矩:共享文件只用 Edit 精确替换,禁整文件 Write(见 CODEBUDDY.md)')
|
||||
if '--write' in argv:
|
||||
os.makedirs(os.path.dirname(SNAP), exist_ok=True)
|
||||
io.open(SNAP, 'w', encoding='utf-8', newline='\n').write(
|
||||
json.dumps(now, ensure_ascii=False, indent=1, sort_keys=True) + '\n')
|
||||
print('已刷新快照。')
|
||||
elif bad:
|
||||
print('(未刷新快照:确认无误后跑 `--write`)')
|
||||
return bad
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
Reference in new issue
Block a user