agent-exec transport 54fb3271bcec24c48fa8696f95200638/616960aa035d

This commit is contained in:
agent-exec-transport
2026-10-18 11:49:07 +00:00
parent f9ebafb64b
commit 05a60f750f
10 changed files with 331 additions and 5 deletions
+10 -1
View File
@@ -13,7 +13,7 @@ PLUGINS := plugins
COMBO := core=$(shell git -C $(CORE) rev-parse --short HEAD 2>/dev/null)+backend=$(shell git -C $(BACKEND) rev-parse --short HEAD 2>/dev/null)+frontend=$(shell git -C $(FRONTEND) rev-parse --short HEAD 2>/dev/null)+ui=$(shell git -C $(UI) rev-parse --short HEAD 2>/dev/null)+agentcmd=$(shell git -C deps/agent-command rev-parse --short HEAD 2>/dev/null)+documents=$(shell git -C modules/documents rev-parse --short HEAD 2>/dev/null)+native=$(shell git -C deps/agent-native rev-parse --short HEAD 2>/dev/null)+squad=$(shell git -C deps/agent-squad rev-parse --short HEAD 2>/dev/null)+intent=$(shell git -C deps/agent-intent rev-parse --short HEAD 2>/dev/null)
VERSION := $(shell git describe --tags --always --dirty 2>/dev/null)+$(COMBO)
.PHONY: help sync status build frontend svc svc-list svc-info svc-commits svc-history svc-update svc-recover svc-watchdog test-backend test-backend-pg test-multi-instance test-lifecycle test-frontend test-ui test-core test-agentcmd test-pins test-libs dev pin clean setup precheck task-triage task-triage-decide task-start task-resume task-sync task-close task-finish task-pr-check ci-wait facts-verify evidence version agent-exec-validate agent-exec-apply agent-exec-admit
.PHONY: help sync status build frontend svc svc-list svc-info svc-commits svc-history svc-update svc-recover svc-watchdog test-backend test-backend-pg test-multi-instance test-lifecycle test-regulations-kb test-frontend test-ui test-core test-agentcmd test-pins test-libs dev pin clean setup precheck task-triage task-triage-decide task-start task-resume task-sync task-close task-finish task-pr-check ci-wait facts-verify evidence version agent-exec-validate agent-exec-apply agent-exec-admit
version:
@echo "agent-system $(VERSION)"
@@ -87,6 +87,7 @@ help:
@echo "make build 构建单二进制 bin/agent-system(契约同步→前端构建→embed→静态链接)"
@echo "make test 全量测试(boundaries + pins + libs + core + backend + frontend + ui + agent-command)"
@echo "make test-governance 临时真实仓库中的任务、证据、写守卫与 CI 凭据回归"
@echo "make test-regulations-kb 法规知识库组合门禁:需 KBREG_TEST_DATABASE_URL、SVC_RDM_SRC_DIR、KB_SRC_DIR(不纳入 make test)"
@echo "make svc 服务级操作:make svc ENV=<name> [ACTION=deploy|start|stop|restart|status|logs] [ARGS='--from dev@<sha> --tag ...']"
@echo "make svc-list 服务总览:make svc-list [ARGS='--tag core'];数据库运维:scripts/svc.sh db create|dsn|backup|backups|restore|clone|copy|ls|set"
@echo "make svc-info 综合信息报告(只读):make svc-info [ARGS='pg1 --reveal']——运行服务/数据库实例与内部库/配置快照"
@@ -164,6 +165,14 @@ test-lifecycle:
bash scripts/fetch-rdm-pin.sh tests/integration/lifecycle/rdm.pin >/dev/null
python3 tests/integration/lifecycle/lifecycle_suite.py
# 法规知识库组合门禁(U8):固定 RDM/KB 修订上的 KB test-pg、RDM kbreg 与客户端制品凭据扫描。
# 缺环境直接失败,不静默跳过;不纳入 make test。见 tests/integration/regulations-kb/README.md。
test-regulations-kb:
@test -n "$${KBREG_TEST_DATABASE_URL}" || { echo "test-regulations-kb: KBREG_TEST_DATABASE_URL 未设置(一次性 PostgreSQL+pgvector 必需,不静默跳过)"; exit 1; }
@test -n "$${SVC_RDM_SRC_DIR}" || { echo "test-regulations-kb: SVC_RDM_SRC_DIR 未设置(RDM 检出必需,不静默跳过)"; exit 1; }
@test -n "$${KB_SRC_DIR}" || { echo "test-regulations-kb: KB_SRC_DIR 未设置(KB-Regulations 检出必需,不静默跳过)"; exit 1; }
bash tests/integration/regulations-kb/run.sh
# 双进程 HTTP 级验收(协作存储方案 U7):两个真实 serve 进程共享隔离 PG。
# 缺环境时明确失败而非静默跳过;见 tests/integration/multi-instance/README.md。
test-multi-instance:
+16
View File
@@ -82,3 +82,19 @@ U2 起实施与部署分别评审。
- [渐进工作台界面重设计 r0.2](modules/documents/docs/proposals/2026-10-12-work-companion-ui-design.md):用户已授权连续实施;[计划 r1](modules/documents/docs/proposals/2026-10-12-work-companion-ui-plan.md)覆盖任务摘要、对话/输入、窄屏和真实状态投影;进展见对应任务卡。
- [系统项目、成员工作入口与私域协作闭环 r1](modules/documents/docs/proposals/2026-10-13-system-project-membership.md):2026-10-13 用户批准阶段 1–9 连续推进;统一全局项目身份与名称、成员个人会话、每项目两树一网和私域权限;当前组合验证/CI/tag 消费推进中,部署与线上修复未授权。
## 法规知识库组合
[法规知识库计划](docs/superpowers/plans/2026-10-18-regulations-knowledge-base-plan.md) U8 的固定组合:
- `modules/backend` 56a7f9f(EXT-20 法规检索/搜索扩展合同)、`modules/documents` 5bb4877(EXT-20 提案)、
`modules/frontend` 50ec836 与 `modules/ui` 5c2b103(法规引用渲染)。
- RDM ece91da(含 kbreg 组合)与 KB-Regulations 5dddad2,固定在
`tests/integration/regulations-kb/components.pin`;KB 经 RDM 的 `pins/agent-system-kb-regulations`
gitlink 接入,门禁要求该 gitlink 是 `kb_sha` 的祖先。
- 凭据变量:`KBREG_DATABASE_URL`、`KBREG_EMBED_API_KEY`、`KBREG_SEARCH_ZHIPU_API_KEY`、`KBREG_MINERU_TOKEN`
(测试库用 `KBREG_TEST_DATABASE_URL`)。按环境注入、轮换与撤销见 KB 仓 `docs/operations/runbook.md`;值不入仓、不回显。
- 门禁:`make test-regulations-kb`(需 `KBREG_TEST_DATABASE_URL`、`SVC_RDM_SRC_DIR`、`KB_SRC_DIR`,不纳入 `make test`),
说明见 `tests/integration/regulations-kb/README.md`。真实搜索/嵌入/MinerU 调用 NOT_RUN,待 owner 批准。
部署、版本发布与业务/法务验收是后续独立门禁,本组合门禁通过不代表其中任何一项完成。
@@ -0,0 +1,53 @@
# 法规知识库组合门禁(U8)
在固定组件修订上验证 RDM + 法规知识库(KB-Regulations)的本地组合:数据与索引可恢复、
双源与引用流程可用、凭据不进入客户端制品。对应
[法规知识库计划](../../../docs/superpowers/plans/2026-10-18-regulations-knowledge-base-plan.md) 的 U8
与 Verification Contract 的 Composition/recovery 行;引用对象见
[法规引用合同](../../../docs/superpowers/specs/2026-10-18-regulations-citation-contract.md)。
## 固定组件
`components.pin` 只有两个键,均为 40 位 hex,`run.sh` 逐行解析、从不执行:
- `rdm_sha`:Agent-System-RDM main(含 U6、U7 与 kbreg 组合)。
- `kb_sha`:Agent-System-KB-Regulations main(含 provider 与 U8 恢复测试)。
`run.sh` 先核对两个检出的 HEAD 等于 pin,再核对 RDM 检出中
`pins/agent-system-kb-regulations` gitlink 是 KB 检出里 `kb_sha` 的祖先(RDM 编译所用 KB 代码包含在被测修订中)。
Dev 侧 Backend/Documents/Frontend/UI 的 gitlink 由本仓 `modules/` 固定,见 PROJECT.md「法规知识库组合」。
## 前置条件
- 一次性 PostgreSQL(带 pgvector),连接串放在 `KBREG_TEST_DATABASE_URL`;PATH 上有 `pg_dump`、`pg_restore`。
可用 KB 仓 `scripts/test-pg-local.sh` 起临时库。门禁会写入该库,不要指向任何真实数据。
- `SVC_RDM_SRC_DIR`:RDM 检出,位于 `rdm_sha`,submodule 已递归物化。
- `KB_SRC_DIR`:KB-Regulations 检出,位于 `kb_sha`,submodule 已物化。
- Go 与 Node(RDM 测试及客户端制品构建所需)。
- 客户端制品扫描需要已构建的 RDM 产物(RDM 检出的 `bin/`、`.build/product/frontend/dist`),
或用 `REGKB_SCAN_DIRS` 指定待扫目录(空格分隔)。
## 用法
```bash
KBREG_TEST_DATABASE_URL=... SVC_RDM_SRC_DIR=/path/to/rdm KB_SRC_DIR=/path/to/kb \
make test-regulations-kb
```
缺任一变量时直接以非零退出并指出变量名,不运行任何阶段。每个阶段打印 `PASS <阶段>` 或
`FAIL <阶段>`,任一失败则整体非零退出。脚本不回显 `KBREG_TEST_DATABASE_URL` 或任何凭据值。
## 各阶段证明什么
| 阶段 | 命令 | 对应 U8 场景 |
|---|---|---|
| `kb-test-pg` | `make -C "$KB_SRC_DIR" test-pg` | provider 组合链;dump/restore 后重建索引仍保持语料修订哈希与历史引用解析;provider/解析器不可用;凭据注入、轮换与撤销 |
| `rdm-kbreg` | `GOWORK=off go test -count=1 ./plugins/kbreg/...`,以及 `-run 'Composed\|RegCite\|Citation\|RegCheck\|RegBatch\|RegAudit'` 覆盖 `./internal/rdmapp/...`、`./plugins/rdm/...` | 完整法规问答的双通道记录、条款引用与审计批次重放;缺一通道时报告降级而非双源通过;保密词留在本地;凭据不进入输出;审计封印校验 |
| `client-asset-secret-scan` | 扫描 `REGKB_SCAN_DIRS`(缺省为 RDM 的 `bin/` 与 `.build/product/frontend/dist`) | 凭据不出现在客户端制品:任何文件含字符串 `KBREG_`,或含环境中非空 `KBREG_*KEY*`、`KBREG_*TOKEN*`、`KBREG_DATABASE_URL` 的值即失败;只打印文件路径与变量名。没有可扫目录时失败 |
跨项目与受限来源的拒绝/脱敏场景归入 `rdm-kbreg` 阶段所选的 RDM 命令总线测试;具体用例以固定 RDM 修订中的测试为准。
## 不在本门禁范围
- 真实网络搜索、嵌入与 MinerU 调用:NOT_RUN,待 owner 批准外部调用后另行执行。
- 部署、版本发布与业务/法务验收:均为后续独立门禁,本门禁通过不代表其中任何一项完成。
@@ -0,0 +1,6 @@
# 法规知识库组合门禁的固定组件(U8)。只接受 rdm_sha、kb_sha 两个键,值为 40 位 hex。
# run.sh 逐行解析本文件,不执行、不 source。升级走 PR。
# rdm_sha:RDM main,含 U6、U7 与 kbreg 组合。
# kb_sha:KB-Regulations main,含 provider 与 U8 恢复测试;须包含 RDM pins/agent-system-kb-regulations。
rdm_sha=ece91dab9f23628be54a91e846389cb3753b88dd
kb_sha=5dddad2ae58e7d0d7fffc95464e80fa0040d5e08
+140
View File
@@ -0,0 +1,140 @@
#!/usr/bin/env bash
# 法规知识库组合门禁(U8)。缺环境直接失败,不静默跳过。
# 不回显任何 KBREG_* 值;扫描结果只打印文件路径与变量名。
set -euo pipefail
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
pin="${REGKB_PIN:-$here/components.pin}"
# 与 scripts/fetch-rdm-pin.sh 同规则:只认已知键、40 位 hex,不执行 pin 文件。
[ -f "$pin" ] || { echo "components.pin 不存在:$pin" >&2; exit 1; }
declare -A got=()
while IFS= read -r line || [ -n "$line" ]; do
case "$line" in
''|\#*) continue ;;
esac
case "$line" in
*=*) ;;
*) echo "components.pin 行格式错误(须为 key=sha)" >&2; exit 1 ;;
esac
key="${line%%=*}"
val="${line#*=}"
case "$key" in
rdm_sha|kb_sha) ;;
*) echo "components.pin 未知键:$key" >&2; exit 1 ;;
esac
[[ "$val" =~ ^[0-9a-f]{40}$ ]] || { echo "components.pin 的 $key 不是 40 位 hex" >&2; exit 1; }
[ -z "${got[$key]:-}" ] || { echo "components.pin 重复键:$key" >&2; exit 1; }
got["$key"]="$val"
done < "$pin"
[ "${#got[@]}" -eq 2 ] || { echo "components.pin 必须同时有 rdm_sha、kb_sha" >&2; exit 1; }
rdm_sha="${got[rdm_sha]}"
kb_sha="${got[kb_sha]}"
missing=0
for v in KBREG_TEST_DATABASE_URL SVC_RDM_SRC_DIR KB_SRC_DIR; do
if [ -z "${!v:-}" ]; then
echo "regulations-kb: $v 未设置(组合门禁必需,不静默跳过)" >&2
missing=1
fi
done
[ "$missing" -eq 0 ] || exit 2
rdm_dir="$SVC_RDM_SRC_DIR"
kb_dir="$KB_SRC_DIR"
head_of() { git -C "$1" rev-parse --verify -q HEAD 2>/dev/null || true; }
rdm_head="$(head_of "$rdm_dir")"
[ "$rdm_head" = "$rdm_sha" ] || { echo "regulations-kb: SVC_RDM_SRC_DIR HEAD=${rdm_head:-<无>},须为 rdm_sha=$rdm_sha" >&2; exit 1; }
kb_head="$(head_of "$kb_dir")"
[ "$kb_head" = "$kb_sha" ] || { echo "regulations-kb: KB_SRC_DIR HEAD=${kb_head:-<无>},须为 kb_sha=$kb_sha" >&2; exit 1; }
kb_link="$(git -C "$rdm_dir" rev-parse --verify -q "HEAD:pins/agent-system-kb-regulations" 2>/dev/null || true)"
[ -n "$kb_link" ] || { echo "regulations-kb: RDM 检出缺少 pins/agent-system-kb-regulations gitlink" >&2; exit 1; }
if ! git -C "$kb_dir" merge-base --is-ancestor "$kb_link" "$kb_sha" 2>/dev/null; then
echo "regulations-kb: RDM 的 KB gitlink $kb_link 不是 kb_sha=$kb_sha 的祖先(或 KB 检出缺该对象)" >&2
exit 1
fi
echo "regulations-kb: rdm=$rdm_sha kb=$kb_sha(RDM KB gitlink $kb_link 为其祖先)"
failed=0
# 在 if 条件中 set -e 不生效:阶段函数内多条命令须显式 && 串联。
stage() {
local name="$1"; shift
echo "=== stage $name ==="
if ( "$@" ); then
echo "PASS $name"
else
echo "FAIL $name"
failed=1
fi
}
stage_kb() {
make -C "$kb_dir" test-pg
}
stage_rdm() {
cd "$rdm_dir" &&
GOWORK=off go test -count=1 ./plugins/kbreg/... &&
GOWORK=off go test -count=1 -run 'Composed|RegCite|Citation|RegCheck|RegBatch|RegAudit' ./internal/rdmapp/... ./plugins/rdm/...
}
stage_scan() {
local dirs=() d
if [ -n "${REGKB_SCAN_DIRS:-}" ]; then
read -r -a dirs <<< "$REGKB_SCAN_DIRS"
else
for d in "$rdm_dir/bin" "$rdm_dir/.build/product/frontend/dist"; do
[ -d "$d" ] && dirs+=("$d")
done
fi
if [ "${#dirs[@]}" -eq 0 ]; then
echo "scan: 没有可扫描的客户端制品目录(先构建 RDM 产品,或设置 REGKB_SCAN_DIRS)" >&2
return 1
fi
local secret_vars=() name
while IFS= read -r name; do
case "$name" in
KBREG_*KEY*|KBREG_*TOKEN*|KBREG_DATABASE_URL)
[ -n "${!name:-}" ] && secret_vars+=("$name") ;;
esac
done < <(compgen -e)
local tmp rc=0 hits f
tmp="$(mktemp)" || return 1
for d in "${dirs[@]}"; do
if [ ! -d "$d" ]; then
echo "scan: 目录不存在:$d" >&2
rc=1
continue
fi
echo "scan: $d"
hits="$(grep -rlF -e 'KBREG_' -- "$d" || true)"
if [ -n "$hits" ]; then
while IFS= read -r f; do echo "scan: $f 含字符串 KBREG_" >&2; done <<< "$hits"
rc=1
fi
for name in "${secret_vars[@]}"; do
printf '%s\n' "${!name}" | grep -v '^$' > "$tmp" || true
[ -s "$tmp" ] || continue
hits="$(grep -rlF -f "$tmp" -- "$d" || true)"
if [ -n "$hits" ]; then
while IFS= read -r f; do echo "scan: $f 含 $name 的值" >&2; done <<< "$hits"
rc=1
fi
done
done
rm -f "$tmp"
echo "scan: 已核对 ${#secret_vars[@]} 个凭据变量:${secret_vars[*]:-<无>}"
return "$rc"
}
stage kb-test-pg stage_kb
stage rdm-kbreg stage_rdm
stage client-asset-secret-scan stage_scan
if [ "$failed" -ne 0 ]; then
echo "regulations-kb: FAIL"
exit 1
fi
echo "regulations-kb: PASS"
+102
View File
@@ -0,0 +1,102 @@
"""法规知识库组合门禁:components.pin 只接受固定 SHA;run.sh 缺环境直接失败。"""
import os
import re
import subprocess
import tempfile
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
GATE = ROOT / "tests" / "integration" / "regulations-kb"
PIN = GATE / "components.pin"
RUN = GATE / "run.sh"
HEX40 = re.compile(r"^[0-9a-f]{40}$")
KEYS = {"rdm_sha", "kb_sha"}
REQUIRED = ("KBREG_TEST_DATABASE_URL", "SVC_RDM_SRC_DIR", "KB_SRC_DIR")
def load_pin(text):
out = {}
for line in text.splitlines():
if not line or line.startswith("#"):
continue
if "=" not in line:
raise ValueError("bad pin line")
key, value = line.split("=", 1)
if key not in KEYS:
raise ValueError("unknown pin key")
if not HEX40.match(value):
raise ValueError("sha must be 40 hex")
if key in out:
raise ValueError("duplicate pin key")
out[key] = value
if set(out) != KEYS:
raise ValueError("pin keys incomplete")
return out
def run_gate(env, timeout=20):
base = {"PATH": os.environ.get("PATH", "/usr/bin:/bin")}
base.update(env)
return subprocess.run(
["bash", str(RUN)], cwd=ROOT, env=base, capture_output=True, text=True, timeout=timeout
)
class RegulationsKbPinTests(unittest.TestCase):
def test_pin_is_two_fixed_shas(self):
doc = load_pin(PIN.read_text())
self.assertEqual(set(doc), KEYS)
for value in doc.values():
self.assertRegex(value, HEX40)
def test_parser_rejects_url_short_sha_and_unknown_key(self):
kb = "kb_sha=" + "b" * 40 + "\n"
bad = [
"rdm_sha=https://example.invalid/repo.git\n" + kb,
"rdm_sha=abc\n" + kb,
"rdm_sha=" + "a" * 40 + "\n" + kb + "url=" + "c" * 40 + "\n",
]
for text in bad:
with self.assertRaises(ValueError):
load_pin(text)
def test_run_sh_parser_rejects_bad_pins_before_env_check(self):
kb = "kb_sha=" + "b" * 40 + "\n"
cases = {
"rdm_sha=https://example.invalid/repo.git\n" + kb: "不是 40 位 hex",
"rdm_sha=abc\n" + kb: "不是 40 位 hex",
"rdm_sha=" + "a" * 40 + "\n" + kb + "url=" + "c" * 40 + "\n": "未知键",
"rdm_sha=$(touch /tmp/regkb-pwned)\n" + kb: "不是 40 位 hex",
kb: "必须同时有",
}
for text, expect in cases.items():
with tempfile.NamedTemporaryFile("w", suffix=".pin", delete=False) as f:
f.write(text)
path = f.name
try:
proc = run_gate({"REGKB_PIN": path})
finally:
os.unlink(path)
self.assertNotEqual(proc.returncode, 0, text)
self.assertIn(expect, proc.stderr, text)
self.assertNotIn("=== stage", proc.stdout)
def test_run_sh_names_each_missing_variable_without_running_stages(self):
proc = run_gate({})
self.assertNotEqual(proc.returncode, 0)
for name in REQUIRED:
self.assertIn(name, proc.stderr)
self.assertNotIn("=== stage", proc.stdout)
for missing in REQUIRED:
env = {n: "/nonexistent-fake" for n in REQUIRED if n != missing}
proc = run_gate(env)
self.assertNotEqual(proc.returncode, 0, missing)
self.assertIn(missing + " 未设置", proc.stderr)
self.assertNotIn("=== stage", proc.stdout)
self.assertNotIn("/nonexistent-fake", proc.stdout + proc.stderr)
if __name__ == "__main__":
unittest.main()