@haiyangbg/buildbeat 2.0.2 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -301
- package/README.en.md +6 -19
- package/README.md +6 -19
- package/SKILL.md +168 -219
- package/bin/buildbeat.js +14 -2
- package/docs/CAPABILITY-MATRIX.md +13 -55
- package/docs/README.md +12 -14
- package/docs/RELEASING.md +8 -9
- package/docs/v2/RFC-0001-product-definition.md +2 -0
- package/docs/v2/RFC-0003-workflow-policy.md +2 -0
- package/docs/v2/guide/00-how-to-talk.md +3 -3
- package/docs/v2/guide/01-quickstart.md +12 -12
- package/docs/v2/guide/03-policy-guide.md +1 -1
- package/docs/v2/guide/06-evidence-guide.md +3 -3
- package/docs/v2/guide/07-approval-guide.md +10 -10
- package/docs/v2/guide/10-recovery.md +6 -6
- package/docs/v2/guide/11-session-handoff.en.md +2 -2
- package/docs/v2/guide/11-session-handoff.md +2 -2
- package/docs/v2/guide/README.md +0 -6
- package/lessons.md +52 -71
- package/package.json +3 -8
- package/src/v2/cli/run.js +33 -23
- package/src/v2/engine/risk-preset.js +1 -1
- package/src/v2/runtime/notify.js +5 -5
- package/src/v2/runtime/overview.js +7 -7
- package/templates/ARCHITECTURE.md +1 -1
- package/templates/contracts/PROTOCOL.md +2 -10
- package/templates/gitignore.template +0 -3
- package/templates/pm/adr/README.md +1 -1
- package/templates/pm/decisions.md +4 -5
- package/templates/standards/CODE.md +1 -1
- package/templates/standards/DESIGN.md +1 -1
- package/templates/standards/REVIEW.md +2 -2
- package/templates/standards/STACK.md +2 -8
- package/templates/v2/AGENTS.md +18 -18
- package/templates/v2/BUILDBEAT.md +2 -3
- package/templates/v2/CLAUDE.md +1 -1
- package/templates/v2/run-config.example.yaml +1 -1
- package/templates/v2//346/214/207/346/214/245/345/217/260.md +6 -6
- package/bin/buildbeat-v2.js +0 -18
- package/bin/solobaton.js +0 -6
- package/docs/CHECKS.md +0 -326
- package/docs/CLI.md +0 -245
- package/docs/LEGACY-V1.16-MIGRATION.md +0 -54
- package/docs/v2/guide/08-migration-v1.md +0 -72
- package/example/.buildbeat/manifest.json +0 -45
- package/example/AGENTS.md +0 -19
- package/example/ARCHITECTURE.md +0 -39
- package/example/BUILDBEAT.md +0 -17
- package/example/CLAUDE.md +0 -7
- package/example/README.md +0 -75
- package/example/contracts/PROTOCOL.md +0 -38
- package/example/pm/NOW.md +0 -22
- package/example/pm/adr/ADR-0001-local-first-sqlite.md +0 -25
- package/example/pm/adr/README.md +0 -7
- package/example/pm/archive//344/270/200/346/234/237/evidence/gate1.md +0 -5
- package/example/pm/archive//344/270/200/346/234/237/evidence/gate2.md +0 -5
- package/example/pm/archive//344/270/200/346/234/237/evidence/gate3.md +0 -5
- package/example/pm/archive//344/270/200/346/234/237/evidence/gate4.md +0 -5
- package/example/pm/archive//344/270/200/346/234/237/evidence/implementation.md +0 -5
- package/example/pm/decisions.md +0 -20
- package/example/pm/status//344/272/247/345/223/201.md +0 -20
- package/example/pm/status//345/205/250/346/240/210.md +0 -15
- package/example/pm/status//346/265/213/350/257/225.md +0 -15
- package/example/pm//344/270/200/346/234/237-/347/234/213/346/235/277.md +0 -97
- package/example/standards/CODE.md +0 -18
- package/example/standards/DESIGN.md +0 -34
- package/example/standards/REVIEW.md +0 -16
- package/example/standards/STACK.md +0 -31
- package/src/cli.js +0 -323
- package/src/constants.js +0 -202
- package/src/doctor.js +0 -267
- package/src/planner.js +0 -251
- package/src/project.js +0 -844
- package/src/upgrader.js +0 -1249
- package/src/v2/presets/risk/legacy-four-gates.yaml +0 -44
- package/src/writer.js +0 -534
- package/templates/.claude/agents/reviewer.md +0 -62
- package/templates/AGENTS.md +0 -85
- package/templates/BUILDBEAT.md +0 -13
- package/templates/CLAUDE.md +0 -7
- package/templates/pm/NOW.md +0 -26
- package/templates/pm/changes/README.md +0 -44
- package/templates/pm/status/README.md +0 -32
- package/templates/pm//345/275/223/346/234/237/347/234/213/346/235/277.md +0 -62
- package/templates/scripts/bus-check.sh +0 -1875
- package/templates/scripts/design-preview.sh +0 -44
- package/templates/scripts/drift-check.sh +0 -112
- package/templates/scripts/pre-commit.sh +0 -74
- package/templates/scripts/verify-status.sh +0 -105
- package/templates//346/214/207/346/214/245/345/217/260.md +0 -58
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# design-preview.sh —— Gate2 真渲染拍板(总线规则⑩)。
|
|
3
|
-
# 把 design/design_<N>期/ 起成本地静态服务,用户在浏览器点过关键流之后再拍板;
|
|
4
|
-
# 静态稿 / 截图不充当拍板对象。设计走查复核时同样可用。
|
|
5
|
-
# 本脚本按自身位置定位协调层根,放 <根>/scripts/ 或 <根>/pm/scripts/ 均可(紧凑布局见 SKILL §3);下文命令示例按默认布局写。
|
|
6
|
-
# 用法:
|
|
7
|
-
# bash scripts/design-preview.sh 1 # 渲染 design/design_1期 → http://localhost:8799
|
|
8
|
-
# bash scripts/design-preview.sh 2 8801 # 指定端口
|
|
9
|
-
# bash scripts/design-preview.sh design_2期 # 也接受完整目录名
|
|
10
|
-
set -uo pipefail
|
|
11
|
-
# 协调层根 = 向上最近的含 pm/NOW.md 的目录(设计稿在 <根>/design/);SDIR 只用于把用法提示打成实际路径。见 bus-check.sh 同段注释。
|
|
12
|
-
_sd="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
13
|
-
ROOT="$_sd"; for _ in 1 2 3 4; do [ -f "$ROOT/pm/NOW.md" ] && break; ROOT="$(dirname "$ROOT")"; done
|
|
14
|
-
[ -f "$ROOT/pm/NOW.md" ] || ROOT="$(cd "$_sd/.." && pwd)"
|
|
15
|
-
SDIR="${_sd#"$ROOT"/}"; [ "$SDIR" = "$_sd" ] && SDIR="."
|
|
16
|
-
|
|
17
|
-
N="${1:?用法: bash $SDIR/design-preview.sh <期号|目录名> [端口,默认 8799]}"
|
|
18
|
-
PORT="${2:-8799}"
|
|
19
|
-
|
|
20
|
-
DIR="$ROOT/design/design_${N}期"
|
|
21
|
-
[ -d "$DIR" ] || DIR="$ROOT/design/$N"
|
|
22
|
-
if [ ! -d "$DIR" ]; then
|
|
23
|
-
echo "✗ 找不到设计目录: design/design_${N}期 或 design/$N" >&2
|
|
24
|
-
echo " 现有:"
|
|
25
|
-
existing_n=0
|
|
26
|
-
for existing in "$ROOT"/design/design_*; do
|
|
27
|
-
[ -d "$existing" ] || continue
|
|
28
|
-
printf ' - %s\n' "${existing#"$ROOT"/}"
|
|
29
|
-
existing_n=$((existing_n + 1))
|
|
30
|
-
done
|
|
31
|
-
[ "$existing_n" -gt 0 ] || echo " (无)"
|
|
32
|
-
exit 1
|
|
33
|
-
fi
|
|
34
|
-
|
|
35
|
-
ENTRY=""
|
|
36
|
-
for candidate in "$DIR"/*.html; do
|
|
37
|
-
[ -f "$candidate" ] || continue
|
|
38
|
-
ENTRY="$candidate"
|
|
39
|
-
break
|
|
40
|
-
done
|
|
41
|
-
echo "▸ 渲染 ${DIR#"$ROOT"/} → http://localhost:$PORT/"
|
|
42
|
-
[ -n "${ENTRY:-}" ] && echo "▸ 入口: http://localhost:$PORT/$(basename "$ENTRY" | sed 's/ /%20/g')"
|
|
43
|
-
echo "▸ Gate2 拍板前:浏览器里把关键流点一遍(规则⑩)。Ctrl-C 停止。"
|
|
44
|
-
exec python3 -m http.server "$PORT" --directory "$DIR"
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# drift-check.sh —— 生产漂移检测(治「git ≠ 生产」与平台侧 env/secret 漂移,BuildBeat lessons.md 第 13 条)
|
|
3
|
-
# 比对「部署平台当前配置(env 指纹 + 镜像/版本 tag)」vs 基线快照 bus-baseline.json(与本脚本同目录)。
|
|
4
|
-
# 本脚本按自身位置定位协调层根,放 <根>/scripts/ 或 <根>/pm/scripts/ 均可(紧凑布局见 SKILL §3);下文命令示例按默认布局写。
|
|
5
|
-
# ⚠ 能力边界:检测「平台配置 vs 基线」——把"配置被改"暴露出来,逼"改完即确认部署 + 刷基线";
|
|
6
|
-
# 不检测「running 容器 vs 平台配置」(配置改了没重新部署、容器跑旧值),后者需平台运行时 API,各项目自行增强。
|
|
7
|
-
# 🔴 红线:env value 只在内存进 sha256,**绝不落盘 / 绝不打印**;基线与输出只有 key 名 + 指纹。
|
|
8
|
-
# 诚实边界:高熵 secret 的指纹不可逆;低熵值(true/false/端口/开关类)key 名已知,可被字典猜出——
|
|
9
|
-
# 基线 bus-baseline.json 按半敏感文件对待:入私仓可以,勿公开传播。
|
|
10
|
-
#
|
|
11
|
-
# 项目接入点(必需):live-config.sh <app名>(与本脚本同目录)—— 自行调用部署平台 CLI,stdout 按行输出:
|
|
12
|
-
# tag <镜像tag或版本号> (可选,一行)
|
|
13
|
-
# env <KEY>=<VALUE> (每个环境变量一行;VALUE 只经内存管道,本脚本只留指纹)
|
|
14
|
-
# 查询失败请返回非 0,别输出半截结果。
|
|
15
|
-
# 用法:
|
|
16
|
-
# bash scripts/drift-check.sh # 检测(与 bus-check 同目录时被自动调用)
|
|
17
|
-
# bash scripts/drift-check.sh --update-baseline # 刷新基线(改 env/secret 或部署后跑 = 「确认已部署」动作)
|
|
18
|
-
# BUS_CHECK_NO_LIVE=1 ... # 跳过线上查询
|
|
19
|
-
# 退出码:0 = 无漂移/跳过/无法判定;1 = --update-baseline 失败(基线保护);2 = 确凿检出漂移(bus-check --strict 据此拦)
|
|
20
|
-
set -uo pipefail
|
|
21
|
-
# 协调层根 = 向上最近的含 pm/NOW.md 的目录;SDIR = 本脚本目录(相对根)。见 bus-check.sh 同段注释。
|
|
22
|
-
_sd="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
23
|
-
ROOT="$_sd"; for _ in 1 2 3 4; do [ -f "$ROOT/pm/NOW.md" ] && break; ROOT="$(dirname "$ROOT")"; done
|
|
24
|
-
[ -f "$ROOT/pm/NOW.md" ] || ROOT="$(cd "$_sd/.." && pwd)"
|
|
25
|
-
SDIR="${_sd#"$ROOT"/}"; [ "$SDIR" = "$_sd" ] && SDIR="."
|
|
26
|
-
cd "$ROOT" || exit 1
|
|
27
|
-
BASELINE="$SDIR/bus-baseline.json"
|
|
28
|
-
UPDATE=0; [ "${1:-}" = "--update-baseline" ] && UPDATE=1
|
|
29
|
-
|
|
30
|
-
# 监控应用:app名|代码子仓(镜像tag↔git tag 锚定:在子仓 git 里查 v<tag> 是否存在;无 tag 规范填 - 跳过)
|
|
31
|
-
APPS=(
|
|
32
|
-
"<应用1>|<代码子仓1>"
|
|
33
|
-
"<应用2>|-"
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
if command -v sha256sum >/dev/null 2>&1; then SHACMD="sha256sum"; else SHACMD="shasum -a 256"; fi
|
|
37
|
-
|
|
38
|
-
# 拉某 app 当前快照 {imageTag, perKeyFp};value 只进 sha256 前 10 位;失败返回非 0
|
|
39
|
-
_snapshot(){
|
|
40
|
-
local name="$1" out imgtag perkey
|
|
41
|
-
out=$(bash "$SDIR/live-config.sh" "$name" 2>/dev/null) || return 1
|
|
42
|
-
[ -z "$out" ] && return 1
|
|
43
|
-
imgtag=$(printf '%s\n' "$out" | awk '$1=="tag"{print $2; exit}')
|
|
44
|
-
perkey=$(printf '%s\n' "$out" | grep '^env ' | sed 's/^env //' \
|
|
45
|
-
| while IFS='=' read -r k v; do printf '%s\t%s\n' "$k" "$(printf '%s' "$k=$v" | $SHACMD | cut -c1-10)"; done \
|
|
46
|
-
| jq -R -s -c 'split("\n")|map(select(length>0)|split("\t")|{(.[0]):.[1]})|add // {}')
|
|
47
|
-
[ -z "$perkey" ] && perkey='{}' # env 空/解析失败→兜底{},不误报"查询失败"
|
|
48
|
-
jq -cn --arg t "${imgtag:-}" --argjson pk "$perkey" '{imageTag:$t, perKeyFp:$pk}'
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if [ "${BUS_CHECK_NO_LIVE:-0}" = "1" ] || [ ! -f "$SDIR/live-config.sh" ] || ! command -v jq >/dev/null 2>&1; then
|
|
52
|
-
echo " (漂移检测跳过:需 $SDIR/live-config.sh + jq + 不设 BUS_CHECK_NO_LIVE)"; exit 0
|
|
53
|
-
fi
|
|
54
|
-
|
|
55
|
-
# ── 刷新基线 ──
|
|
56
|
-
if [ "$UPDATE" = "1" ]; then
|
|
57
|
-
acc="{}"; fail=0
|
|
58
|
-
for pair in "${APPS[@]:-}"; do
|
|
59
|
-
[ -n "$pair" ] || continue
|
|
60
|
-
name="${pair%%|*}"
|
|
61
|
-
snap=$(_snapshot "$name") || { echo " ⚠️ $name 查询失败"; fail=1; continue; }
|
|
62
|
-
acc=$(printf '%s' "$acc" | jq -c --arg n "$name" --argjson s "$snap" '. + {($n):$s}')
|
|
63
|
-
done
|
|
64
|
-
if [ "$fail" = "1" ]; then
|
|
65
|
-
echo " ✗ 有应用查询失败 —— 基线未写入(保留旧基线);修好平台 CLI/凭据后重跑 --update-baseline"
|
|
66
|
-
exit 1
|
|
67
|
-
fi
|
|
68
|
-
printf '%s' "$acc" | jq --arg d "$(date +%Y-%m-%d)" --arg s "$SDIR" \
|
|
69
|
-
'{updated:$d, note:("🔴只存指纹不存value;改env/secret或部署后跑 " + $s + "/drift-check.sh --update-baseline"), apps:.}' \
|
|
70
|
-
> "$BASELINE"
|
|
71
|
-
echo " ✅ 基线已刷新 → $BASELINE"
|
|
72
|
-
exit 0
|
|
73
|
-
fi
|
|
74
|
-
|
|
75
|
-
# ── 检测 ──
|
|
76
|
-
if [ ! -f "$BASELINE" ]; then
|
|
77
|
-
echo " ⚠️ 无基线 → 先跑: bash $SDIR/drift-check.sh --update-baseline"; exit 0
|
|
78
|
-
fi
|
|
79
|
-
|
|
80
|
-
drift=0; checked=0
|
|
81
|
-
for pair in "${APPS[@]:-}"; do
|
|
82
|
-
[ -n "$pair" ] || continue
|
|
83
|
-
name="${pair%%|*}"; subrepo="${pair##*|}"
|
|
84
|
-
snap=$(_snapshot "$name") || { printf " %-16s (查询失败,跳过)\n" "$name"; continue; }
|
|
85
|
-
checked=1
|
|
86
|
-
imgtag=$(printf '%s' "$snap" | jq -r '.imageTag')
|
|
87
|
-
base_app=$(jq -c --arg n "$name" '.apps[$n] // empty' "$BASELINE" 2>/dev/null)
|
|
88
|
-
if [ -z "$base_app" ]; then printf " ⚠️ %-16s 基线无此应用 → --update-baseline\n" "$name"; drift=1; continue; fi
|
|
89
|
-
base_tag=$(printf '%s' "$base_app" | jq -r '.imageTag // ""')
|
|
90
|
-
# env 漂移:并集 key 分类 +新增 -删除 ≠值变(只报 key 名,不报 value)
|
|
91
|
-
changed=$(jq -rn \
|
|
92
|
-
--argjson b "$(printf '%s' "$base_app" | jq '.perKeyFp')" \
|
|
93
|
-
--argjson c "$(printf '%s' "$snap" | jq '.perKeyFp')" \
|
|
94
|
-
'($b+$c|keys) | map(. as $k |
|
|
95
|
-
if $b[$k]==null then "+"+$k
|
|
96
|
-
elif $c[$k]==null then "-"+$k
|
|
97
|
-
elif $b[$k]!=$c[$k] then "≠"+$k
|
|
98
|
-
else empty end) | join(" ")')
|
|
99
|
-
msg=""
|
|
100
|
-
[ -n "$imgtag" ] && [ "$imgtag" != "$base_tag" ] && msg="$msg 镜像 $base_tag→$imgtag(新部署?)"
|
|
101
|
-
[ -n "$changed" ] && msg="$msg env:[$changed]"
|
|
102
|
-
gt=""
|
|
103
|
-
if [ "$subrepo" != "-" ] && [ -d "$subrepo/.git" ] && [ -n "$imgtag" ]; then
|
|
104
|
-
# 锚定假设「镜像 tag X ↔ git tag vX」;你的 tag 规范不同就改这一行
|
|
105
|
-
git -C "$subrepo" rev-parse "v$imgtag" >/dev/null 2>&1 || gt=" 🏷git 无 v$imgtag(git≠生产?)"
|
|
106
|
-
fi
|
|
107
|
-
if [ -n "$msg" ] || [ -n "$gt" ]; then printf " ⚠️ %-16s%s%s\n" "$name" "$msg" "$gt"; drift=1
|
|
108
|
-
else printf " ✅ %-16s 配置/镜像==基线 (tag %s)\n" "$name" "${imgtag:-?}"; fi
|
|
109
|
-
done
|
|
110
|
-
if [ "$checked" = "0" ]; then echo " ⚠️ 没有成功查到任何应用(APPS 为空或全部查询失败)—— 无法判定漂移,检查 APPS 配置/平台 CLI/凭据"; exit 0
|
|
111
|
-
elif [ "$drift" = "0" ]; then echo " —— 无漂移"; exit 0
|
|
112
|
-
else echo " ⚠️ 有漂移 → 配置改了是否已重新部署?新部署是否打 tag + 跑 --update-baseline?"; exit 2; fi
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# pre-commit.sh —— 红线机器闸。规则不能只靠自觉:每条规则问一句「违反了会怎样」,答案得是「拦下来」。
|
|
3
|
-
# 闸①:凭据不入 git(红线1)—— gitleaks 扫暂存区,报警即拦;未装 gitleaks 只警告不拦(装:brew install gitleaks)。
|
|
4
|
-
# 闸②:协调层可信 —— bus-check --strict 检出任一 conflict/error finding 即拦(离线模式,不查线上;仅 meta 仓生效,子仓自动跳过)。
|
|
5
|
-
# 闸③:状态分写(规则⑦)—— 一次 commit 暂存 ≥2 个域的 status 文件即拦(单会话=单域,不该同时写别人的;
|
|
6
|
-
# 换期压缩仪式例外:连同 pm/archive/ 一起提交即放行,或 BUS_RITUAL=1 git commit)。
|
|
7
|
-
# 闸④:不批量 stage(红线2)—— 暂存文件数 > BUS_MAX_STAGED(默认 40)即拦,像 `git add -A` 的手笔;
|
|
8
|
-
# 确属大重构:BUS_ALLOW_BULK=1 git commit 放行一次。
|
|
9
|
-
# 闸⑤:契约先落盘(规则②,仅提醒不拦)—— 暂存文件名疑似接口边界(route/controller/api/schema/proto)时,
|
|
10
|
-
# 提醒检查 PROTOCOL.md 是否同步;契约在 meta 仓、代码在子仓,跨仓无法原子核验,故不拦以免误伤。
|
|
11
|
-
# 装法(meta 仓 + 各代码子仓,每仓各装一次;子仓从 meta 仓拷同一份即可):
|
|
12
|
-
# cp scripts/pre-commit.sh .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit
|
|
13
|
-
# (紧凑布局下脚本在 pm/scripts/,拷贝源路径随之改;闸②两种布局都能自己找到 bus-check)
|
|
14
|
-
# (钩子想进版本控制可改用:git config core.hooksPath <钩子目录>,克隆后一次 config 即带)
|
|
15
|
-
# 配套:红线3 已禁 --no-verify,这道闸绕不过去;bus-check 会自检「闸装了没」。
|
|
16
|
-
set -uo pipefail
|
|
17
|
-
cd "$(git rev-parse --show-toplevel)" || exit 1
|
|
18
|
-
|
|
19
|
-
# ── 闸①:gitleaks(v8.19+ 用 `gitleaks git`,旧版退回 `protect`)──
|
|
20
|
-
if command -v gitleaks >/dev/null 2>&1; then
|
|
21
|
-
if gitleaks git -h >/dev/null 2>&1; then
|
|
22
|
-
gitleaks git --staged --no-banner --redact >/dev/null 2>&1
|
|
23
|
-
else
|
|
24
|
-
gitleaks protect --staged --no-banner --redact >/dev/null 2>&1
|
|
25
|
-
fi || { echo "⛔ gitleaks 报警:疑似凭据进入暂存区,已拦截。看细节重跑:gitleaks git --staged(确认误报则登记 .gitleaksignore)"; exit 1; }
|
|
26
|
-
else
|
|
27
|
-
echo "⚠️ 未装 gitleaks —— 红线1(凭据不入 git)当前没有机器闸,只剩自觉。装:brew install gitleaks"
|
|
28
|
-
fi
|
|
29
|
-
|
|
30
|
-
# ── 闸②:bus-check --strict(只有协调层仓同时有 pm/NOW.md 与 bus-check.sh;两种布局都认,见 SKILL §3)──
|
|
31
|
-
BC=""; for p in scripts/bus-check.sh pm/scripts/bus-check.sh; do [ -f "$p" ] && { BC="$p"; break; }; done
|
|
32
|
-
if [ -f pm/NOW.md ] && [ -n "$BC" ]; then
|
|
33
|
-
out=$(BUS_CHECK_NO_FETCH=1 BUS_CHECK_NO_LIVE=1 bash "$BC" --strict 2>&1) || {
|
|
34
|
-
printf '%s\n' "$out" | grep -E "⚠️|⛔"
|
|
35
|
-
echo "⛔ bus-check --strict 未过 —— 全量输出看:bash $BC"
|
|
36
|
-
exit 1
|
|
37
|
-
}
|
|
38
|
-
fi
|
|
39
|
-
|
|
40
|
-
STAGED=$(git -c core.quotepath=false diff --cached --name-only) # quotepath=false:中文文件名按原样输出,否则被引号+八进制转义,规则匹配不上
|
|
41
|
-
|
|
42
|
-
# ── 闸③:状态分写(规则⑦)—— 一次 commit 只该动一个域的 status ──
|
|
43
|
-
st_count=$(printf '%s\n' "$STAGED" | grep -c '^pm/status/.*\.md$' 2>/dev/null | tr -d ' ')
|
|
44
|
-
st_count=${st_count:-0}
|
|
45
|
-
if [ "$st_count" -ge 2 ] && [ "${BUS_RITUAL:-0}" != "1" ]; then
|
|
46
|
-
# 排除 status/README.md 自身;换期压缩仪式(连同 archive/ 提交)放行
|
|
47
|
-
real=$(printf '%s\n' "$STAGED" | awk '/^pm\/status\/.*\.md$/ && $0 !~ /README\.md$/ {n++} END {print n+0}')
|
|
48
|
-
has_archive=$(printf '%s\n' "$STAGED" | grep -c '^pm/archive/' 2>/dev/null | tr -d ' ')
|
|
49
|
-
if [ "${real:-0}" -ge 2 ] && [ "${has_archive:-0}" = "0" ]; then
|
|
50
|
-
echo "⛔ 一次 commit 暂存了 $real 个域的 status(规则⑦:各域只写自己的)—— 分开提交;换期压缩仪式请连同 pm/archive/ 一起提交(或 BUS_RITUAL=1 git commit)"
|
|
51
|
-
exit 1
|
|
52
|
-
fi
|
|
53
|
-
fi
|
|
54
|
-
|
|
55
|
-
# ── 闸④:不批量 stage(红线2)——像 git add -A 的手笔即拦 ──
|
|
56
|
-
staged_n=$(printf '%s\n' "$STAGED" | grep -c . | tr -d ' ')
|
|
57
|
-
MAXN="${BUS_MAX_STAGED:-40}"
|
|
58
|
-
if [ "${staged_n:-0}" -gt "$MAXN" ] && [ "${BUS_ALLOW_BULK:-0}" != "1" ]; then
|
|
59
|
-
echo "⛔ 暂存了 $staged_n 个文件(>$MAXN)—— 像 \`git add -A\`(红线2:只 stage 自己域的具体文件,多仓分别提交);确属大重构:BUS_ALLOW_BULK=1 git commit"
|
|
60
|
-
exit 1
|
|
61
|
-
fi
|
|
62
|
-
|
|
63
|
-
# ── 闸⑤:契约先落盘(规则②,仅提醒)——只看**提供方**(Controller/路由/schema 定义) ──
|
|
64
|
-
# 消费方(src/api/ 等客户端调用层、前端页面 router)是「跟随契约」不是「改契约」,不提醒——
|
|
65
|
-
# 宽匹配在真实仓回放中 59% 提交误响,常驻红字=没有红字(评估 D2)。按项目调下面两个 env;
|
|
66
|
-
# 更精的收法(自行升级):只匹配 diff 内容里新增/删除的路由定义与 DTO 字段,或只在重轨提醒。
|
|
67
|
-
CONTRACT_HINT="${BUS_CONTRACT_HINT:-controller|endpoint|schema|\.proto|routes?/|router\.(go|py|rb|php|java|kt)}"
|
|
68
|
-
CONTRACT_SKIP="${BUS_CONTRACT_SKIP:-(^|/)api(s)?/|(^|/)src/router/|/client(s)?/|request}"
|
|
69
|
-
hint_hits=$(printf '%s\n' "$STAGED" | grep -iE "$CONTRACT_HINT" | grep -viE "$CONTRACT_SKIP" || true)
|
|
70
|
-
if [ -n "$hint_hits" ] && ! printf '%s\n' "$STAGED" | grep -q 'PROTOCOL\.md'; then
|
|
71
|
-
echo "⚠️ 暂存文件疑似动了接口**提供方**($(printf '%s\n' "$hint_hits" | head -1) 等)—— 跨边界行为变了的话,先改 contracts/PROTOCOL.md 再动代码(规则②;此为提醒不拦截)"
|
|
72
|
-
fi
|
|
73
|
-
|
|
74
|
-
exit 0
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# verify-status.sh —— 工程层验证能力(证据分级 L3「自动化测试过」的兜底;bus-check §2.7 自动调用)
|
|
3
|
-
# 本脚本按自身位置定位协调层根,放 <根>/scripts/ 或 <根>/pm/scripts/ 均可(紧凑布局见 SKILL §3);下文命令示例按默认布局写。
|
|
4
|
-
# 三种用法:
|
|
5
|
-
# bash scripts/verify-status.sh # 打印各套件状态:套件 | 命令 | 上次全绿时间(bus-check 调的是这个)
|
|
6
|
-
# bash scripts/verify-status.sh --run # 逐套件真跑;全绿的套件记录「上次全绿时间」到标记文件
|
|
7
|
-
# bash scripts/verify-status.sh --format=machine
|
|
8
|
-
# # 给 bus-check 返回稳定 TSV finding,不打印人类文案
|
|
9
|
-
# 接入:改 SUITES 数组即可(名称|命令;命令里自己 cd 进子仓)。标记文件 .last-green-<套件>(与本脚本同目录)
|
|
10
|
-
# 是本地实查产物,不入 git(gitignore.template 已排除)——新机器上显示"从未全绿"是诚实的:你确实没在这台机器跑过。
|
|
11
|
-
set -uo pipefail
|
|
12
|
-
# 协调层根 = 向上最近的含 pm/NOW.md 的目录;SDIR = 本脚本目录(相对根)。见 bus-check.sh 同段注释。
|
|
13
|
-
_sd="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
14
|
-
ROOT="$_sd"; for _ in 1 2 3 4; do [ -f "$ROOT/pm/NOW.md" ] && break; ROOT="$(dirname "$ROOT")"; done
|
|
15
|
-
[ -f "$ROOT/pm/NOW.md" ] || ROOT="$(cd "$_sd/.." && pwd)"
|
|
16
|
-
SDIR="${_sd#"$ROOT"/}"; [ "$SDIR" = "$_sd" ] && SDIR="."
|
|
17
|
-
cd "$ROOT" || exit 1
|
|
18
|
-
|
|
19
|
-
# 套件表:名称|命令(示例:npm / mvn 各一,按项目替换;没有测试就留占位符,bus-check 会如实红字)
|
|
20
|
-
SUITES=(
|
|
21
|
-
"<套件1>|cd <代码子仓1> && npm test --silent"
|
|
22
|
-
"<套件2>|cd <代码子仓2> && mvn -q test"
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
RUN=0; FORMAT="human"
|
|
26
|
-
for arg in "$@"; do
|
|
27
|
-
case "$arg" in
|
|
28
|
-
--run) RUN=1 ;;
|
|
29
|
-
--format=machine) FORMAT="machine" ;;
|
|
30
|
-
*) echo "verify-status: unknown argument: $arg" >&2; exit 2 ;;
|
|
31
|
-
esac
|
|
32
|
-
done
|
|
33
|
-
[ "$RUN" = 0 ] || [ "$FORMAT" = "human" ] || {
|
|
34
|
-
echo "verify-status: --run cannot be combined with --format=machine" >&2
|
|
35
|
-
exit 2
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
emit_machine_finding() {
|
|
39
|
-
code="$1"; level="$2"; message="$3"; path="$4"
|
|
40
|
-
message="$(printf '%s' "$message" | tr '\t\r\n' ' ')"
|
|
41
|
-
path="$(printf '%s' "$path" | tr '\t\r\n' ' ')"
|
|
42
|
-
printf 'FINDING\t%s\t%s\t%s\t%s\n' "$code" "$level" "$message" "$path"
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
mark_epoch() {
|
|
46
|
-
value="$1"
|
|
47
|
-
if date -j -f '%Y-%m-%d %H:%M' "$value" '+%s' >/dev/null 2>&1; then
|
|
48
|
-
date -j -f '%Y-%m-%d %H:%M' "$value" '+%s'
|
|
49
|
-
elif date -d "$value" '+%s' >/dev/null 2>&1; then
|
|
50
|
-
date -d "$value" '+%s'
|
|
51
|
-
else
|
|
52
|
-
return 1
|
|
53
|
-
fi
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
n=0; ph_warned=0; failed=0
|
|
57
|
-
for pair in "${SUITES[@]:-}"; do
|
|
58
|
-
[ -n "$pair" ] || continue
|
|
59
|
-
case "$pair" in *"<"*)
|
|
60
|
-
if [ "$ph_warned" = 0 ] && [ "$FORMAT" = "human" ]; then
|
|
61
|
-
echo "⚠️ SUITES 还是占位符 —— 填入真实测试命令,L3 级证据才有兜底(没有测试?先补,见 SKILL §8.5 第 3 步)"
|
|
62
|
-
fi
|
|
63
|
-
ph_warned=1; continue;; esac
|
|
64
|
-
n=$((n+1))
|
|
65
|
-
name="${pair%%|*}"; cmd="${pair#*|}"
|
|
66
|
-
mark="$SDIR/.last-green-$name"
|
|
67
|
-
if [ "$RUN" = 1 ]; then
|
|
68
|
-
echo "▸ 跑 $name:$cmd"
|
|
69
|
-
if bash -c "$cmd"; then
|
|
70
|
-
date '+%Y-%m-%d %H:%M' > "$mark"; echo "✅ $name 全绿 → 已记 $(cat "$mark")"
|
|
71
|
-
else
|
|
72
|
-
echo "❌ $name 未过 —— 修完再跑 --run 刷新「上次全绿」"
|
|
73
|
-
failed=$((failed + 1))
|
|
74
|
-
fi
|
|
75
|
-
elif [ "$FORMAT" = "human" ]; then
|
|
76
|
-
last="从未全绿(或没在本机跑过)"; [ -f "$mark" ] && last="$(cat "$mark")"
|
|
77
|
-
printf '%s | %s | 上次全绿:%s\n' "$name" "$cmd" "$last"
|
|
78
|
-
else
|
|
79
|
-
max_age_days="${BUS_L3_MAX_AGE_DAYS:-7}"
|
|
80
|
-
case "$max_age_days" in ''|*[!0-9]*) max_age_days=7 ;; esac
|
|
81
|
-
if [ ! -f "$mark" ]; then
|
|
82
|
-
emit_machine_finding "sync.l3_stale" "warning" "Configured L3 suite '$name' has no local all-green marker." "$mark"
|
|
83
|
-
continue
|
|
84
|
-
fi
|
|
85
|
-
last="$(head -1 "$mark" 2>/dev/null || true)"
|
|
86
|
-
if ! last_epoch="$(mark_epoch "$last")"; then
|
|
87
|
-
emit_machine_finding "sync.l3_stale" "warning" "Configured L3 suite '$name' has an unreadable all-green marker." "$mark"
|
|
88
|
-
continue
|
|
89
|
-
fi
|
|
90
|
-
now_epoch="$(date '+%s')"
|
|
91
|
-
age_days=$(( (now_epoch - last_epoch) / 86400 ))
|
|
92
|
-
if [ "$age_days" -gt "$max_age_days" ]; then
|
|
93
|
-
emit_machine_finding "sync.l3_stale" "warning" "Configured L3 suite '$name' is stale ($age_days days > $max_age_days days)." "$mark"
|
|
94
|
-
fi
|
|
95
|
-
fi
|
|
96
|
-
done
|
|
97
|
-
if [ "$n" = 0 ]; then
|
|
98
|
-
if [ "$FORMAT" = "machine" ]; then
|
|
99
|
-
emit_machine_finding "sync.l3_unconfigured" "unverified" "No real L3 suite is configured." "$SDIR/verify-status.sh"
|
|
100
|
-
elif [ "$RUN" = 1 ]; then
|
|
101
|
-
echo "(没有可跑的套件)"
|
|
102
|
-
fi
|
|
103
|
-
fi
|
|
104
|
-
[ "$failed" -eq 0 ] || exit 1
|
|
105
|
-
exit 0
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
# 指挥台 — Builder 怎么驱动工作包内的 AI 视角
|
|
2
|
-
|
|
3
|
-
> 忘了怎么开场就看这页。先从看板认领一个端到端工作包;同一个 Builder 对它从判断、实现、测试一直负责到合并/发布证据。每个 session 一开就自动读 `AGENTS.md` + `pm/NOW.md`,你基本只需说「**开工 / 接着做 / 当前工作包该你这个视角了**」,剩下它自驱。
|
|
4
|
-
> **每个 session 第一次对话只设定 AI 视角**:「你是当前工作包的产品/全栈/测试视角,负责……(照 `AGENTS.md` §1 表说)」;这不是人类岗位或成员分配。
|
|
5
|
-
|
|
6
|
-
| AI 视角 Session | 在哪开(cwd) | 你的一句话 | 它在当前工作包内自动做啥 |
|
|
7
|
-
|---|---|---|---|
|
|
8
|
-
| **产品** | `pm/` | 「把需求X拆工单」/「验收X」 | 定一个覆盖多个子项的工作包、维护决策收件箱、更看板、按包记 decisions、收敛候选 hash 集;review-ready 后派一次 reviewer |
|
|
9
|
-
| **全栈** | 工作区根 | 「看板上需求X该你了,开工」 | 读工作包+契约 → 在范围内持续实现 → 每提交过机器闸 → 只记实现语义 delta → 工作包/里程碑时状态带 hash + CHANGELOG |
|
|
10
|
-
| **测试** | `<被测仓>/` | 「跑X的E2E + 走查设计」 | 黑盒 E2E + 带图设计走查 → 直接提 bug → 给里程碑核查提供证据 |
|
|
11
|
-
|
|
12
|
-
## 工作包闭环
|
|
13
|
-
同一个 Builder 对这一整条负责:1. **产品视角**收敛规格 + 人拍板 **⛔Gate1** → 2. 产品视角写设计 brief(要求单 HTML 可渲染)→ 设计工具出稿 → **真渲染后人拍板 ⛔Gate2** → 3. **全栈视角**实现(先改 PROTOCOL、每提交过机器闸、写者自发现问题先收敛;只有改冻结对外语义/不可逆副作用才提前 `risk-delta`)→ 4. **测试视角** E2E + 带图走查 → review-ready 后 milestone reviewer 静默全核**一次** → P0/P1 合并修完后 closure **一次** → **⛔Gate3 人确认合并** → 5. 部署候选 → **⛔Gate4 人确认上线** → 工作包收尾。多个 Builder 并行时各走自己的闭环,不把这条链拆成人类岗位流水线。
|
|
14
|
-
|
|
15
|
-
> **会话不按子任务交还接力棒**:每轮先认领 `objective / in_scope / terminal_condition`;文档、commit、reviewer、status 都可能只是包内步骤。范围内仍有安全工作就继续,不等你再说“继续”。
|
|
16
|
-
> **审批只分三档**:跨 Gate/扩范围/改冻结契约/不可逆外部动作/风险接受才 `STOP_NOW`;冻结前可逆选择进看板收件箱,到 Gate 默认一次批 2–5 个(确实只有 1 个就单项);事实、推导约束、归档/status/P2 等无需批。
|
|
17
|
-
> **人批预算**:每个工作包、每道 Gate 默认只发 1 个批量请求。你只答一部分或要解释时,会话保持同一决策包继续说明,不会重新包装成“第二次审批”;不阻塞的工作照常继续。
|
|
18
|
-
> 决策包收敛后,当前工作包的产品视角先落 `pm/decisions.md` 一行再回写各处;部分回答只更新收件箱,不为 `3/14 → 11/14 → 14/14` 制造三条台账。**线上是什么版本别问会话、别信文档,跑 `bash scripts/bus-check.sh` 看实况**。
|
|
19
|
-
> **三轨制**:小改快轨 / 单功能标准轨 / 契约变更重轨(`pm/changes/` 提案)。本期走哪轨看 `pm/NOW.md`。
|
|
20
|
-
> **核查门不按任务数收费**:首次 milestone 前不启动 reviewer;review-ready 后每工作包每 Gate 默认 1 次 milestone,P0/P1 合并修完后 1 次 closure,P2 不复核。reviewer 单次静默核完再返回,不播报中间 findings。同一 candidate 复用结论;返回前 hash 变化就标 `SUPERSEDED` 并停止,不得边改边 delta。
|
|
21
|
-
> **4 个 Gate 不可自动跨过**;reviewer 批准 ≠ 你合并。你只是节拍器,信息全在 repo 里流动,不用你搬运。
|
|
22
|
-
|
|
23
|
-
## 域回复怎么写
|
|
24
|
-
|
|
25
|
-
每个 AI 视角面向人收口或交接时,统一按「已做 → 未做 → 下一步」回复:
|
|
26
|
-
|
|
27
|
-
```md
|
|
28
|
-
## 〔当前域〕|✅ 已完成 / 🔄 未完成
|
|
29
|
-
|
|
30
|
-
### 已做
|
|
31
|
-
1. 〔功能或业务结果〕
|
|
32
|
-
- 证据:〔commit、测试结果或报告〕
|
|
33
|
-
|
|
34
|
-
### 未做
|
|
35
|
-
1. 〔还没完成或没验证什么〕
|
|
36
|
-
- 原因:〔具体原因〕
|
|
37
|
-
|
|
38
|
-
### 下一步
|
|
39
|
-
- **本域已完成:** 下一棒是〔哪个域 / AI 视角〕,负责〔业务级目标〕。
|
|
40
|
-
- **本域未完成:** 需要〔谁〕提供或确认〔什么〕。
|
|
41
|
-
- **无需协助:** 我继续做,暂不交棒。
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
`已做`只写功能/业务结果,证据紧跟对应事项;多项共用时在列表末写「共同证据」。`未做`要写原因,没有就写「无」。`下一步`只保留符合当前状态的一条;工作包已完成就写「下一棒:无」。没有真实阻塞就继续做,不把自己能解决的事包装成求助。完整口径见 `AGENTS.md` 的「域回复格式」。
|
|
45
|
-
|
|
46
|
-
## 检查结果怎么读
|
|
47
|
-
|
|
48
|
-
先跑 `bash scripts/bus-check.sh --strict`,再按级别读摘要;exit 0 只表示没有 `conflict/error`,不表示所有范围都核过。
|
|
49
|
-
|
|
50
|
-
| Level | 含义 | 怎么办 |
|
|
51
|
-
|---|---|---|
|
|
52
|
-
| `confirmed` | 一条事实已直接观测 | 只复用该事实,不外推全绿 |
|
|
53
|
-
| `warning` | 有风险/追溯弱点,但尚无确定矛盾 | 按 `path` 定位,受影响 Gate 前处理或明确挂账 |
|
|
54
|
-
| `unverified` | 工具没可靠核到这段范围 | 补证据/工具/权限后重跑,收尾必须列出 |
|
|
55
|
-
| `conflict` | 声明与事实矛盾或必需证据缺失 | 先修再重跑;strict 阻断 |
|
|
56
|
-
| `error` | 协议结构或检查过程不可信 | 先修格式/唯一性/检查器;strict 阻断 |
|
|
57
|
-
|
|
58
|
-
常见处置:`sync.scan_truncated` 看 `reason=limit|symlink|permission`——先确认省略范围,不要为消警盲抬阈值;symlink 目标单独核或落成根内 regular file;权限只补最小读取/目录搜索能力。`sync.l3_*` 配真实套件或刷新证据;`gate.* / evidence.* / ref.*` 修 canonical 行与证据引用;`stack.drift / sync.multirepo_drift` 回权威来源逐项对齐,脚本不代改。JSON 中 `coverage.complete=false` 时,结论只能写“已覆盖部分 + 未验证边界”。
|