build / build-and-scan (push) Waiting to run
文档库:目录改为编号制(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/;交接单不入库(政策)。
138 lines
6.3 KiB
Bash
138 lines
6.3 KiB
Bash
#!/usr/bin/env bash
|
||
# preflight-lock.sh — 【开工门禁】「先判定可锁定范围,确认锁定后再处理」
|
||
#
|
||
# 用户口径(2026-09-22):
|
||
# 「会话执行任务前先判断,当前任务涉及范围是否都可锁定,确认锁定后开始处理」
|
||
#
|
||
# 与 handoff-guard.sh 的关系:**门禁(本脚本,先判定)→ 抢锁(handoff-guard.sh,后动手)**。
|
||
# ⛔ 本脚本**不建任何锁**,只做判定与提示;它**不替换** handoff-guard.sh。
|
||
#
|
||
# 在 CODEBUDDY.md §6(并发纪律)里是**开工第 0 步**:判定全部可锁 ⇒ 才去 --claim-exec。
|
||
#
|
||
# 用法:
|
||
# bash preflight-lock.sh "<会话名>" <目标文件1> [目标文件2 ...]
|
||
#
|
||
# 退出码:0 = 全部可锁且已锁(可以开工)|1 = 存在不可锁项(停手)|2 = 用法错误
|
||
#
|
||
set -uo pipefail
|
||
|
||
ME="${1:-}"; shift || true
|
||
if [ -z "$ME" ] || [ $# -eq 0 ]; then
|
||
echo "用法:bash preflight-lock.sh \"<会话名>\" <目标文件...>" >&2
|
||
exit 2
|
||
fi
|
||
|
||
DOCS_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||
CODE_ROOT="${DSH_CODE_REPO:-D:/github/dsh_shenxian}"
|
||
WS_ROOT="${DSH_WS_ROOT:-E:/ProgramData/AIProject/aliyun-dsh-server}"
|
||
LOCKROOT="$DOCS_ROOT/05-交接单/.locks"
|
||
|
||
# ── 机制层(⛔ 不受 architecture.md 的 src/** 分层管辖,但**全平台共用**)────────
|
||
# 判据:这些文件任一改动都会改变**所有会话**的行为 ⇒ 与层④同性质(全局影响)。
|
||
# 2026-09-22 实测踩到:首版门禁把本任务自己的 4 个目标文件全判进【D】未归类 ⇒ 误报。
|
||
declare -a MECHANISM_RE=(
|
||
'^07-scripts/handoff-guard\.sh$' # 全局锁本体
|
||
'^07-scripts/lock-guard-hook\.py$' # 无锁拒写钩子
|
||
'^07-scripts/op-lock\.sh$' # 服务器侧操作锁
|
||
'^07-scripts/(skill-load-guard|stop-dialog-guard)\.py$'
|
||
'^07-scripts/docs-sync-check\.sh$' # 推送前对账(所有会话共用)
|
||
'^07-scripts/check-layering\.mjs$' # 分层 ratchet
|
||
'^07-scripts/layering-baseline\.json$'
|
||
'^state\.py$' # 所有会话的开机第 0 步
|
||
'^CODEBUDDY\.md$' # 每会话自动加载的规则
|
||
)
|
||
|
||
# ── 不可锁资源清单(命中 ⇒ 必须走"秒级独占"或串行排队)──────────────
|
||
# 判据来源:architecture.md R1–R4 层号 + docs/tsconfig.json outDir
|
||
declare -a NONLOCKABLE_RE=(
|
||
'^src/(config|crypto|isolation|index)\.ts$' # 层④ 零业务依赖 · 全层依赖
|
||
'^src/db/schema\.ts$' # 迁移号 = 全局单调序列
|
||
'^src/web/server\.ts$' # 唯一挂载注册点
|
||
'^package\.json$' # 依赖账本单一
|
||
'^07-scripts/layering-baseline\.json$' # 分层 ratchet 单一
|
||
)
|
||
|
||
declare -a SHARED_RE=(
|
||
'^lib/' # 构建产物(outDir=lib)· 全局独占
|
||
'^node_modules/'
|
||
)
|
||
|
||
warn_shared=(); need_skeleton=(); ok_files=(); unknown=(); mechanism=()
|
||
|
||
for f in "$@"; do
|
||
rel="${f//\\//}"
|
||
# 剥掉已知根前缀(顺序要紧:先长后短,否则残留中间段 ⇒ 正则永不匹配)
|
||
rel="${rel#*aliyun-dsh-server/}"
|
||
rel="${rel#*dsh-server-docs/}"
|
||
rel="${rel#*dsh_shenxian/}"
|
||
rel="${rel#./}"
|
||
matched=0
|
||
for re in "${MECHANISM_RE[@]}"; do
|
||
if printf '%s' "$rel" | grep -qE "$re"; then mechanism+=("$rel"); matched=1; break; fi
|
||
done
|
||
[ "$matched" = 1 ] && continue
|
||
for re in "${NONLOCKABLE_RE[@]}"; do
|
||
if printf '%s' "$rel" | grep -qE "$re"; then need_skeleton+=("$rel"); matched=1; break; fi
|
||
done
|
||
[ "$matched" = 1 ] && continue
|
||
for re in "${SHARED_RE[@]}"; do
|
||
if printf '%s' "$rel" | grep -qE "$re"; then warn_shared+=("$rel"); matched=1; break; fi
|
||
done
|
||
[ "$matched" = 1 ] && continue
|
||
if printf '%s' "$rel" | grep -qE '^(src|poc|test|web|docs|scripts|交接单|skills)/'; then
|
||
ok_files+=("$rel")
|
||
else
|
||
unknown+=("$rel")
|
||
fi
|
||
done
|
||
|
||
echo "══════════ 开工门禁 · 可锁定范围判定 ══════════"
|
||
echo "会话:$ME"
|
||
echo
|
||
|
||
echo "【A】可独立锁定(域锁,全程持有):${#ok_files[@]} 个"
|
||
for f in "${ok_files[@]:0:8}"; do echo " ✓ $f"; done
|
||
[ ${#ok_files[@]} -gt 8 ] && echo " …还有 $(( ${#ok_files[@]} - 8 )) 个"
|
||
|
||
echo
|
||
echo "【B】不可全程锁定(层④/迁移号/唯一注册点)→ 改走**秒级独占**:${#need_skeleton[@]} 个"
|
||
for f in "${need_skeleton[@]}"; do echo " ⚠ $f"; done
|
||
[ ${#need_skeleton[@]} -eq 0 ] && echo " (无)"
|
||
|
||
echo
|
||
echo "【E】机制层(⛔ 不受 src 分层管辖,但**全平台共用** ⇒ 与层④同性质):${#mechanism[@]} 个"
|
||
for f in "${mechanism[@]}"; do echo " ◆ $f"; done
|
||
[ ${#mechanism[@]} -eq 0 ] && echo " (无)"
|
||
[ ${#mechanism[@]} -gt 0 ] && echo " ⚠ 改这些 = 改所有会话的行为 ⇒ 必须: ①独占②兼容旧行为③改后跑自证"
|
||
|
||
echo
|
||
echo "【C】共享资源(构建产物 / 依赖树)→ 纳入发布锁:${#warn_shared[@]} 个"
|
||
for f in "${warn_shared[@]:0:4}"; do echo " ⚠ $f"; done
|
||
[ ${#warn_shared[@]} -eq 0 ] && echo " (无)"
|
||
|
||
if [ ${#unknown[@]} -gt 0 ]; then
|
||
echo
|
||
echo "【D】未归类路径(⛔ 必须先想清"属哪个域",R2):${#unknown[@]} 个"
|
||
for f in "${unknown[@]:0:6}"; do echo " ? $f"; done
|
||
fi
|
||
|
||
echo
|
||
echo "════════════════════════════════════════════"
|
||
rc=0
|
||
[ ${#unknown[@]} -gt 0 ] && rc=1
|
||
[ ${#mechanism[@]} -gt 0 ] && rc=1
|
||
if [ "$rc" = 1 ]; then
|
||
echo "结论:**暂不可并行开工** ——"
|
||
[ ${#unknown[@]} -gt 0 ] && echo " · 【D】${#unknown[@]} 个未归类路径:先定域(R2)"
|
||
if [ ${#mechanism[@]} -gt 0 ]; then
|
||
echo " · 【E】${#mechanism[@]} 个机制层文件:**全平台共用** ⇒ ⛔ 不得与其他会话并行"
|
||
echo " ⇒ 仅当**确认无其他会话在跑**时,才允许独占开工;否则停手等窗口"
|
||
fi
|
||
exit 1
|
||
fi
|
||
echo "结论:**可以锁定** ——"
|
||
echo " · ${#ok_files[@]} 个走域锁|${#need_skeleton[@]} 个走秒级独占|${#warn_shared[@]} 个并发布锁"
|
||
echo " · 下一步:bash handoff-guard.sh --claim-exec \"$ME\" --domains <由 A 组反推的域>"
|
||
echo " ⛔ 本脚本是**提示性门禁**:hook 只拦 Write/Edit,Bash 写入不受限(有意安全阀)"
|
||
exit 0
|