#!/usr/bin/env bash
set -u
FAIL=0
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
pin_tag() {
local tag
tag="$(grep -m1 "$1.*tag = " "$REPO_ROOT/Cargo.toml" | sed -E 's/.*tag = "([^"]+)".*/\1/')"
[[ -z "$tag" ]] && { echo "[v154-guards] FAIL: 守卫常量派生失败(Cargo.toml 无 $1 tag 钉面)" >&2; exit 1; }
echo "$tag"
}
KERNEL_TAG="$(pin_tag 'xwkernel.git')"
XWWASM_TAG="$(pin_tag 'xwwasm.git')"
note() { echo "[v154-guards] $*"; }
die() { echo "[v154-guards] FAIL: $*" >&2; FAIL=1; }
# ★★ v1.9.0 P0-2 器面专批: 本档已【无 checkout 读取】——
# 原 `xwcore_lock_commit()` / `xwcore_src_dir()`(自 `Cargo.lock` 取短 sha 再于
# `${CARGO_HOME}/git/checkouts` 定位源码目录)随其唯四调用方(xwcore 面 4 断言)同批删除;
# 原 `skip_note()`/`SKIP_COUNT`(P0-4 之 silent-skip→WARN 面, 唯 `ac1_syntect_lazy` 用)
# 亦随该断言删除而【无对象】⇒ 同删(禁留死支)。详见档头 P0-2 注。
# ── v152 新增: CodeGraph 惰性判据 ─────────────────────────────
# ★★ v1.9.0 P0-2: 原 `assert_v152_ac_cg_lazy_boot` / `assert_v152_ac_cg_lazy_ensure` /
# `assert_v152_ac_cg_dual_bounds` 三判据(xwcore 面 · 读 checkout 之源码内容)已删 ——
# 其内容断言之归属 = `xwcore` 自仓 `gate`(详见档头)。主线侧之【缺失覆盖】记 `GAP`。
# ── v151 承继: 有界化结构判据 ─────────────────────────────────
# G-AC4 (v150 承继): wasmtime 惰性行为面——xwwasm 钉 v0.1.2 + v0.1.1 零残留
assert_v151_ac4_wasmtime_lazy() {
local n
n="$(grep -c "xwwasm.git.*tag = \"$XWWASM_TAG\"" "$REPO_ROOT/Cargo.toml" || true)"
if [[ "$n" -ge "2" ]]; then
note "v152_ac4_wasmtime_lazy: xwwasm 钉 $XWWASM_TAG 双声明在位 ✓"
else
die "xwwasm 钉 $XWWASM_TAG 声明计数=$n, 期望 ≥2(xw-plugin + xw-plugin-wasmtime)"
fi
if grep -q 'xwwasm.git.*tag = "v0.1.1"' "$REPO_ROOT/Cargo.toml"; then
die "xwwasm v0.1.1 残留声明(惰性化载体未升钉)"
else
note "v152_ac4_wasmtime_lazy: xwwasm v0.1.1 零残留 ✓"
fi
}
# G-AC5 (v150 承继): worker 缺省 min(4,ncpu)——parse_worker_threads + kernel 钉
assert_v151_ac5_worker_default() {
if grep -q "parse_worker_threads" "$REPO_ROOT/src/main.rs"; then
note "v152_ac5_worker_default: 根 bin parse_worker_threads 在位 ✓"
else
die "根 bin parse_worker_threads 缺失(worker 收口未落)"
fi
if grep -q "available_parallelism" "$REPO_ROOT/src/main.rs"; then
note "v152_ac5_worker_default: 缺省 min(4,ncpu) 计算在位 ✓"
else
die "缺省 min(4,ncpu) 计算缺失(available_parallelism 未引用)"
fi
local n
n="$(grep -c "xwkernel.git.*tag = \"$KERNEL_TAG\"" "$REPO_ROOT/Cargo.toml" || true)"
if [[ "$n" -ge "4" ]]; then
note "v152_ac5_worker_default: kernel 钉 $KERNEL_TAG 声明在位 ✓"
else
die "kernel 钉 $KERNEL_TAG 声明计数=$n, 期望 ≥4"
fi
}
# G-AC6 (v150 承继): mallopt cfg(gnu)+env 不覆盖——clamp_malloc_arenas + libc 直依赖
assert_v151_ac6_arena_cap() {
if grep -q "clamp_malloc_arenas" "$REPO_ROOT/src/main.rs"; then
note "v152_ac6_arena_cap: 根 bin clamp_malloc_arenas 在位 ✓"
else
die "根 bin clamp_malloc_arenas 缺失(arena 收口未落)"
fi
if grep -q "cfg(target_env = \"gnu\")" "$REPO_ROOT/src/main.rs"; then
note "v152_ac6_arena_cap: cfg(gnu) 门控在位 ✓"
else
die "cfg(gnu) 门控缺失(非 gnu 平台无保护)"
fi
if grep -q "MALLOC_ARENA_MAX" "$REPO_ROOT/src/main.rs"; then
note "v152_ac6_arena_cap: env 不覆盖守卫在位 ✓"
else
die "MALLOC_ARENA_MAX env 守卫缺失(用户显式值会被覆盖)"
fi
if grep -q "^libc = " "$REPO_ROOT/Cargo.toml"; then
note "v152_ac6_arena_cap: libc 直依赖在位 ✓"
else
die "libc 直接依赖缺失(mallopt 无法调用)"
fi
}
# G-AC7 (v150 承继, v154 L1 集合语义硬化): fancy-regex 双值集合精确匹配原值域 {0.16.2, 0.19.2}——无序, 消顺序敏感假红
assert_v151_ac7_dual_value() {
local vals
vals="$(grep -A2 'name = "fancy-regex"' "$REPO_ROOT/Cargo.lock" | grep -oP 'version = "\K[0-9.]+' || true)"
local v150 v152
v150="$(echo "$vals" | head -1)"
v152="$(echo "$vals" | sed -n 2p)"
local norm_vals norm_target
norm_vals="$(echo "$vals" | sort)"
norm_target="$(printf '0.16.2\n0.19.2' | sort)"
if [[ "$norm_vals" == "$norm_target" ]]; then
note "v154_ac7_dual_value: fancy-regex 双值集合 {$v150, $v152} 无序精确匹配原值域 {0.16.2, 0.19.2} ✓"
else
die "fancy-regex 双值集合期望 {0.16.2, 0.19.2} 实得 {$v150, $v152}——双值漂移须人工核验终态语义(v1.5.4 L1 集合语义, 消顺序敏感假红)"
fi
}
assert_v151_ac3_rayon_cap() {
if grep -q "clamp_rayon_threads" "$REPO_ROOT/src/main.rs"; then
note "v152_ac3_rayon_cap: 根 bin clamp_rayon_threads 在位 ✓"
else
die "根 bin clamp_rayon_threads 缺失(rayon 收口未落)"
fi
if grep -q "RAYON_NUM_THREADS" "$REPO_ROOT/src/main.rs"; then
note "v152_ac3_rayon_cap: RAYON_NUM_THREADS 缺省策略在位 ✓"
else
die "RAYON_NUM_THREADS 缺省策略缺失"
fi
if grep -q "min(4, ncpu)" "$REPO_ROOT/src/main.rs"; then
note "v152_ac3_rayon_cap: 缺省 min(4,ncpu) 计算在位 ✓"
else
die "缺省 min(4,ncpu) 计算缺失"
fi
}
assert_v151_ac3_tokio_cap() {
if grep -q "clamp_tokio_threads" "$REPO_ROOT/src/main.rs"; then
note "v152_ac3_tokio_cap: 根 bin clamp_tokio_threads 在位 ✓"
else
die "根 bin clamp_tokio_threads 缺失(tokio 收口未落)"
fi
if grep -q "TOKIO_WORKER_THREADS" "$REPO_ROOT/src/main.rs"; then
note "v152_ac3_tokio_cap: TOKIO_WORKER_THREADS 缺省策略在位 ✓"
else
die "TOKIO_WORKER_THREADS 缺省策略缺失"
fi
}
assert_v151_ac8_guard_param() {
if grep -q "active_guard_gen" "$REPO_ROOT/scripts/tests/v143_gates.sh"; then
note "v152_ac8_guard_param: 世代自动解析在位 ✓"
else
die "v143_gates.sh active_guard_gen 缺失(世代参数化未落)"
fi
if grep -q "active_guard_script" "$REPO_ROOT/scripts/tests/v143_gates.sh"; then
note "v152_ac8_guard_param: 现役守卫自动接线在位 ✓"
else
die "v143_gates.sh active_guard_script 缺失(自动接线未落)"
fi
}
assert_v151_crates_absent() {
local n _bad
n="$(git -C "$REPO_ROOT" ls-files crates/ 2>/dev/null | wc -l || true)"
_bad="$(git -C "$REPO_ROOT" ls-files crates/ 2>/dev/null | grep -vE '^crates/xwteam/' | wc -l || true)"
if [[ "${_bad:-0}" -ne "0" ]]; then
die "crates/ 白名单外存在 ${_bad} 个跟踪文件(插件化出树未彻底)"
fi
if [[ "$n" -eq "7" ]]; then
note "v152_crates_absent: crates/ 跟踪文件 7 档皆在 白名单 crates/xwteam/(物理残留免疫)✓"
else
die "crates/ 跟踪文件数 ${n} ≠ 7(白名单成员仓之档数 —— 增删须同批改本判据与名单)"
fi
}
assert_v151_path_zero() {
local n
n="$(grep -A5 '^\[workspace\]' "$REPO_ROOT/Cargo.toml" | grep -c '^\s*"' || true)"
if [[ "$n" -eq "0" ]]; then
note "v152_path_zero: workspace members path 计数=0 ✓"
else
die "workspace members path 计数=$n(应=0, 全 git dep 形态)"
fi
}
# v143 承继: [patch.xwcode.git] 段退役
assert_v151_xwcode_git_absent() {
if grep -q '\[patch\."https://gitcode.com/kemeng1/xwcode.git"\]' "$REPO_ROOT/Cargo.toml"; then
die "[patch.xwcode.git] 段回流(ca2f7e34 时代终结失守)"
else
note "v152_xwcode_git_absent: [patch.xwcode.git] 段退役 ✓"
fi
}
# ── v153 新增: AC-G1/AC-G2 门禁化判据 ─────────────────────────
# AC-G1: 内存红线真实口径门禁载体在位——realtotal_gate.sh 收编+红侧构造实证脚本
assert_v153_ac_g1_mem_gate() {
if [[ -f "$REPO_ROOT/scripts/tests/realtotal_gate.sh" && -x "$REPO_ROOT/scripts/tests/realtotal_gate.sh" ]]; then
# v1.6.0 SB.4: 阈值面精确钉(防旧口径回潮; 用户铁律 gate 化同步面)。
# ★★ v1.9.14 P1 抬线批(用户 2026-10-23 定夺 · 承 ar R19): 钉值 20,000 ⇒ 32,768
# (32 MiB)—— 用户铁律本体之值随用户令变更; 本守之【职】不变(阈值面精确钉,
# 防静默漂移 ∧ 防旧口径回潮), 唯【所钉之值】随现行处方走。
if grep -q "THRESHOLD_KB=32768" "$REPO_ROOT/scripts/tests/realtotal_gate.sh" && \
grep -q "NTOT" "$REPO_ROOT/scripts/tests/realtotal_gate.sh"; then
note "v153_ac_g1_mem_gate: realtotal_gate.sh 收编在位(红线 32,768 kB[v1.9.14 P1] + NTOT 原值判定)✓"
else
die "realtotal_gate.sh 缺 THRESHOLD_KB=32768/NTOT 判定标识(内存红线门禁载体不完整或阈值面静默漂移)"
fi
else
die "realtotal_gate.sh 缺失/不可执行(AC-G1 探针机械化未落)"
fi
if [[ -f "$REPO_ROOT/scripts/tests/realtotal_gate_redside.sh" ]]; then
note "v153_ac_g1_mem_gate: realtotal_gate_redside.sh 红侧构造载体在位 ✓"
else
die "realtotal_gate_redside.sh 缺失(红侧构造实证载体未落)"
fi
if grep -q "检查19" "$REPO_ROOT/scripts/verify_release.sh"; then
note "v153_ac_g1_mem_gate: verify_release 检查19 接线在位 ✓"
else
die "verify_release.sh 检查19 缺失(3j 部署位门禁未接线)"
fi
}
# AC-G1: CI memory-gate job 分级挂载(manual/scheduled 先行, push-gate 收敛后挂)
assert_v153_ac_g1_ci_job() {
if grep -q "memory-gate" "$REPO_ROOT/.github/workflows/ci.yml" && \
grep -q "workflow_dispatch" "$REPO_ROOT/.github/workflows/ci.yml" && \
grep -q "realtotal_gate" "$REPO_ROOT/.github/workflows/ci.yml"; then
note "v153_ac_g1_ci_job: CI memory-gate job 在位(分级挂载 manual/scheduled 先行)✓"
else
die "CI memory-gate job 缺失/未分级(AC-G1 CI 载体未落)"
fi
}
# AC-G2: selftest 锚绑定机制化——红侧⑦增条目变异+动态字面整形
assert_v153_ac_g2_registry_bind() {
if grep -q "red7_registry_append_detected" "$REPO_ROOT/scripts/tests/verify_release_gate_selftest.sh"; then
note "v153_ac_g2_registry_bind: selftest 红侧⑦增条目变异在位 ✓"
else
die "selftest 红侧⑦缺失(AC-G2 ① 锚绑定双向补齐未落)"
fi
if grep -q "RED_SIDE_COUNT" "$REPO_ROOT/scripts/tests/verify_release_gate_selftest.sh"; then
note "v153_ac_g2_registry_bind: selftest 动态红侧计数字面整形在位 ✓"
else
die "selftest 动态字面整形缺失(AC-G2 ③ 三红侧陈旧字面未整)"
fi
}
# AC-G2: 锚绑定纪律落位——3d 登记 check14 条目须同批重锚(硬条款, tasks 版本级 Step13.4)
assert_v153_ac_g2_anchor_bind_discipline() {
if grep -q "同批重锚\|同步重锚\|重锚" "$REPO_ROOT/openspec/changes/v1.5.3-gate-and-convergence/tasks.md"; then
note "v153_ac_g2_anchor_bind_discipline: 3d 重锚纪律硬条款在位 ✓"
else
die "tasks.md 缺同批重锚硬条款(AC-G2 ① 纪律未文档化)"
fi
}
# ── v154 新增: L5 日期防呆守卫 ─────────────────────────────
# L5 (v1.5.4 新增, 1d W2 裁定): CHANGELOG/ROADMAP/发布文档条目日期 ≤ 当日
# v1.5.3 W① 未来日期判例机制化——未来日期(> 当日)→ FAIL(真不变量)。
# ==当日 仅作 3d-3i 发布窗 D 面纪律(非本守卫断言)。日期定谳源=本机 date(CST)唯一权威;
# comm 显示钟为 UTC 不可信(判例入注)。红侧构造: 注入未来日期条目 → 断言 FAIL。
# ★★ `v1.9.18` `P4` 订正(承 `docs/V1918_RULINGS_05.md` §二 `P4` · `test` `M1E_PIPELINE_PROBLEMS.md` §P4):
# 旧形 `date +%Y-%m-%d` 取 **ambient `TZ`** ⇒ 而上注自陈「定谳源 = **CST**」⇒ ★ **名实不符**。
# ★ 实证(`#6` 容器): `.xuanwu/release/Dockerfile.verify-release` ∧ `.xuanwu/snippets/setup-rust-ci.sh`
# **皆无 `TZ` 之设** ⇒ 随基镜像 = **UTC** ⇒ `today` 得 `2026-10-24`,而文档日期 `2026-10-25`
# 于 CST **即当日** ⇒ 判「未来日期」= ★ **假红**(判据钉 TZ 承其自陈之语义, 非放宽判据)。
# ★ 治: **显式钉 `TZ=Asia/Shanghai`** —— 使「定谳源」与「注释所陈」一致, 且**不随宿主/容器漂**。
# ★ 禁取「宿主 `date`」或「取二 TZ 之较宽者」: 前者即今之 ambient 之弊, 后者等于**弃判据**。
assert_v154_l5_date_guard() {
local today
# ★ 时区显式钉(CST = 判据之语义基)—— 非 ambient、非宿主、非 UTC
today="$(TZ=Asia/Shanghai date +%Y-%m-%d)"
local f fut
fut=0
for f in "$REPO_ROOT/CHANGELOG.md" "$REPO_ROOT/ROADMAP.md" "$REPO_ROOT/openspec/specs/roadmap.md"; do
[[ -f "$f" ]] || continue
while read -r d; do
[[ -z "$d" ]] && continue
if [[ "$d" > "$today" ]]; then
die "L5 日期防呆: $f 含未来日期 $d > 当日 $today(CST=Asia/Shanghai 显式钉 · comm 显示钟 UTC 不可信)——v1.5.3 W① 判例机制化 FAIL"
fut=1
fi
done < <(grep -oP '\d{4}-\d{2}-\d{2}' "$f" || true)
done
if [[ "$fut" -eq 0 ]]; then
note "v154_l5_date_guard: 日期防呆通过(全文档条目日期 ≤ 当日 $today, CST=Asia/Shanghai 显式钉)✓"
fi
}
# ── v151 承继判据(★ v1.9.0 P0-2 后: 十一 ⇒ 九 —— `ac4_codegraph_bound` ∧ `ac1_syntect_lazy`
# 二条随 checkout 依赖同批删, 见档头)────────────────────────
assert_v151_ac4_wasmtime_lazy
assert_v151_ac5_worker_default
assert_v151_ac6_arena_cap
assert_v151_ac7_dual_value
assert_v151_crates_absent
assert_v151_path_zero
assert_v151_xwcode_git_absent
# v151 新增有界化判据(承继)
assert_v151_ac3_rayon_cap
assert_v151_ac3_tokio_cap
assert_v151_ac8_guard_param
# ★ v1.9.0 P0-2: 原 `assert_v151_ac4_codegraph_bound` ∧ `assert_v151_ac1_syntect_lazy` 调用点已删。
# ★ v1.9.0 P0-2: 原 `assert_v152_ac_cg_lazy_boot` / `assert_v152_ac_cg_lazy_ensure` /
# `assert_v152_ac_cg_dual_bounds` 三调用点已删(xwcore 面 checkout 断言 ⇒ 归 xwcore 自仓 gate)。
# v153 新增 AC-G1/AC-G2 门禁化判据
assert_v153_ac_g1_mem_gate
assert_v153_ac_g1_ci_job
assert_v153_ac_g2_registry_bind
assert_v153_ac_g2_anchor_bind_discipline
# v154 新增 L5 日期防呆守卫
assert_v154_l5_date_guard
if [[ "$FAIL" == "1" ]]; then
die "守卫未全过"
exit 1
fi
# ★ v1.9.0 P0-2: 原 SKIP_COUNT 警示支(P0-4 之 WARN 化面)随 `ac1_syntect_lazy` 删除而废 ——
# 本档已无 checkout 读取 ⇒ 【无跳过路径】⇒ 该支【无对象】(禁留死支)。读数之纯净由
# 空 `CARGO_HOME` 跑(`EXIT=0`)自证, 见档头「后果之自证」。
note "v154 十五守卫全过 ✓(★ v1.9.0 P0-2 后: 二十 ⇒ 十五 —— 五条 checkout 依赖断言删除, 见档头)"
exit 0