diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index 3c750cef..17f9a9ca 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -8,20 +8,40 @@ on: jobs: test: runs-on: ubuntu-latest + env: + GOPROXY: https://goproxy.cn,direct steps: - - name: Configure submodule auth over HTTPS - run: git config --global url."https://weichen:${{ secrets.SUBMODULE_TOKEN }}@git-home.u1680832.nyat.app:30319/".insteadOf "ssh://git@git-ssh.u1680832.nyat.app:31411/" - - uses: actions/checkout@v4 - with: - submodules: recursive - fetch-depth: 0 - - uses: actions/setup-go@v5 - with: - go-version: "1.27" - - uses: actions/setup-node@v4 - with: - node-version: "22" - - name: Combination test (core + backend + frontend) + - name: Checkout + env: + TOKEN: ${{ secrets.GITHUB_TOKEN }} + SUBMODULE_TOKEN: ${{ secrets.SUBMODULE_TOKEN }} + run: | + set -euo pipefail + git init -q + git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git" + git -c http.extraheader="AUTHORIZATION: token ${TOKEN}" fetch --depth=1 origin "${GITHUB_SHA}" + git checkout -q --detach FETCH_HEAD + if [ -z "${SUBMODULE_TOKEN}" ]; then + echo "SUBMODULE_TOKEN is not configured" >&2 + exit 1 + fi + git config --global url."https://weichen:${SUBMODULE_TOKEN}@git-home.u1680832.nyat.app:30319/".insteadOf "ssh://git@git-ssh.u1680832.nyat.app:31411/" + git -c fetch.recurseSubmodules=false submodule update --init --recursive + - name: Install Go 1.27.1 + run: | + set -euo pipefail + curl -fsSL -o "$RUNNER_TEMP/go.tgz" https://mirrors.aliyun.com/golang/go1.27.1.linux-amd64.tar.gz + tar -C /usr/local -xzf "$RUNNER_TEMP/go.tgz" + ln -sfn /usr/local/go/bin/go /usr/local/bin/go + ln -sfn /usr/local/go/bin/gofmt /usr/local/bin/gofmt + - name: Install Node 22 + run: | + set -euo pipefail + curl -fsSL -o "$RUNNER_TEMP/node.tar.xz" https://npmmirror.com/mirrors/node/v22.23.3/node-v22.23.3-linux-x64.tar.xz + tar -C /usr/local --strip-components=1 -xJf "$RUNNER_TEMP/node.tar.xz" + - name: Install house-style Python packages + run: python3 -m pip install --break-system-packages -i https://pypi.tuna.tsinghua.edu.cn/simple python-docx python-pptx openpyxl + - name: make test (boundaries, pins, libs, core, backend, frontend, ui, agent-command) run: make test - name: Build the single binary run: make build diff --git a/.task/feature-duplication-lesson-records.md b/.task/archive/feature-duplication-lesson-records.md similarity index 92% rename from .task/feature-duplication-lesson-records.md rename to .task/archive/feature-duplication-lesson-records.md index 0164b263..20612e9e 100644 --- a/.task/feature-duplication-lesson-records.md +++ b/.task/archive/feature-duplication-lesson-records.md @@ -1,6 +1,6 @@ # feature/duplication-lesson-records 任务卡 -任务主记录:Agent-System-Documents PR#7(内容)+ 本仓 PR(gitlink 钉入,链接见下方回填);状态:已完成;owner:weichen +任务主记录:PR #13 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/13(merge f20c634fea);状态:已完成(回填);owner:weichen ## 基线与授权 @@ -65,3 +65,9 @@ | 同步前验收 | 3072611209c445a0d87d54545cfd23d2b971e177 | 957de90142bc98bde3f479092e3892754000ef15 | 2026-10-01T05:23:44Z | | 同步前验收 | 4c4071e5d8e256dd8f82ba9c74efa1effd1e9df3 | 0ed945956af3f10231f7d80706c798fc83617e6c | 2026-10-01T04:50:27Z | | 同步前验收 | 4adc4cc458f15546e5605dc55843acc26b310c41 | 916437ca2d93310ed5b4e517f99b1a787d9af843 | 2026-10-01T06:09:46Z | + +## 闭合记录(card-autoclose 回填) + +- 依据:merge f20c634fea9e4316db664b4da7b4a6c772dd855e(PR #13)已包含于 origin/main,分支成果已落盘;本段为合入后回填。 +- 合并主题:Merge pull request 'Pin documents with the duplication lesson and follow-up records' (#13) from feature/duplication-lesson-records into main +- 归档:随闭合移入 .task/archive/;任务执行期的过程证据见原任务记录与 PR。 diff --git a/.task/feature-agent-stack-r0-r1.md b/.task/feature-agent-stack-r0-r1.md new file mode 100644 index 00000000..9f44aaa6 --- /dev/null +++ b/.task/feature-agent-stack-r0-r1.md @@ -0,0 +1,66 @@ +# feature/agent-stack-r0-r1 任务卡 + +任务主记录:PR #152 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/152 ;状态:进行中;owner:weichen + +## 基线与授权 + +- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= f4aa0348d7f60e4dd5b6b222205a2c561465db8e +- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-agent-stack-r0-r1(worktree);分支:feature/agent-stack-r0-r1 +- 授权与交付终点:按提案第 5 节实施 R0(组合一致性)与 R1(三库 P0 与安全向 P1)。允许在 Agent-Intent、Agent-Native、Agent-Squad、Agent-System-Backend、Agent-System-Dev 五个仓各从 origin/main 切 feature/agent-stack-r0-r1,提交、推送并建 PR,推进 Dev 与 Backend 的 gitlink。终点 = 五个 PR 已开且均未合入。未授权:合入、发版、改写历史、变更分支保护、改 development-guidelines 嵌套指针。 +- 追加授权(2026-10-03,主检出 `make precheck` verdict READY,head=`f4aa0348d7`,linked worktrees=2):在同一批 `feature/agent-stack-r0-r1` 上补收尾加固与 CI。允许改 Squad 的并行审批与挂起、Dev 的指针检查、`make pin`、导入棘轮和 `task.sh`,五个仓的 CI workflow,以及任务卡欠账。R2 与 D1 等五个 PR 由所有者合入后再从各仓 main 开独立任务。未授权:合入、发版、改分支保护、配置 `SUBMODULE_TOKEN`。 + +## 目标与非目标 + +- 目标:Dev 测的就是发布的组合(go.work、指针一致性检查、库间依赖棘轮、库测试纳入 make test);修 I1/I2/I6、N1–N4/N13/N14、Q1–Q5/Q8/Q9/Q14–Q16,每条带回归测试。 +- 非目标:实现原生 Anthropic/Google 协议、合入 PR、发版、编排收敛到 Squad(后续批次)。 + +## 同步记录 + +| 节点 | sync_base_sha | tested_head_sha | checked_at | +|---|---|---|---| +| 启动 | f4aa0348d7f60e4dd5b6b222205a2c561465db8e | f4aa0348d7f60e4dd5b6b222205a2c561465db8e | 2026-10-03T00:27:10Z | +| 同步前验收 | f4aa0348d7f60e4dd5b6b222205a2c561465db8e | 978bf8bdaec0890e241849f0fb9e6999b0a1de86 | 2026-10-03T02:10:29Z | + +## 结果与验证 + +- 结果:五个 PR 已开,均未合入。合入顺序:Intent #3 https://git-home.u1680832.nyat.app:30319/weichen/Agent-Intent/pulls/3 、Native #8 https://git-home.u1680832.nyat.app:30319/weichen/Agent-Native/pulls/8 、Squad #7 https://git-home.u1680832.nyat.app:30319/weichen/Agent-Squad/pulls/7 、Backend #50 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Backend/pulls/50 、Dev #152 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/152 。指针:intent `3130190acb396589d6022c243bbe07ce7c31c881`,native `694b08cdbef5d52496f4425c9a22d1b77df14c23`,squad `8e987bf535efbbbf317147542e858217f2bb3504`,backend `898fdd137148836b2e52666a2b5e0d591ec308c3`。Dev 在 `978bf8b` 钉住这四条并加入指针检查、导入棘轮和三库测试。 +- go.work 未采用。实测工作区模块目录都落在 Dev 顶层 `deps/` 与 `modules/`,但最小版本选择把第三方抬高:`golang.org/x/crypto` v0.56.0→v0.57.0,`exp` `v0.0.0-20250305212735-054e65f0b394`→`v0.0.0-20251023183803-a4bb9ffd2546`,`net` v0.57.0→v0.59.0,`term` v0.45.0→v0.46.0,`text` v0.41.0→v0.42.0。库测试因此用 `GOWORK=off`。`development-guidelines` 嵌套指针仍是 `26b562d`,与 Dev 的 `ed2af3e` 不一致,`check-pins` 只告警。 +- 编号与回归测试: + - I1 `TestRecognizeClassifierRejectsBadRefinement`、`TestRecognizeClassifierRebuildsTamperedHandler` + - I2 `TestScopedSlotValidatorsDoNotInterfere` + - I6 `TestRecognizeClassifierRefinedTopGetsSlots` + - N1 `TestLoopBudgetToolCallCap`(含 `assertToolCallsPaired`) + - N2 `TestParallelHostCallbacksAreSerial`、`TestHooksDoNotRunConcurrently` + - N3 `TestStdioSlowCancelThenEcho` + - N4 `TestPresetsAndClientForChatCompletions` + - N13 `TestFetchResolveChangesToLoopbackRejected` + - N14 `TestFetchHostCheckRedirectStillRejectsPrivate` + - Q1 `TestStepApprovalApproveExecutes`、`TestStepApprovalSkipDoesNotCancel` + - Q2 `TestApprovalSurvivesRestart` + - Q3 `TestAtomicWriteLeavesNoTemp`、`TestAtomicWriteRenameFailureKeepsOriginal` + - Q4 `TestInterruptedRunningStepStaysHeld` + - Q5 `TestTwoEnginesOneRunIsBusy` + - Q8/Q9 `TestWakeupDoesNotApprovePermission`(原有 `TestWakeupRunDone`、`TestWakeupGuardMaxFires` 仍通过) + - Q14 `TestListenGuard`、`TestMissingTokenIsUnauthorized` + - Q15 `TestPathTraversalRejected` + - Q16 `TestOversizedBodyIs413` +- 验证: + - 启动 precheck(主检出,task-start 记录于 2026-10-03T00:27:10Z):verdict READY。head 与 origin/main 均为 `f4aa0348d7`。linked worktree 为 1(未合入的 `feature/agent-stack-blueprint`),未出现 `SHARED_WORKSPACE_BUSY` 或 `SYNC_REQUIRED`。 + - `make task-sync`:`Already up to date.` `[synced] sync_base=f4aa0348d7 tested_head=978bf8bdae checked_at=2026-10-03T02:10:29Z`。脚本按文件名把这一行写进了另一张任务卡,本提交已改回本卡。 + - `make test` 退出码 0(HEAD `314f798`,与 `978bf8b` 的代码相同):boundaries ok;pins ok(guidelines 三条 warn);intent 五包 ok;native 各包 ok(mcp 12.0s、tools 11.3s);squad 六包 ok;core ok;backend 各包 ok;frontend vitest 13 文件 68 项;ui tsc 与 vitest 11 文件 79 项;agent-command 两包 ok。 + - `make build` 退出码 0。版本串 `314f798+core=7c66075+backend=898fdd1+frontend=794c3e1+ui=b4a4fc1+agentcmd=cc1ad77+documents=acd6f01+native=694b08c+squad=8e987bf+intent=3130190`。 + - 负例:Backend 的 squad gitlink 临时改为 `5cbac00b994223b6aa76afd9bc70277b8b49e985` 后 `scripts/check-pins.sh` 退出码 1(`pin: modules/backend deps/agent-squad is 5cbac00b9942, Dev deps/agent-squad is 8e987bf535ef`)。已恢复到 `898fdd1`,再次运行 `pins ok`。该临时提交未推送。 +- 影响与剩余项:非 unix 平台没有 flock,只有进程内互斥(见 agent-squad PROJECT.md)。合入、发版、改写历史均未做。 +- 级联钉住与验收(2026-10-03,Dev `789a6f4`): + - 指针:intent `7f7879b06b053f0a62746309b79e0e1b9a00f42d`,native `91c155618487e21edeb4f1d3cc27e985aae2b0d2`,squad `e928ea3c0fa649cbbbad22a93a1bc0a12dee5f7d`,backend `ef0a1bef0f7bee626e59f160be51fb0e5f6aa692`。 + - `make task-sync`:`Already up to date.` `[synced] sync_base=f4aa0348d7 tested_head=755474e6f5 checked_at=2026-10-03T10:22:12Z`。记录写在本卡。 + - `make test` 退出码 0:`boundaries ok`;`pins ok`(guidelines 三条 warn);intent、native、squad、core、backend、frontend(13 文件 68 项)、ui(tsc 与 11 文件 79 项)、agent-command 均通过。 + - `make build` 退出码 0。版本串 `789a6f4+core=7c66075+backend=ef0a1be+frontend=794c3e1+ui=b4a4fc1+agentcmd=cc1ad77+documents=acd6f01+native=91c1556+squad=e928ea3+intent=7f7879b`。 + - 各库在钉住后的头上另跑了 `scripts/submodule-consistency.sh`(intent、native、squad 均为 ok)以及 vet、test、race。Backend 跑了 `GOWORK=off go vet ./... && GOWORK=off go test -count=1 ./...`,退出码 0。 + - CI 首轮未绿。Intent 运行 661(头 `7f7879b`)在 30 秒内失败:克隆 `actions/checkout` 时 `dial tcp 20.205.243.166:443: i/o timeout`。按计划停下,没有改走宿主代理,也没有预热缓存。`SUBMODULE_TOKEN` 与必需检查都未配置。 +- 收尾加固(2026-10-03): + - Squad `73b8d09545a8c479344aded2227de5fc520ef769`。并行组第二步 `TestParallelGroupSecondStepApprovalHolds`、`TestParallelGroupSecondStepPermissionHolds`;自由文本 `TestFreeTextKeepsChoiceHold`;失败后挂起仍在 `TestFailedDecisionRestoresPending`。`GOWORK=off go vet ./...`、`go test -count=1 ./squad/ ./cli/`、`go test -race -count=1 ./squad/` 均通过。 + - 同 tree 正例:Backend 所记 squad `8e987bf535efbbbf317147542e858217f2bb3504` 的 tree `0fe06ce3ceea5cfa32476e2f0eed7f86cebc44a0`,`commit-tree` 得本地 `6c0f1e8bf1be85f10163eb0c6eebb5a6aa8f8798` 并临时写入 Dev 索引。`check-pins` 输出 `ok modules/backend agent-squad (same tree)`,退出码 0。 + - 负例:空 tree 的本地提交 `8db069d7352d` 写入同一索引后,`check-pins` 退出码 1,`pin: modules/backend deps/agent-squad is 8e987bf535ef, Dev deps/agent-squad is 8db069d7352d`。索引已 `git reset` 恢复,再次 `pins ok`。两个临时对象都没有推送。 + - 棘轮:`scripts/lint-imports.go` 在空 `GOMODCACHE` 下的配对与有缓存的 `go list` 相同,14 对。`GOMODCACHE=$(mktemp -d) bash scripts/lint-boundaries.sh` 与默认缓存的输出都是 `boundaries ok`。 +| 同步前验收 | f4aa0348d7f60e4dd5b6b222205a2c561465db8e | 755474e6f5b4fb404c14d84437428d4886e5317f | 2026-10-03T10:22:12Z | diff --git a/Makefile b/Makefile index 2f1e7325..9178bb7d 100644 --- a/Makefile +++ b/Makefile @@ -10,10 +10,10 @@ 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) +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-commits svc-history svc-update test-backend test-frontend test-ui test-core test-agentcmd dev pin clean setup precheck task-start task-sync task-close task-finish version +.PHONY: help sync status build frontend svc svc-list svc-commits svc-history svc-update test-backend test-frontend test-ui test-core test-agentcmd test-pins test-libs dev pin clean setup precheck task-start task-sync task-close task-finish version version: @echo "agent-system $(VERSION)" @@ -44,7 +44,7 @@ task-finish: help: @echo "make sync 物化全部 submodule(递归)" @echo "make build 构建单二进制 bin/agent-system(契约同步→前端构建→embed→静态链接)" - @echo "make test 全量测试(core + backend + frontend)" + @echo "make test 全量测试(boundaries + pins + libs + core + backend + frontend + ui + agent-command)" @echo "make svc 服务级操作:make svc ENV= [ACTION=deploy|start|stop|restart|status|logs] [ARGS='--from dev@ --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-commits 列出可部署提交:make svc-commits SRC=dev|release [N=20]" @@ -99,11 +99,21 @@ test-agentcmd: 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 ./... + plugin-new: @test -n "$(NAME)" || (echo "用法:make plugin-new NAME="; exit 2) bash scripts/plugin-new.sh "$(NAME)" -test: test-boundaries test-core test-backend test-frontend test-ui test-agentcmd +test: 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 相互隔离。 @@ -139,9 +149,8 @@ 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 - @cd $(BACKEND)/deps/agent-system-core && git fetch -q origin && git checkout -q origin/main || true - @cd $(BACKEND)/deps/agent-command && git fetch -q origin && git checkout -q origin/main || true @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 diff --git a/PROJECT.md b/PROJECT.md index 7d6313fd..15102969 100644 --- a/PROJECT.md +++ b/PROJECT.md @@ -2,5 +2,5 @@ - 用途:开发集成父仓(gitlink 权威);非分发单元。 - 组合:modules/{core,backend,frontend,ui,documents} + deps/{development-guidelines,agent-native,agent-squad,agent-intent,agent-command,error-code-system,logging-system}。 -- 验证:make test(core+backend+frontend 全量);make build 产出 bin/agent-system。 +- 验证:`make test` = test-boundaries(公开边界,另含三库之间导入配对棘轮)+ test-pins + test-libs(agent-intent、agent-native、agent-squad 各自 `GOWORK=off` 的 vet 与 test)+ test-core + test-backend + test-frontend + test-ui + test-agentcmd。`make build` 在 backend 模块内编译,replace 指向该模块自己的 `deps/`;`scripts/check-pins.sh` 要求这些 gitlink 与 Dev 顶层指针相同。未加 go.work:工作区最小版本选择会把 `golang.org/x/crypto`、`exp`、`net`、`term`、`text` 选得比 backend 单独构建更新。Releases 仓的 `scripts/build-release.sh` 递归克隆本仓固定提交后运行 `make build`,因此跟随同一套指针。 - 部署:非容器;Releases 仓从本仓固定提交导出版本。 diff --git a/deps/agent-intent b/deps/agent-intent index 1dbbe814..7f7879b0 160000 --- a/deps/agent-intent +++ b/deps/agent-intent @@ -1 +1 @@ -Subproject commit 1dbbe8149c561b36282bf781e61a06d1284be3a1 +Subproject commit 7f7879b06b053f0a62746309b79e0e1b9a00f42d diff --git a/deps/agent-native b/deps/agent-native index c6d867cd..91c15561 160000 --- a/deps/agent-native +++ b/deps/agent-native @@ -1 +1 @@ -Subproject commit c6d867cde7d87696feaf3073f097ca15b0145273 +Subproject commit 91c155618487e21edeb4f1d3cc27e985aae2b0d2 diff --git a/deps/agent-squad b/deps/agent-squad index 590fd86f..e928ea3c 160000 --- a/deps/agent-squad +++ b/deps/agent-squad @@ -1 +1 @@ -Subproject commit 590fd86f4c8bfada0ec76efbfb57133c61607bd6 +Subproject commit e928ea3c0fa649cbbbad22a93a1bc0a12dee5f7d diff --git a/modules/backend b/modules/backend index 46d5f7b6..ef0a1bef 160000 --- a/modules/backend +++ b/modules/backend @@ -1 +1 @@ -Subproject commit 46d5f7b66830167ccf00b5d6d9cd0e73ad62e47d +Subproject commit ef0a1bef0f7bee626e59f160be51fb0e5f6aa692 diff --git a/policy/lib-deps-allowlist.txt b/policy/lib-deps-allowlist.txt new file mode 100644 index 00000000..e391def8 --- /dev/null +++ b/policy/lib-deps-allowlist.txt @@ -0,0 +1,16 @@ +# 三个库之间的包导入现状。新增配对会使 scripts/lint-boundaries.sh 失败; +# 已经消失的配对只告警,提示从本文件删除。每行:导入方包 被导入包。 +agent-native/adapters/intentgate agent-intent/intent +agent-squad/cli agent-intent/intent +agent-squad/cli agent-native/exec +agent-squad/cli agent-native/runtime/llm +agent-squad/platform agent-native/runtime +agent-squad/platform agent-native/runtime/llm +agent-squad/squad agent-native/adapters/native +agent-squad/squad agent-native/exec +agent-squad/squad agent-native/runtime/llm +agent-squad/squad agent-native/runtime/mcp +agent-squad/squad agent-native/runtime/subagent +agent-squad/squad agent-native/runtime/tools +agent-squad/squadintent agent-intent/intent +agent-squad/squadintent agent-native/exec diff --git a/scripts/check-pins.sh b/scripts/check-pins.sh new file mode 100755 index 00000000..219a017e --- /dev/null +++ b/scripts/check-pins.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash +# 组合指针一致:容器仓在 Dev 钉住的提交上,deps/ 里的每个 gitlink +# 必须等于 Dev 顶层对应指针。development-guidelines 只告警。 +set -euo pipefail +root=$(cd "$(dirname "$0")/.." && pwd) +cd "$root" + +fail=0 + +pin_of() { + git rev-parse ":$1" +} + +check_link() { + local container="$1" name="$2" sha="$3" expect_path="$4" + local expect + if ! expect=$(pin_of "$expect_path" 2>/dev/null); then + echo "pin: $container deps/$name has no Dev pin at $expect_path" >&2 + fail=1 + return + fi + if [ "$sha" != "$expect" ]; then + local sha_tree expect_tree + if ! sha_tree=$(git -C "$expect_path" rev-parse "${sha}^{tree}" 2>/dev/null); then + echo "pin: $container deps/$name ${sha:0:12} is missing locally; fetch $expect_path first" >&2 + fail=1 + return + fi + if ! expect_tree=$(git -C "$expect_path" rev-parse "${expect}^{tree}" 2>/dev/null); then + echo "pin: Dev $expect_path ${expect:0:12} is missing locally; fetch $expect_path first" >&2 + fail=1 + return + fi + if [ "$sha_tree" = "$expect_tree" ]; then + echo "ok $container $name (same tree)" + return + fi + echo "pin: $container deps/$name is ${sha:0:12}, Dev $expect_path is ${expect:0:12}" >&2 + fail=1 + return + fi + echo "ok $container $name" +} + +containers=(modules/backend deps/agent-native deps/agent-squad deps/agent-intent) +for container in "${containers[@]}"; do + commit=$(pin_of "$container") + while read -r mode _typ sha name; do + [ "$mode" = "160000" ] || continue + base=${name#deps/} + if [ "$base" = "development-guidelines" ]; then + dev=$(pin_of deps/development-guidelines) + if [ "$sha" != "$dev" ]; then + echo "warn: $container deps/development-guidelines is ${sha:0:12}, Dev is ${dev:0:12}" >&2 + fi + continue + fi + if [ "$base" = "agent-system-core" ]; then + check_link "$container" "$base" "$sha" modules/core + else + check_link "$container" "$base" "$sha" "deps/$base" + fi + done < <(git -C "$container" ls-tree "$commit" deps/) +done + +if [ "$fail" -ne 0 ]; then + exit 1 +fi +echo "pins ok" diff --git a/scripts/lint-boundaries.sh b/scripts/lint-boundaries.sh index e00d3e7b..ba35e59c 100755 --- a/scripts/lint-boundaries.sh +++ b/scripts/lint-boundaries.sh @@ -24,6 +24,49 @@ if [ -d "$sdk" ]; then fi fi check "$root/examples" + +# 库间依赖棘轮:三个库的包导入兄弟库的配对,相对白名单只许减少、不许新增。 +allow="$root/policy/lib-deps-allowlist.txt" +observed=$(mktemp) +recorded=$(mktemp) +trap 'rm -f "$observed" "$recorded"' EXIT +siblings=(agent-intent agent-native agent-squad) +for dir in deps/agent-intent deps/agent-native deps/agent-squad; do + mod=$(awk '/^module /{print $2; exit}' "$root/$dir/go.mod") + ( + cd "$root/scripts" + go run lint-imports.go "$root/$dir" + ) | while read -r importer rest; do + for imp in $rest; do + for sib in "${siblings[@]}"; do + case "$imp" in + "$sib"|"$sib"/*) ;; + *) continue ;; + esac + case "$importer" in + "$mod"|"$mod"/*) ;; + *) continue ;; + esac + if [ "$sib" != "$mod" ]; then + echo "$importer $imp" + fi + done + done + done +done | sort -u >"$observed" +grep -v -e '^#' -e '^$' "$allow" | sort -u >"$recorded" +new_pairs=$(comm -13 "$recorded" "$observed" || true) +if [ -n "$new_pairs" ]; then + echo "boundary: new sibling-library import pairs" >&2 + echo "$new_pairs" >&2 + fail=1 +fi +stale_pairs=$(comm -23 "$recorded" "$observed" || true) +if [ -n "$stale_pairs" ]; then + echo "warn: sibling-library pairs no longer imported; delete from policy/lib-deps-allowlist.txt" >&2 + echo "$stale_pairs" >&2 +fi + if [ "$fail" -ne 0 ]; then exit 1 fi diff --git a/scripts/lint-imports.go b/scripts/lint-imports.go new file mode 100644 index 00000000..488340eb --- /dev/null +++ b/scripts/lint-imports.go @@ -0,0 +1,99 @@ +// 离线列出模块内非测试 Go 文件的导入。 +// 只使用标准库,跳过 _test.go 和带 go.mod 的子目录,避免 go list 去下载模块图。 +package main + +import ( + "bufio" + "fmt" + "go/parser" + "go/token" + "os" + "path/filepath" + "strings" +) + +func main() { + for _, root := range os.Args[1:] { + mod := modulePath(filepath.Join(root, "go.mod")) + if mod == "" { + fmt.Fprintf(os.Stderr, "lint-imports: %s has no module path\n", root) + os.Exit(1) + } + err := filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error { + if err != nil || !d.IsDir() { + return nil + } + name := d.Name() + if name == "vendor" || (name != "." && strings.HasPrefix(name, ".")) { + return filepath.SkipDir + } + if path != root { + if _, statErr := os.Stat(filepath.Join(path, "go.mod")); statErr == nil { + return filepath.SkipDir + } + } + return printImports(root, mod, path) + }) + if err != nil { + fmt.Fprintf(os.Stderr, "lint-imports: %v\n", err) + os.Exit(1) + } + } +} + +func modulePath(goMod string) string { + f, err := os.Open(goMod) + if err != nil { + return "" + } + defer f.Close() + sc := bufio.NewScanner(f) + for sc.Scan() { + fields := strings.Fields(sc.Text()) + if len(fields) >= 2 && fields[0] == "module" { + return fields[1] + } + } + return "" +} + +func printImports(root, mod, dir string) error { + rel, err := filepath.Rel(root, dir) + if err != nil { + return err + } + importer := mod + if rel != "." { + importer = mod + "/" + filepath.ToSlash(rel) + } + entries, err := os.ReadDir(dir) + if err != nil { + return err + } + fset := token.NewFileSet() + seen := map[string]struct{}{} + var imports []string + for _, entry := range entries { + name := entry.Name() + if entry.IsDir() || !strings.HasSuffix(name, ".go") || strings.HasSuffix(name, "_test.go") { + continue + } + file, err := parser.ParseFile(fset, filepath.Join(dir, name), nil, parser.ImportsOnly) + if err != nil { + continue + } + for _, imp := range file.Imports { + path := strings.Trim(imp.Path.Value, `"`) + if _, ok := seen[path]; ok { + continue + } + seen[path] = struct{}{} + imports = append(imports, path) + } + } + if len(imports) == 0 { + return nil + } + fmt.Printf("%s %s\n", importer, strings.Join(imports, " ")) + return nil +} diff --git a/scripts/task.sh b/scripts/task.sh index 84c1617a..f9814795 100755 --- a/scripts/task.sh +++ b/scripts/task.sh @@ -42,6 +42,23 @@ cmd_precheck() { fi done [ -n "$open_cards" ] && echo "[cards] 已合入未闭合任务卡:$(echo $open_cards)——make task-close 回填归档(主检出运行)" + local done_open="" stale="" now ts ref + for pc in .task/*.md; do + [ -f "$pc" ] || continue + case "$(card_status "$pc")" in + 已完成*) done_open="${done_open} ${pc##*/}" ;; + esac + done + [ -n "$done_open" ] && echo "[cards] 已完成但未归档:$(echo $done_open)" + now=$(date +%s) + while read -r ref ts; do + [ -n "$ref" ] || continue + git merge-base --is-ancestor "$ref" "origin/${TARGET_BRANCH}" 2>/dev/null && continue + if [ $((now - ts)) -gt 86400 ]; then + stale="${stale} ${ref#origin/}" + fi + done < <(git for-each-ref --format='%(refname:short) %(committerdate:unix)' "refs/remotes/origin/feature/card-close-*") + [ -n "$stale" ] && echo "[cards] 闭合分支超过 24 小时未合入:$(echo $stale)" echo "[required-reading] core/baseline.md, workflows/delivery.md §1$([ "$dirty" -gt 0 ] && echo ", standards/collaboration.md(树脏=他人或未收尾现场,不得写入)")" echo "[R0] $(echo "$rmain" | cut -c1-10)(完整:$rmain)" if [ "$dirty" -gt 0 ] || [ "${sub_drift:-0}" -gt 0 ]; then @@ -119,13 +136,27 @@ cmd_sync() { [ "$(git status --porcelain | grep -v '^.. \.zcode/' | wc -l)" -eq 0 ] || die "dirty tree — 先显式提交;不隐式 stash(collaboration.md:11)" git merge --no-edit "origin/${TARGET_BRANCH}" git merge-base --is-ancestor "origin/${TARGET_BRANCH}" HEAD || die "sync incomplete" - local base head card + local base head card branch slug pc base=$(git rev-parse "origin/${TARGET_BRANCH}"); head=$(git rev-parse HEAD) - card=$(ls .task/*.md 2>/dev/null | head -1 || true) - if [ -n "$card" ]; then + branch=$(git rev-parse --abbrev-ref HEAD) + slug=$(echo "$branch" | tr '/' '-') + card=".task/${slug}.md" + if [ ! -f "$card" ]; then + card="" + for pc in .task/*.md; do + [ -f "$pc" ] || continue + if [ "$(card_of_branch "$pc")" = "$branch" ]; then + card=$pc + break + fi + done + fi + if [ -n "$card" ] && [ -f "$card" ]; then printf '| 同步前验收 | %s | %s | %s |\n' "$base" "$head" "$(date -u +%FT%TZ)" >> "$card" git add "$card" git commit -q -m "Record the sync checkpoint" || true + else + echo "[warn] 找不到分支 ${branch} 的任务卡,同步记录未写入" >&2 fi echo "[synced] sync_base=$(echo "$base" | cut -c1-10) tested_head=$(echo "$head" | cut -c1-10) checked_at=$(date -u +%FT%TZ)" echo "[next] 跑正式验收(make test 等),证据写入任务卡" @@ -189,13 +220,18 @@ cmd_close() { for c in .task/*.md; do [ -f "$c" ] || continue b=$(card_of_branch "$c"); [ -n "$b" ] || continue - st=$(card_status "$c"); case "$st" in 已完成*) continue ;; esac + st=$(card_status "$c") + done_unarchived=0 + case "$st" in 已完成*) done_unarchived=1 ;; esac if card_pending "${c##*/}"; then echo "[close] ${c##*/} 已有待合入的闭合分支,跳过(等其 PR 合入)" continue fi m=$(card_merge "$b") || m="" - [ -n "$m" ] || continue + if [ -z "$m" ]; then + [ "$done_unarchived" = 1 ] || continue + m="ARCHIVE" + fi cards="$cards $c|$b|$m" done @@ -213,6 +249,13 @@ $c|$b|$m" local n=0 sha subject pr closed_list="" while IFS='|' read -r c b m; do [ -n "$c" ] || continue + if [ "$m" = "ARCHIVE" ]; then + mkdir -p .task/archive + git mv "$c" ".task/archive/${c##*/}" + closed_list="$closed_list ${c##*/}(already-complete)" + n=$((n+1)) + continue + fi sha="${m%%|*}"; subject="${m#*|}" pr=$(printf '%s' "$subject" | sed -n 's/.*(#\([0-9]\+\)) from.*/\1/p') [ -n "$pr" ] || pr=unknown