#!/usr/bin/env bash
# v1.6.5 AC5 组合审计门禁 —— (丁) 判据【机械化】(代码评审 S1 · 欠项 C · 裁定 40 附加严一)
#
# ★ 判据(逐字承 docs/V1.6.5_M1_DECISION.md §2.3 名册):
#   令 T = Cargo.lock 之 `?tag=` 去前导 v; 令 V = 同 [[package]] 之 version。
#   ① 方向性: 凡 V ≠ T 之 git 直钉包, 须 **V ≤ T**(拒「钉了旧 tag」之真错钉)
#   ② 名册完备: V ≠ T 之包【集合】须恰等于登记名册(双向: 无未登记之破例 ∧ 无名册僵尸项)
#
# ★ 裁定 70(第五要件 · 阳性对照须接入出口码): 本脚本以【合成夹具】跑四组对照,
#   任一对照之行为不符预期 ⇒ 显式 `exit 1` —— 禁以「本仓绿」充「谓词可信」。
#   (据: 阳性对照为【静默恒假】之唯一可检面, 而 `rc` 断言捕不到。)
# ★ 裁定 71(跨实现核限缩为加分项): 本脚本【钉死实现】= POSIX `awk` + `sort -V`,
#   且【零正则谓词】⇒ 无 grep 方言风险面; 非可移植性面 = `sort -V`(GNU coreutils 扩展)。
# ★ 裁定 62/70 之「附五件」中, 本脚本适用者: rc 断言(下)、显名方言(无正则, 记 N/A)、
#   阳性对照接线(有)、`-F` 不作主锚(N/A)、跨实现核(N/A · 记加分项未取)。
#
# 用法: bash scripts/tests/ac5_registry_gate.sh [--lock <path>] [--selftest] [--quiet]
# 退出码: 0 = 判据成立 ∧ 对照全符; 1 = 判据不成立 或 对照失效(禁发布)

set -uo pipefail

REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
LOCK_FILE="$REPO_ROOT/Cargo.lock"
SELFTEST_ONLY=0
QUIET=0

while [[ $# -gt 0 ]]; do
    case "$1" in
        --lock) LOCK_FILE="$2"; shift 2 ;;
        --selftest) SELFTEST_ONLY=1; shift ;;
        --quiet) QUIET=1; shift ;;
        *) echo "未知参数: $1" >&2; exit 2 ;;
    esac
done

FAIL=0
say()  { [[ "$QUIET" -eq 1 ]] || echo "$@"; }
bad()  { echo "FAIL: $*" >&2; FAIL=$((FAIL+1)); }

# ── 名册(V ≠ T 之【已登记】包名; 权威载体 = docs/V1.6.5_M1_DECISION.md §2.3)──
# 第 11 条 `xwtui` 系 v1.6.5 `M2` 修复打点(v0.3.8 → v0.3.9)所致(裁定 72 §5.3)。
# ★ v1.6.5 `3d` 批随动(ar 裁定 93 · 2026-09-30): **删 `xw-tools` ⇒ 名册 `11` → `10`** ——
#   据: 发布链外之笔 `64f9e07a4` 使 lock 之 `xw-tools` ⇒ `version = "0.1.9"` ∧ `tag = v0.1.9`
#   ⇒ **`V = T`**; 而本判据之【名册完备 = 双向】(无未登记之破例 ∧ **无名册僵尸项**)⇒
#   名册仍载 `xw-tools` 即【僵尸】⇒ 检查21 `FAIL`(**非你侧之过** —— 该笔系 ar 另案)。
# ★ v1.6.6 `④` 批随动(2026-10-01 · 裁定 360/362 · 配套表 #8「xwcode 主线 ⇒ dev」): **删 `xwtui` ⇒ 名册 `10` → `9`** ——
#   据: 主线双面同批升钉 `xwtui` `v0.3.9` → `v0.3.11`(`Cargo.toml` 声明面 + `[patch."…/xwtui.git"]` 面)
#   使 lock 之 `xwtui` ⇒ `version = "0.3.11"` ∧ `tag = v0.3.11` ⇒ **`V = T`**(★ 并闭原【`version` 塌陷】:
#   旧态 `version = "0.3.8"` / `tag = v0.3.9`)⇒ 名册仍载 `xwtui` 即【僵尸】⇒ 检查21 `FAIL`。
#   ★ 本删系【本版自身升钉】之随动, 与上条 `xw-tools`(ar 另案)别。★ 载体面: 本表(机械载体)——
#   权威文档 `docs/V1.6.5_M1_DECISION.md` §2.3 系 `v1.6.5` 之【时点记录】, 依该档既有约定
#   「真值随 `AC5_REGISTRY` 而变, 现行读数见 检查21」(承裁定 123【显名时点面 · 非改数】)**不追改**,
#   该档 `v1.6.5` 历史面保持原样 ⇒ 归属歧义已呈 ar 裁(见本批通报)。
# ★ v1.6.6 裁定 402 批【补落随动】(2026-10-01 · dev 自捕): **删 `xwcore` ⇒ 名册 `9` → `8`** ——
#   据: 裁定 402 主线升钉 `xwcore` `v0.1.27` → `v0.1.29`(`Cargo.toml` 声明面 + `[patch."…/xwcore.git"]` 面)
#   使 lock 之 `xwcore` ⇒ `version = "0.1.29"` ∧ `tag = v0.1.29` ⇒ **`V = T`**
#   (一手核: xwcore 仓 `v0.1.29` 之 `Cargo.toml` `[workspace.package] version = "0.1.29"`)
#   ⇒ 名册仍载 `xwcore` 即【僵尸】⇒ 检查21 `FAIL`(**承上条 `xw-tools`/`xwtui` 同族** ——
#   三例同根: 升钉使 `V = T` 而名册未随动删)。
#   ★ 二态对照(本处之证): 改前 `bash scripts/tests/ac5_registry_gate.sh` ⇒ `rc=1`
#     (`FAIL 名册僵尸项: xwcore(已登记而本刻 V=T ⇒ 名册须随动删)`)/ 改后 ⇒ `rc=0`。
# ★ v1.6.7 批丁【随动】(2026-10-02 · dev 自捕): **删 5 项 ⇒ 名册 `8` → `3`** ——
#   据: 批丁 主线重钉(承裁决 38 §三)使 `xwmcp`→`v0.2.1` ∧ `xwproviders`→`v0.1.6`(含 `xw-llm`/`xw-llm-kernel`)
#   ∧ `xwuseragents`→`v0.1.4`; lock 随动重解析(`version` 与 `tag` 逐值升)⇒ 该 `5` 包 **`V = T`**
#   ⇒ 名册仍载即【僵尸】⇒ 检查21 `FAIL`(**承 `xw-tools`/`xwtui`/`xwcore` 同族** —— 四例同根:
#   升钉使 `V = T` 而名册未随动删)。
#   ★ 余 `3` 项【保留】且须【显名其因】: `xwcapgap`/`xw-compress`/`xw-nudge` 于 lock 中**未随动**
#     (其 `[workspace.dependencies]` 声明为【inert】—— `members = []` ⇒ 无成员消费该声明;
#      其真钉在上游 `xwkernel`/`xwcore` 之 manifest = 插件侧)⇒ 该 `3` 包仍 `V ≠ T` ⇒ 仍属破例。
#   ★ 二态对照(本处之证): 改前 `bash scripts/tests/ac5_registry_gate.sh` ⇒ `rc=1`
#     (`FAIL 名册僵尸项: xwmcp xw-llm xw-llm-kernel xwproviders xwuseragents`)/ 改后 ⇒ `rc=0`。
# ★ v1.6.7 重钉(`xwkernel` `v1.4.8` → `v1.4.9`)【随动】(2026-10-02 · dev 自捕 · 承裁决 77 §二/§三):
#   **删 `xwcapgap` ⇒ 名册 `3` → `2`** ——
#   据: 重钉使 `kernel-host` 之传递钉 `xwcapgap` 由 `v0.1.1` ⇒ `v0.1.2`(一手核: xwkernel 仓之
#   `kernel-host/Cargo.toml` `:56` `v0.1.1` ⇒ `:73` `v0.1.2`)⇒ 主线 lock 随动重解析
#   (`version` `0.1.0` ⇒ `0.1.2` ∧ `tag` `v0.1.1` ⇒ `v0.1.2`)⇒ **`V = T`**
#   ⇒ 名册仍载即【僵尸】⇒ 检查21 `FAIL`(**承 `xw-tools`/`xwtui`/`xwcore`/批丁 同族** —— 五例同根)。
#   ★★ **订正上条(批丁)之「余 `3` 项未随动」判**: 该判于【批丁之时点面】成立, 然 `xwcapgap` 之钉
#      **真源 = `xwkernel` 之 `kernel-host`**(上条已识)⇒ `xwkernel` 一升钉即随动 ⇒ 非「恒不随动」。
#      ⇒ 余 `2` 项(`xw-compress`/`xw-nudge`)仍属破例(其真钉不在本笔所升之 `kernel-host` 钉面)。
#   ★ 二态对照(本处之证 · **无管道**直取): 改前 ⇒ **rc=1**
#     (`FAIL 名册僵尸项: xwcapgap(已登记而本刻 V=T ⇒ 名册须随动删)`)/ 改后 ⇒ **rc=0**。
#   ★★ 前向订正(承裁决 80 §三 · dev 自领): 本笔初报曾判「本脚本自身恒 `rc=0`, 判在输出」——
#     **失实**。其因 = 初测用 `bash <本脚本> | tail -N; echo rc=$?` ⇒ `$?` 取【管道末位(`tail`)】
#     之出口码, 非被管脚本者(僵尸态实为 `rc=1`)。★ 在册判据: 管道末位之 `rc` ≠ 被管之 `rc`
#     ⇒ 取 `rc` 须【无管道】或取 `${PIPESTATUS[0]}`(其害 = 假绿 ∧ 假红二向)。
# ★ v1.6.7 AC3【乙】批【随动】(2026-10-02 · 用户裁 · 承裁决 86 §一/§三): **删 `xw-nudge` ∧ `xw-compress` ⇒ 名册 `2` → `0`(清空)** ——
#   据: 主线加 2 个 `[patch]` 段(键 https + 条目 ssh · 跨 scheme)⇒ 令声明面生效 ⇒
#   lock 随动重解析(`xw-compress` `0.1.0`/`?tag=v0.1.1#c41b4e75b` ⇒ `0.1.2`/`?tag=v0.1.2#578be3317`
#   ∧ `xw-nudge` `0.1.0`/`?tag=v0.1.2#065197f3e` ⇒ `0.1.3`/`?tag=v0.1.3#6c9ac9283`)⇒ 二仓 **`V = T`**
#   ⇒ 名册仍载即【僵尸】⇒ 检查21 `FAIL`(**承 `xw-tools`/`xwtui`/`xwcore`/批丁/④重钉 同族** —— 六例同根)。
#   ★ 二态对照(本处之证 · **无管道**直取): 改前 ⇒ **rc=1**
#     (`FAIL 名册僵尸项: xw-nudge xw-compress(已登记而本刻 V=T ⇒ 名册须随动删)`)/ 改后 ⇒ **rc=0**(破例 0 条)。
#   ★★ 名册【清空】为本版【目标态】—— `AC5` 之「破例」于本版全数清偿(`AC3` 差集亦随之空)。
# ★★ v1.7.0 集成批【随动 · 零项】(2026-10-03 · 承 ar 集成裁决 01 §五 · 令③): **名册不动**(仍 `0`)——
#   据(我侧现取): `xwkernel` 二 `tag` 之差(`4f8245ea9` ⇄ `e6e4231b`)唯六处补 `license.workspace`
#   ∧ 版本 `1.4.9` ⇒ `1.4.10` ⇒ **零依赖钉变动** ⇒ `kernel-host` 之传递钉(含 `xwcapgap` `v0.1.2`)
#   **未随动** ⇒ 本批【不产生新僵尸】∧ 亦【不新增破例】。
#   ★ 判据面之显名: 本批之随动面 = 主线 `Cargo.toml` 双面八行 ∧ `Cargo.lock` 八行(kernel 四包
#     之 `version`/`source`)∧ `v143_structural_guards.sh` 之 `KERNEL_TAG`; **本脚本之名册面零动**。
#     ★★ 补注(`v1.9.12` M-CICD 随手批 · 承 `ar` 裁决 01 裁 1「`v143` 删档」): `v143_structural_guards.sh`
#     已**全档删除**(死世代 —— 不在跑线〔世代解析取最大 ⇒ 现役 = `v154`〕∧ `检查14` 名册零 `v143_*`)。
#     上句系 **`v1.7.0` 当时**之随动面**史实**(不改);删档后其「`KERNEL_TAG`」之指已无档可指
#     ⇒ 依「禁静默陈旧」补本注。★ 本脚本之名册面**仍零动**(删档不改本脚本任何判据)。
#   ★ 并载(承在册【升钉使 V=T ⇒ 须从名册删名】之律 · 其射程限): 该律之【触发条件】= 名册所载包
#     之 `V` 因升钉而 **= `T`**; 本批无该情形(名册已空 ∧ 无新 `V = T`)⇒ **律不触发**。
# ★★ v1.7.3 AC5 bump【入册 · +2 条】(2026-10-05 · 承 ar 裁决「取①」): `xw-llm` ∧ `xw-llm-kernel` ——
#   据(一手读 `v0.1.7` checkout): `xwproviders` 仓 `v0.1.7` 之根 `Cargo.toml` version=**0.1.7** 而
#   `crates/xw-llm` ∧ `crates/xw-llm-kernel` 仍 **0.1.6** ⇒ 该二包 `V(0.1.6) ≠ T(0.1.7)` = **版本塌陷**。
#   前态对照(`v0.1.6` = `2293948`): 根 0.1.6 ∧ 二子 crate 皆 0.1.6 ⇒ 全 `V=T`(故 M1 时本门 rc=0)
#   ⇒ 塌陷系 `v0.1.7` **新引入**。
#   ★ 归因(**非主线之责**): 插件侧**版本卫生**(`tag` 升而二子 crate 未随升)⇒ 主线钉值 `v0.1.7`
#     系本批目标, **无误** ⇒ 不可「订正钉值」消之, 唯**显名入册**。
#   ★ 功能影响 = **零**(cargo 钉值凭 `tag` + `rev`, 非 `version` 字段)—— 承 `xw-compress`/`xw-nudge`
#     判例(其真钉在插件侧 manifest ⇒ 主线唯入册)。
#   ★ **销册条件**(具名 · 承「禁静默陈旧」): 上游 `xwproviders` 对齐二子 crate 版本(随新 `tag`)
#     ⇒ 该二包 `V = T` ⇒ **本二条须随动删**(否则触发下方「名册僵尸项」FAIL —— 自守即此)。
# ★★ v1.7.4 AC1【随动 · 销册 ⇒ 名册 `2` → `0`】(2026-10-05 · 承上条所载【销册条件】之达成):
#   **删 `xw-llm` ∧ `xw-llm-kernel`** ——
#   据(一手读 `xwproviders` `v0.1.8` 树, deref `6fd9463d792a93f583b62201e7e629279393b62c`): 该 tag 已将
#   三 crate 之版本形改 `version.workspace = true`(根 `Cargo.toml:37` `version = "0.1.8"` ∧
#   `crates/xw-llm/Cargo.toml:29` ∧ `crates/xw-llm-kernel/Cargo.toml:26` 皆 `version.workspace = true`
#   ⇒ 与 `crates/xwproviders/Cargo.toml:15` 同款)⇒ 二包 **`V = T = 0.1.8`** ⇒ 上条【销册条件】达成。
#   ★ 主线侧随动: `Cargo.toml` 双面六行(声明面 `:406/:410/:411` ∧ `[patch."…/xwproviders.git"]` 面
#     `:805/:806/:807`)`v0.1.7` → `v0.1.8` ⇒ `Cargo.lock` 三包 `version` `0.1.6`/`0.1.7` → `0.1.8`。
#   ★ 二态对照(本处之证 · **无管道**直取): 改前 ⇒ **rc=1**
#     (`FAIL 名册僵尸项: xw-llm xw-llm-kernel(已登记而本刻 V=T ⇒ 名册须随动删)`)/ 改后 ⇒ **rc=0**(破例 0 条)。
# ★ v1.9.11 `M2` WASM host P0 批(2026-10-20 · 承 `ar` 裁04 裁3「升钉①即起」): **增 `xw-plugin-kernel` ⇒ 名册 `0` → `1`** ——
#   据(一手读 `xwwasm` `v0.1.7` 树, deref `6b1d85e04fc96e46ba0865dd0f79ba24f76f4ae6`): 该 tag 根 `Cargo.toml:29`
#   `version = "0.1.7"` ∧ `crates/xw-plugin/Cargo.toml:12` 与 `crates/xw-plugin-wasmtime/Cargo.toml:18` 皆
#   `version.workspace = true` ⇒ 二包 **`V = T = 0.1.7`**; 而 `crates/xw-plugin-kernel/Cargo.toml:3`
#   `version = "0.1.1"` ⇒ **`V = 0.1.1` ≠ `T = 0.1.7`**(方向 `V ≤ T` ✓ —— 多包 workspace 之【独立版本轴】,
#   非「钉了旧 tag」之真错钉)。
#   ★ 入图之由: 本批升钉使 `xw-plugin`(`v0.1.7`)携 `xw-plugin-kernel` 入主线解析图(`R2` `CompositeSource` 面)
#     ⇒ 该破例**首次现于主线 lock** ⇒ 名册**须同批登记**(不登 ⇒ 检查21 `FAIL 名册外之破例`)。
#   ★ 二态对照(本处之证 · **无管道**直取): 登记前 ⇒ **rc=1**(`FAIL 名册外之破例: xw-plugin-kernel`)/
#     登记后 ⇒ **rc=0**(破例 `1` 条 · 全 `V≤T` ∧ 名册完备)。
AC5_REGISTRY=("xw-plugin-kernel")
# ★ 名册【作 ac5_eval 之实参】传入(`ac5_eval <lock> [名册名…]`)—— 不设 env 覆盖,
#   免「实跑面被静默旁路」之洞; 本仓实跑恒不传名册 ⇒ 必取上表。

# ── 谓词 0: 版本序比较(GNU `sort -V`; 禁字符串比较——`0.1.9` vs `0.1.10` 会误判)──
ver_le() { # ver_le A B ⟺ A ≤ B
    local a="$1" b="$2"
    [[ "$a" == "$b" ]] && return 0
    [[ "$(printf '%s\n%s\n' "$a" "$b" | sort -V | head -1)" == "$a" ]]
}

# ── 谓词 1: 抽取 git 直钉包(name / V / T)—— 零正则, 纯字段切分 ──
# 输出行: <name>\t<V>\t<T>
extract_pins() {
    awk '
        function flush() {
            if (src ~ /[?&]tag=/ && name != "" && ver != "") {
                t = src
                sub(/.*[?&]tag=/, "", t)
                sub(/#.*/, "", t)
                sub(/"$/, "", t)
                sub(/^v/, "", t)          # ★ 前导 v 须剥——否则 T 恒 "v…" ≠ V(本处缺陷由阳性对照 f4 当场捕出)
                print name "\t" ver "\t" t
            }
        }
        /^\[\[package\]\]/ { flush(); name=""; ver=""; src=""; next }
        /^name = "/    { l=$0; sub(/^name = "/,"",l);    sub(/"$/,"",l);    name=l }
        /^version = "/ { l=$0; sub(/^version = "/,"",l); sub(/"$/,"",l);    ver=l  }
        /^source = "/  { src=$0 }
        END { flush() }
    ' "$1"
}

# ── 谓词 2: 判据本体 —— 输出「OK」或「FAIL <理由>」, 返回 0/1 ──
# 用法: ac5_eval <lock> [--registry <名…>]
#   不给 --registry ⇒ 取 AC5_REGISTRY(本仓实跑之唯一路径)
#   给 --registry 而零名 ⇒ 【空名册】(阳性对照 f4 用)—— 与「未给」须可区分
ac5_eval() {
    local lock="$1"; shift
    local -a registry=(); local have_reg=0
    if [[ "${1:-}" == "--registry" ]]; then have_reg=1; shift; registry=("$@"); fi
    if [[ "$have_reg" -eq 0 ]]; then registry=("${AC5_REGISTRY[@]}"); fi
    [[ -f "$lock" ]] || { echo "FAIL 锁文件不存在: $lock"; return 1; }
    local pins; pins="$(extract_pins "$lock")"
    [[ -n "$pins" ]] || { echo "FAIL 零 git 直钉包(谓词面取空 ⇒ 无信息量)"; return 1; }

    local -A reg=(); local n
    for n in "${registry[@]}"; do reg["$n"]=1; done

    local -A exc=(); local nv=0 line name V T
    while IFS=$'\t' read -r name V T; do
        [[ -z "${name:-}" ]] && continue
        nv=$((nv+1))
        [[ "$V" == "$T" ]] && continue          # 无破例 ⇒ 无需登记
        exc["$name"]=1
        # ① 方向性
        if ! ver_le "$V" "$T"; then
            echo "FAIL 真错钉: $name V=$V > T=$T(钉了旧 tag)"
            return 1
        fi
    done <<< "$pins"

    # ② 名册完备(双向)
    local unreg=() zombie=()
    for n in "${!exc[@]}"; do [[ -n "${reg[$n]:-}" ]] || unreg+=("$n"); done
    for n in "${registry[@]}"; do [[ -n "${exc[$n]:-}" ]] || zombie+=("$n"); done
    if [[ "${#unreg[@]}" -gt 0 ]]; then
        echo "FAIL 名册外之破例: ${unreg[*]}(V≠T 而未登记 —— 须显名入册或订正钉值)"
        return 1
    fi
    if [[ "${#zombie[@]}" -gt 0 ]]; then
        echo "FAIL 名册僵尸项: ${zombie[*]}(已登记而本刻 V=T ⇒ 名册须随动删)"
        return 1
    fi

    echo "OK git 直钉 $nv 包 ∧ 破例 ${#exc[@]} 条(全 V≤T ∧ 名册完备)"
    return 0
}

# ── 阳性对照夹具(裁定 70: 果须接入出口码)──
# 夹具 1: V > T 真错钉 ⇒ 须 FAIL
# 夹具 2: 名册外破例 ⇒ 须 FAIL
# 夹具 3: 名册内 V ≤ T ⇒ 须 OK
# 夹具 4: V == T(无破例 ∧ 名册空)⇒ 须 OK —— 以【空名册】跑, 验「零破例」路径
mkfix() { printf '%s\n' "$@" > "$1"; }
FIX_DIR="$(mktemp -d)"
trap 'rm -rf "$FIX_DIR"' EXIT

mkfix "$FIX_DIR/f1.lock" \
  '[[package]]' 'name = "xwcore"' 'version = "0.9.0"' \
  'source = "git+ssh://git@gitcode.com/kemeng1/xwcore.git?tag=v0.1.27#deadbeef"'
mkfix "$FIX_DIR/f2.lock" \
  '[[package]]' 'name = "xw-unknown"' 'version = "0.1.0"' \
  'source = "git+ssh://git@gitcode.com/kemeng1/xwunknown.git?tag=v0.1.5#deadbeef"'
mkfix "$FIX_DIR/f3.lock" \
  '[[package]]' 'name = "xwcore"' 'version = "0.1.25"' \
  'source = "git+ssh://git@gitcode.com/kemeng1/xwcore.git?tag=v0.1.27#deadbeef"'
mkfix "$FIX_DIR/f4.lock" \
  '[[package]]' 'name = "xw-plugin"' 'version = "0.1.3"' \
  'source = "git+ssh://git@gitcode.com/kemeng1/xwwasm.git?tag=v0.1.3#deadbeef"'

POS_OK=1
run_fixture() { # run_fixture <名> <锁> <期望 rc> <名册…(空串 = 空名册)>
    local label="$1" lock="$2" want="$3"; shift 3
    local out rc; local -a reg=()
    [[ -n "${1:-}" ]] && read -r -a reg <<< "$1"
    out="$(ac5_eval "$lock" --registry ${reg[@]+"${reg[@]}"})"; rc=$?
    if [[ "$rc" -ne "$want" ]]; then
        bad "阳性对照失效: $label 期望 rc=$want 实得 rc=$rc(输出: $out)"
        POS_OK=0
    else
        say "  对照 $label: rc=$rc 符($out)"
    fi
}

say "── 阳性对照(裁定 70: 果接入出口码)──"
run_fixture "f1 真错钉 V>T"     "$FIX_DIR/f1.lock" 1 "xwcore"
run_fixture "f2 名册外破例"     "$FIX_DIR/f2.lock" 1 ""
run_fixture "f3 名册内 V≤T"     "$FIX_DIR/f3.lock" 0 "xwcore"
run_fixture "f4 零破例 V=T"     "$FIX_DIR/f4.lock" 0 ""

if [[ "$POS_OK" -ne 1 ]]; then
    bad "阳性对照未全符 ⇒ 本谓词【不可信】—— 禁以本脚本之「本仓绿」充判据成立(裁定 70)"
fi

if [[ "$SELFTEST_ONLY" -eq 1 ]]; then
    [[ "$FAIL" -eq 0 ]] && say "自检通过 ✓" || echo "自检失败 ✗" >&2
    exit "$FAIL"
fi

# ── 本仓实跑(不传名册 ⇒ 必取 AC5_REGISTRY; 名册旁路面无 env 入口, 见上注)──
say "── 本仓判据(面: $LOCK_FILE; 名册 ${#AC5_REGISTRY[@]} 条)──"
if out="$(ac5_eval "$LOCK_FILE")"; then
    say "AC5 (丁) 判据: $out ✓"
else
    bad "AC5 (丁) 判据: $out"
fi

[[ "$FAIL" -eq 0 ]] && say "AC5 组合审计门禁 PASS ✓"
exit "$FAIL"