# Agent-System-Dev 顶层编排
# 部署约束：非容器化；默认产物为单二进制（modules/backend，内嵌前端与嵌入式存储）。

SHELL := /bin/bash
CORE       := modules/core
BACKEND    := modules/backend
FRONTEND   := modules/frontend
UI         := modules/ui
BIN        := bin/agent-system
EMBED_DIR  := $(BACKEND)/web/dist
PLUGINS    := plugins
# 版本溯源（release-operations：运行制品须可对上固定组合）。无 tag 时 --always 给短 SHA。
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-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 image-build image-verify

version:
	@echo "agent-system $(VERSION)"

TASK := scripts/task.sh
export PRECHECK_RECEIPT RECEIPT IMPORT_RECEIPT TASK_WRITERS_STOPPED TRIAGE_RECEIPT
TASK_WORK_MODE ?= delivery
export TASK_WORK_MODE
TASK_PARENT_BRANCH ?= $(PARENT)
TASK_PARENT_PR ?= $(PARENT_PR)
export TASK_PARENT_BRANCH TASK_PARENT_PR
TASK_BRANCH_KIND ?= $(KIND)
export TASK_BRANCH_KIND
TASK_INITIATIVE ?= $(INITIATIVE)
export TASK_INITIATIVE

setup:
	git config core.hooksPath scripts/hooks
	git config submodule.fetchJobs 8
	chmod +x scripts/hooks/pre-commit scripts/hooks/post-merge scripts/task.sh scripts/evidence.sh scripts/facts-verify.sh scripts/agent-exec
	@echo "OK: pre-commit/post-merge 钩子已启用（core.hooksPath=scripts/hooks）"

precheck:
	@bash $(TASK) precheck

task-triage:
	@bash $(TASK) triage "$(REQ)"

task-triage-decide:
	@VERDICT="$(VERDICT)" INITIATIVE="$(INITIATIVE)" REASON="$(REASON)" bash $(TASK) triage-decide

task-start:
	@if [ -z "$(NAME)" ]; then echo "usage: make task-start NAME=feature/<slug> TRIAGE_RECEIPT=<path> [PRECHECK_RECEIPT=<path>]（KIND=child 已登记在父卡时可省略 TRIAGE_RECEIPT）"; exit 2; fi
	@bash $(TASK) start "$(NAME)"

task-resume:
	@bash $(TASK) resume

task-sync:
	@bash $(TASK) sync

# 任务卡闭合：默认在任务 worktree 合入前运行 SELF=1（本卡随当前 PR 完成并归档，无需单独闭合 PR）；
# 不带参数（主检出）或 FOLD=--fold 仅用于已合入未闭合的遗留卡。
task-close:
	@bash $(TASK) close $(if $(SELF),--self,$(FOLD))

task-finish:
	@bash $(TASK) finish $(ABANDON)

# PR base/head 门禁（两级分支）：本地分支卡片期望目标 vs Gitea PR 与 origin；FINAL=1 用于 initiative 收口。
task-pr-check:
	python3 scripts/task_pr_check.py local $(if $(PR),--pr $(PR)) $(if $(FINAL),--final)

# CI 等待（单条阻塞命令）：轮询 Gitea 合并状态，失败即停；两次连续全绿才算成功。默认 SHA=HEAD、20 分钟超时。
ci-wait:
	python3 scripts/ci_wait.py $(if $(SHA),--sha $(SHA)) $(if $(TIMEOUT),--timeout $(TIMEOUT)) $(if $(INTERVAL),--interval $(INTERVAL)) $(if $(CONTEXTS),--contexts $(CONTEXTS))

# 证据写入：执行命令并把结果行追加进当前任务卡（AGENTS.md「证据与验证门禁」）。
# 本地执行只记 SELF；RECEIPT 捕获启动收据，IMPORT_RECEIPT 导入原始收据而不重跑。
# 用法：CMD="<单行命令>" make evidence [TAIL=15]；退出码随被执行命令。
# CMD 经环境直传（不经 make 变量展开重引号，命令内可含引号/管道）；SRC/TAIL 为简单词，走 make 变量。
evidence:
	@SRC='$(SRC)' TAIL='$(TAIL)' bash scripts/evidence.sh

# 外部事实登记表逐条重验（--update 刷新 PASS 项 last_verified）。
facts-verify:
	@bash scripts/facts-verify.sh $(ARGS)

help:
	@echo "make sync        物化全部 submodule（递归）"
	@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']——运行服务/数据库实例与内部库/配置快照"
	@echo "make svc-commits 列出可部署提交：make svc-commits SRC=dev|release [N=20]"
	@echo "make svc-history 事件历史：make svc-history [ENV] [N=20]（完整文档：docs/svc.md）"
	@echo "make svc-update  自动更新过时服务：make svc-update [NAMES='dev pg1'] [ARGS='--check --all']"
	@echo "make svc-recover 自恢复：拉回 autostart=1 但已死的服务：make svc-recover [ARGS='dev --quiet']"
	@echo "make svc-watchdog 自恢复看护：make svc-watchdog ARGS='install|remove|status'（systemd 用户 timer）"
	@echo "make dev         开发模式说明（前后端分离热更新）"
	@echo "make pin         推进 submodule 指针到各仓远端 main（提交前须 make test 通过）"
	@echo "make status      submodule 状态"
	@echo "make image-build 构建 Docker 测试环境镜像 agent-system-test-env:dev"
	@echo "make image-verify 运行镜像内环境自检 verify-env.sh"
	@echo "make agent-exec-validate  校验 AGENT-EXECUTION.md（scripts/agent-exec validate，经 dm-server agent-exec）"
	@echo "make agent-exec-apply RECEIPT=<path>  快进当前分支到成功收据的 transport"
	@echo "make agent-exec-admit  校验暂存路径具备 transport 来源或属于 host 豁免"
	@echo "make task-triage REQ='<需求>' TRIAGE_RECEIPT=<path>  以远端为准发现接纳中的 initiative 并写分流收据（候选存在时 exit 5，待决策）"
	@echo "make task-triage-decide TRIAGE_RECEIPT=<path> VERDICT=JOIN|NEW [INITIATIVE=<branch>] REASON='<理由>'  记录显式分流决策（不按数量自动 JOIN）"
	@echo "make task-start NAME=feature/<slug> TRIAGE_RECEIPT=<path> [KIND=...] [INITIATIVE=...] [PRECHECK_RECEIPT=<path>]  建任务前核验分流收据（失败不建分支）；KIND=child 已登记在父卡「子任务登记」时可省略 TRIAGE_RECEIPT；省略 PRECHECK_RECEIPT 时自动运行启动检查"
	@echo "make task-pr-check [PR=N] [FINAL=1]  核验当前任务分支 PR 的 base/head 门禁（远端为准；FINAL=1 用于 initiative 收口）"
	@echo "make ci-wait [SHA=<完整 SHA>] [TIMEOUT=1200] [INTERVAL=15] [CONTEXTS=a,b]  阻塞等待该 SHA 的 CI 结论（失败即停）"

agent-exec-validate:
	scripts/agent-exec validate --repo .

agent-exec-apply:
	@if [ -z "$(RECEIPT)" ]; then echo "usage: make agent-exec-apply RECEIPT=<path>"; exit 2; fi
	scripts/agent-exec apply --repo . --receipt "$(RECEIPT)"

agent-exec-admit:
	scripts/agent-exec admit --repo .

image-build:
	docker build -t agent-system-test-env:dev docker/

image-verify:
	docker run --rm --shm-size=1g agent-system-test-env:dev /usr/local/bin/verify-env.sh

sync:
	git submodule update --init --recursive --jobs 8

status:
	@git submodule status

# 契约同步：Core 的 TS 类型镜像到前端与 UI 组件库（Core 为权威，单一来源）。
contracts: test-worktree
	cp $(CORE)/ts/types.ts $(FRONTEND)/src/contracts.ts.tmp
	{ echo "// 从 Agent-System-Core ts/types.ts 同步（make contracts 生成；Core 为权威）。"; cat $(FRONTEND)/src/contracts.ts.tmp; } > $(FRONTEND)/src/contracts.ts
	rm $(FRONTEND)/src/contracts.ts.tmp
	cp $(CORE)/ts/types.ts $(UI)/src/contracts.ts.tmp
	{ echo "// 从 Agent-System-Core ts/types.ts 同步（make contracts 生成；Core 为权威）。"; cat $(UI)/src/contracts.ts.tmp; } > $(UI)/src/contracts.ts
	rm $(UI)/src/contracts.ts.tmp

frontend: contracts
	cd $(UI) && npm ci --no-fund --no-audit
	cd $(FRONTEND) && npm ci --no-fund --no-audit && npm run build
	rm -rf $(EMBED_DIR) && mkdir -p $(EMBED_DIR) && cp -r $(FRONTEND)/dist/* $(EMBED_DIR)/ && touch $(EMBED_DIR)/.gitkeep

build: frontend
	cd $(BACKEND) && CGO_ENABLED=0 go build -trimpath -ldflags "-s -w -X main.version=$(VERSION)" -o ../../$(BIN) ./cmd/agent-system
	cd $(BACKEND) && CGO_ENABLED=0 go build -trimpath -ldflags "-s -w" -o ../../bin/storage-migrate ./cmd/storage-migrate
	@echo "OK: $(BIN)（版本 $(VERSION)；随附插件目录 $(PLUGINS)/）+ bin/storage-migrate（协作存储 U4 迁移工具）"

test-core:
	cd $(CORE) && go test ./...

test-backend:
	cd $(BACKEND) && go test ./...

# 真实 PG/Redis 门禁（协作存储方案 U2）：缺环境直接失败而非静默跳过。
# CI 的必跑 job 提供原生服务；本地用 scripts/svc.sh db create <名> --port 8897
# 与 redis-server --port 6399 起隔离实例（见 docs/guides/team-storage.md）。
test-backend-pg:
	@test -n "$${AGENT_PG_TEST_DSN}" || { echo "test-backend-pg: AGENT_PG_TEST_DSN 未设置（真实 PostgreSQL 必需，不静默跳过）"; exit 1; }
	@test -n "$${AGENT_REDIS_TEST_ADDR}" || { echo "test-backend-pg: AGENT_REDIS_TEST_ADDR 未设置（真实 Redis 必需，不静默跳过）"; exit 1; }
	cd $(BACKEND) && go test -count=1 ./...

# 真实 Dev/RDM + PostgreSQL 生命周期门禁。缺环境直接失败，不静默跳过。
# 登录单独标 NOT_RUN（依赖账户方案 U7/U5），不把其余真实组合场景记成失败。
test-lifecycle:
	@test -n "$${AGENT_PG_TEST_DSN}" || { echo "test-lifecycle: AGENT_PG_TEST_DSN 未设置（真实 PostgreSQL 必需，不静默跳过）"; exit 1; }
	@test -n "$${SVC_RDM_SRC_DIR}" || { echo "test-lifecycle: SVC_RDM_SRC_DIR 未设置（RDM 检出必需，不静默跳过）"; exit 1; }
	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:
	@test -n "$${AGENT_PG_TEST_DSN}" || { echo "test-multi-instance: AGENT_PG_TEST_DSN 未设置（真实 PostgreSQL 必需，不静默跳过）"; exit 1; }
	mkdir -p tests/integration/multi-instance/.build
	cd $(BACKEND) && CGO_ENABLED=0 go build -o ../../tests/integration/multi-instance/.build/agent-system ./cmd/agent-system
	cd tests/integration/multi-instance && AGENT_BIN=./.build/agent-system go test -count=1 -timeout 15m .

eval-deliverables:
	cd $(BACKEND) && go test ./eval/ ./internal/quality/ -count=1

test-frontend: contracts
	cd $(FRONTEND) && npm ci --no-fund --no-audit && npx vitest run && node scripts/check-knip.mjs

test-ui: contracts
	cd $(UI) && npm ci --no-fund --no-audit && npx tsc --noEmit && npx vitest run && node scripts/check-knip.mjs

test-agentcmd:
	cd deps/agent-command && go vet ./... && go test -count=1 ./...

test-boundaries:
	bash scripts/lint-boundaries.sh

# 容器仓 deps/ gitlink 与 Dev 顶层指针一致。读索引中的 gitlink。
test-pins:
	bash scripts/check-pins.sh

# 三个库在各自模块内测试。不用 go.work：工作区 MVS 会抬高 golang.org/x 版本。
test-libs:
	cd deps/agent-intent && GOWORK=off go vet ./... && GOWORK=off go test -count=1 ./...
	cd deps/agent-native && GOWORK=off go vet ./... && GOWORK=off go test -count=1 ./...
	cd deps/agent-squad && GOWORK=off go vet ./... && GOWORK=off go test -count=1 ./...
	cd deps/agent-squad/adapters/intent && GOWORK=off go vet ./... && GOWORK=off go test -count=1 ./...
	cd deps/agent-squad/adapters/native && GOWORK=off go vet ./... && GOWORK=off go test -count=1 ./...
	cd deps/agent-squad/adapters/sqlite && GOWORK=off go vet ./... && GOWORK=off go test -count=1 ./...
	cd deps/agent-squad/cmd/agent-squad && GOWORK=off go vet ./... && GOWORK=off go test -count=1 ./...

plugin-new:
	@test -n "$(NAME)" || (echo "用法：make plugin-new NAME=<id>"; exit 2)
	bash scripts/plugin-new.sh "$(NAME)"

.PHONY: test test-governance test-worktree test-affected contracts

# host 最小直接检查：按 BASE（缺省为卡片 immediate_target 或 origin/main）以来的改动选 make 目标；DRY=1 只打印。
test-affected:
	python3 scripts/test_affected.py $(if $(BASE),--base $(BASE)) $(if $(DRY),--dry-run)

test-governance:
	PYTHONDONTWRITEBYTECODE=1 python3 -m unittest discover -s tests -p 'test_*.py' -v

test-worktree:
	python3 scripts/check-worktree.py verify

# 即使 make -j，也先确认实际被测子仓与固定组合一致。
test-boundaries test-pins test-libs test-core test-backend test-frontend test-ui test-agentcmd: | test-worktree

test: test-worktree test-governance test-boundaries test-pins test-libs test-core test-backend test-frontend test-ui test-agentcmd

# 运行演示（mock 模式）：单二进制 + 示例插件 + 嵌入式存储，浏览器 http://127.0.0.1:8787
# 环境服务管理（scripts/svc.sh）：deploy = 停旧 → 构建 → 启动；dev/prod 相互隔离。
svc:
	@test -n "$(ENV)" || (echo "用法：make svc ENV=<name> [ACTION=deploy|stop|start|restart|status|logs] [ARGS='--port 9000 --tag ...']"; exit 2)
	scripts/svc.sh $(ENV) $(ACTION) $(ARGS)

svc-list:
	scripts/svc.sh list $(ARGS)

svc-info:
	scripts/svc.sh info $(ARGS)

svc-commits:
	@test -n "$(SRC)" || (echo "用法：make svc-commits SRC=dev|release [N=20]"; exit 2)
	scripts/svc.sh commits $(SRC) $(N)

svc-history:
	scripts/svc.sh history $(ENV) $(N)

svc-recover:
	scripts/svc.sh recover $(ARGS)

svc-watchdog:
	scripts/svc.sh watchdog $(ARGS)

svc-update:
	scripts/svc.sh update $(NAMES) $(ARGS)

demo: build
	$(BIN) serve --addr 127.0.0.1:8787 --data ./data --plugins $(PLUGINS) --mock

dev:
	@echo "开发模式（原生，非容器）："
	@echo "  1. make sync && make contracts"
	@echo "  2. 终端 A：cd $(BACKEND) && go run ./cmd/agent-system serve --addr 127.0.0.1:8787 --data ./data --plugins ../$(PLUGINS)"
	@echo "  3. 终端 B：cd $(FRONTEND) && npm ci && npm run dev   # Vite 代理到 8787"
	@echo "  4. mock 演示：设置→运行时→演示模式；或终端 A 加 --mock"

# 推进指针（多仓纪律：先验证后固定）。
pin:
	@for m in modules/core modules/backend modules/frontend modules/ui modules/documents deps/agent-native deps/agent-squad deps/agent-intent deps/error-code-system deps/logging-system deps/development-guidelines deps/agent-command; do \
		(git -C $$m fetch -q origin && git -C $$m checkout -q origin/main) || echo "skip $$m"; \
	done
	@echo "submodule 指针已推进；请 make test 后 git add .gitmodules modules deps && git commit"
	@scripts/check-pins.sh || echo "pin: check-pins 未通过，上面列出的容器需要重钉；pin 本身不失败"

clean:
	rm -rf $(BIN) $(FRONTEND)/dist $(FRONTEND)/node_modules $(UI)/node_modules data
