From 6d35f911fe49a5dc3512f210fceb6c3fbc260e34 Mon Sep 17 00:00:00 2001 From: weichen Date: Thu, 22 Oct 2026 05:59:51 +0000 Subject: [PATCH 01/15] Record the followups task card (pin gate + M5) Why: carry authorization and unit plan for the audit follow-ups: pin-regression gate and native subagent Writable preflight (M5). Evidence: card only; no code change. Impact: task metadata only. Refs: feature/audit-followups-pin-gate-m5 --- .task/feature-audit-followups-pin-gate-m5.md | 152 +++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 .task/feature-audit-followups-pin-gate-m5.md diff --git a/.task/feature-audit-followups-pin-gate-m5.md b/.task/feature-audit-followups-pin-gate-m5.md new file mode 100644 index 00000000..12f71ed4 --- /dev/null +++ b/.task/feature-audit-followups-pin-gate-m5.md @@ -0,0 +1,152 @@ +# feature/audit-followups-pin-gate-m5 任务卡 + +任务主记录:;状态:进行中;owner:weichen + +## 基线与授权 + +- 权威仓:ssh://git@192.168.3.2:2222/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= b7636742fc9ea937e6030123afc608fd294861bf +- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-audit-followups-pin-gate-m5(worktree);分支:feature/audit-followups-pin-gate-m5 +- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false +- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=d2608b7436d1c2d773c607a79568c57cfaa2c32cebf35d1a0cabda9562a543c1;triage_receipt=/tmp/triage-followups-1792647600.json;receipt_sha256=01837cb87f83d50c255ee460dfa8e28ade7ed600624bea7d0c083b7653c8a5dd + +- 授权与交付终点:<本次允许的动作与终点;未授权事项写明> +- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进) +- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md) +- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入) +- 人工决策点:仅限目标/范围/验收变更、重大取舍、高后果操作或无法自动判定的业务验收 +- 当前阶段:需求拆解;当前增量:I1(待定义) +- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位) + +## 目标与非目标 + +<预期结果、受影响入口/模块/契约;明确不做什么。> + +## Agent 实施收据 + +路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。 + +| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 | +|---|---|---|---|---|---|---|---|---|---|---| +| 待定义 | — | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | — | + +## 关联方向与规划 + +- 适用状态:待核对(无适用规划时说明原因) +- 主记录路径/版本:待核对(路径按仓库根目录定位,归档后不变;只引用唯一正文) +- 对应里程碑:待核对 + +## 需求分析 + +| ID | 描述 | 项目符合度及依据 | 优先级 | 验收条件 | 依赖 | 结论 | +|---|---|---|---|---|---|---| +| R1 | 待填写 | 待评估 | 待定 | 待定义 | 待确认 | 待评审 | + +## 方案与计划 + +- 方案引用:待填写(含对象版本) +- 实施计划引用:待填写(含增量与需求 ID) +- 测试计划引用:待填写(含需求 ID 与验收条件) + +## 阶段记录 + +| 阶段 | 对象版本 | 状态 | 决策/执行者 | 时间 | 依据 | +|---|---|---|---|---|---| +| 需求拆解 | 待定 | 待执行 | — | — | — | +| 需求评估 | 待定 | 待执行 | — | — | — | +| 需求表确认 | 待定 | 待执行 | — | — | — | +| 方案设计 | 待定 | 待执行 | — | — | — | +| 实施计划 | 待定 | 待执行 | — | — | — | +| 实施与自检 | 待定 | 待执行 | — | — | — | +| 正式测试与验收 | 待定 | 待执行 | — | — | — | +| 代码 Review | 待定 | 待执行 | — | — | — | +| 合入 | 待定 | 待执行 | — | — | — | +| 合入核验与收口 | 待定 | 待执行 | — | — | — | + +## 探索试验与迭代 + +| 增量/试验 | 假设 | 范围 | 判断和停止条件 | 授权/决策依据 | 结果 | 变更影响 | 需重审补测 | +|---|---|---|---|---|---|---|---| +| 待定义 | — | — | — | 待核对 | NOT_RUN | 待评估 | 待评估 | + +## 同步记录 + +| 节点 | sync_base_sha | tested_head_sha | checked_at | +|---|---|---|---| +| 启动 | b7636742fc9ea937e6030123afc608fd294861bf | b7636742fc9ea937e6030123afc608fd294861bf | 2026-10-22T05:41:15Z | + +## 证据(Evidence) + + +| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) | +|---|---|---|---|---|---| + +## 交付调度 + +- 调度状态:未入队;调度记录:无 +- entry_id:无;attempt_id:无;窗口执行者:无 +- 固定候选:无(base/head、组件与相关配置引用实际尝试) +- 调度依据:policy/delivery.md#合入调度 +- 固定后证据:源码外 RECEIPT;实际结果后按授权原样导入和闭合 + +## 结果与验证(完成后填写) + +- 结果: +- 验证:<结论摘要 PASS/FAIL/SKIP/NOT_RUN 逐项;逐条证据由 make evidence 写入上方证据表> +- 影响与剩余项: + +## 规划进展与下一步 + +- 本次推进/剩余:待核对(依据实际成果更新适用方案/规划) +- 首选下一任务:待核对(无适用规划、已完成或受阻时如实说明,不虚构任务) +- 推荐依据:待填写 +- 前置条件/待确认:待核对 +- 预期产物/验收条件:待定义 +- 重复任务核对:待核对(已有/在途任务、PR 与规划记录) +- 启动授权:未授权(建议不自动启动新任务,仍须明确授权) + +## 合入与清理 + +- Review 记录:NOT_RUN(审阅智能体/平台身份、base/head、结论、问题闭合及重放依据) +- 冲突记录:NOT_RUN(最新目标同步、冲突处置、组合兼容核验及复审/补测) +- CI 记录:NOT_RUN(当前 HEAD 完整 SHA、全部必需检查、运行链接、执行身份及 GATE 结果) +- 合入记录:NOT_RUN(交付授权范围、PR、被核验 HEAD、合入 SHA、目标分支及内容核验) +- 清理记录:NOT_RUN(授权依据、写者退出确认、worktree/分支/任务卡处置) + + +```json +{ + "summary_schema": 1, + "source": "SELF", + "timestamp": "2026-10-22T05:40:13.195742+00:00", + "command": "make precheck", + "exit_code": 0, + "cwd": "/home/weichen/projects/Agent-System-Dev", + "before_head": "b7636742fc9ea937e6030123afc608fd294861bf", + "after_head": "b7636742fc9ea937e6030123afc608fd294861bf", + "branch": "main", + "target_branch": "main", + "target_head": "b7636742fc9ea937e6030123afc608fd294861bf", + "root_target": "main", + "immediate_target": "main", + "before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "output_tail": [ + "make[1]: Entering directory '/home/weichen/projects/Agent-System-Dev'", + "[repo] branch=main head=b7636742fc", + "[remote] origin/main=b7636742fc ahead=0 behind=0", + "[tree] clean submodules_drift=0 uninit=0", + "[worktrees] linked=5", + "[initiatives] local cards (not authoritative): none", + "[hooks] core.hooksPath=scripts/hooks(pre-commit/post-merge 实际接线)", + "[facts] policy/facts.md 6 条事实均在有效期内", + "[required-reading] core/baseline.md, workflows/delivery.md §1, policy/delivery.md", + "[R0] b7636742fc(完整:b7636742fc9ea937e6030123afc608fd294861bf)", + "[worktree] before-sync: recursive component checks passed", + "[verdict] READY — 保留本次 precheck receipt 后建任务工作区:PRECHECK_RECEIPT=/tmp/task-precheck.json make task-start NAME=feature/your-branch", + "make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev'" + ], + "output_sha256": "750b4668efa152b0a18152e950ab40051a737f940cf5e108e6adc79324dea82a", + "receipt_sha256": "e2cd793c34846aafbd4c3dd5a77d8bc08fb51323ded1082b212c91e78074bad7", + "receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/e2cd793c34846aafbd4c3dd5a77d8bc08fb51323ded1082b212c91e78074bad7.json" +} +``` -- 2.54.0 From 3080c0f4af549fd4bcd5acde246e2aa8f7c2e9a5 Mon Sep 17 00:00:00 2001 From: weichen Date: Thu, 22 Oct 2026 06:14:23 +0000 Subject: [PATCH 02/15] Bump the agent-native pin to the M5 subagent fix Why: land the review-M5 preflight fix (delegate_task under a Writable write-set is now rejected like exec/MCP/host tools). Evidence: transport success (gitlinkm5c-run.json); native unit tests green (verify-m5.json); gate/pins checks re-run with the pin bump. Impact: one component gitlink. Refs: feature/audit-followups-pin-gate-m5 --- deps/agent-native | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/agent-native b/deps/agent-native index cff69355..b5b9f17d 160000 --- a/deps/agent-native +++ b/deps/agent-native @@ -1 +1 @@ -Subproject commit cff69355f757a8057e63ce710cc234d632692d50 +Subproject commit b5b9f17dc78fe3445e0ea155c5c845480554b5e5 -- 2.54.0 From ba1d3b2b8122335f1861b2823f68ff8ffdd07b9e Mon Sep 17 00:00:00 2001 From: weichen Date: Thu, 22 Oct 2026 06:16:13 +0000 Subject: [PATCH 03/15] Add the pin-regression gate to pins checks and the PR gate Why: PR #682 merged a stale branch that reset five component pins to pre-batch values, silently dropping the batch-1 composition on main. Evidence: materialized byte-exact from the reviewed transport (gatemat-run.json); regression/forward/same/skip/missing-object cases (verify-pingate.json); make test-governance and test-pins re-run below. Impact: new check script wired into check-pins and task-pr-check; PIN_REGRESSION_SKIP=1 documents deliberate downgrades. Refs: feature/audit-followups-pin-gate-m5 --- scripts/check-pins.sh | 2 + scripts/check_pin_regression.sh | 100 +++++++++++++++ scripts/task_pr_check.py | 17 ++- tests/test_check_pin_regression.py | 187 +++++++++++++++++++++++++++++ tests/test_check_pins.py | 17 +++ 5 files changed, 322 insertions(+), 1 deletion(-) create mode 100755 scripts/check_pin_regression.sh create mode 100644 tests/test_check_pin_regression.py diff --git a/scripts/check-pins.sh b/scripts/check-pins.sh index cab88a2b..ac986a47 100755 --- a/scripts/check-pins.sh +++ b/scripts/check-pins.sh @@ -64,6 +64,8 @@ for container in "${containers[@]}"; do done < <(git -C "$container" ls-tree "$commit" deps/) done +bash "$root/scripts/check_pin_regression.sh" || fail=1 + if [ "$fail" -ne 0 ]; then exit 1 fi diff --git a/scripts/check_pin_regression.sh b/scripts/check_pin_regression.sh new file mode 100755 index 00000000..41782269 --- /dev/null +++ b/scripts/check_pin_regression.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env bash +# 组合指针不倒退:HEAD 中每个 gitlink(deps/*、modules/*)相对 origin/main +# 只能前进——新 pin 必须以 origin/main 的旧 pin 为祖先。事故原型:PR #682 +# 合入了过期分支,其树把五个组件 pin 重置回批次前的值,静默丢掉了 main 上 +# batch-1 的组合。 +# +# ┌─ 逃生口(须 owner Review)──────────────────────────────────────────────┐ +# │ PIN_REGRESSION_SKIP=1 声明这是一次有意的降级:失败改为醒目的 warn 并 │ +# │ 退出 0。只用于经组件 owner 审阅的刻意回退,PR │ +# │ 描述须写明原因;不得用来绕过过期分支。 │ +# └─────────────────────────────────────────────────────────────────────────┘ +# +# 其他开关: +# PIN_REGRESSION_NO_FETCH=1 旧 pin 在子仓本地缺失时不尝试 fetch(测试/离线用)。 +# +# 在当前工作目录所属仓库的顶层运行。origin/main 本地不可解析(浅检出)时只告警、退出 0。 +set -euo pipefail +root=$(git rev-parse --show-toplevel) +cd "$root" + +base=origin/main +skip=${PIN_REGRESSION_SKIP:-0} + +if ! git rev-parse --verify -q "$base^{commit}" >/dev/null; then + echo "warn: $base is not resolvable locally; pin regression check skipped" >&2 + exit 0 +fi + +fail=0 + +declare -A main_pins=() +while read -r mode _typ sha path; do + [ "$mode" = "160000" ] || continue + main_pins["$path"]=$sha +done < <(git ls-tree -r "$base") + +# 子仓须是独立检出的仓库;未初始化时 git -C 会落到 Dev 自身。 +own_repo() { + local path="$1" top + top=$(git -C "$path" rev-parse --show-toplevel 2>/dev/null) || return 1 + [ "$top" = "$root/$path" ] +} + +has_commit() { + git -C "$1" cat-file -e "$2^{commit}" 2>/dev/null +} + +ensure_commit() { + local path="$1" sha="$2" + has_commit "$path" "$sha" && return 0 + [ "${PIN_REGRESSION_NO_FETCH:-0}" = "1" ] && return 1 + git -C "$path" fetch -q origin "$sha" >/dev/null 2>&1 || true + has_commit "$path" "$sha" +} + +regress() { + if [ "$skip" = "1" ]; then + echo "warn: PIN_REGRESSION_SKIP=1 — $* (deliberate downgrade; requires owner review)" >&2 + else + echo "pin: $*" >&2 + fail=1 + fi +} + +while read -r mode _typ new path; do + [ "$mode" = "160000" ] || continue + old=${main_pins["$path"]:-} + if [ -z "$old" ]; then + echo "ok $path (new, not on $base)" + continue + fi + if [ "$old" = "$new" ]; then + echo "ok $path" + continue + fi + if ! own_repo "$path"; then + regress "$path is not checked out; cannot verify ${new:0:12} descends from $base ${old:0:12}" + continue + fi + if ! ensure_commit "$path" "$old"; then + regress "$path $base pin $old is missing locally and could not be fetched; cannot verify no regression" + continue + fi + if ! ensure_commit "$path" "$new"; then + regress "$path HEAD pin $new is missing locally and could not be fetched; cannot verify no regression" + continue + fi + if git -C "$path" merge-base --is-ancestor "$old" "$new"; then + echo "ok $path (forward ${old:0:12}..${new:0:12})" + continue + fi + regress "$path moves BACKWARD: $base is ${old:0:12}, HEAD is ${new:0:12} (not a descendant)." \ +"This is the PR #682 pattern (a stale branch resetting pins over main): merge $base and re-pin," \ +"or set PIN_REGRESSION_SKIP=1 for an owner-reviewed deliberate downgrade" +done < <(git ls-tree -r HEAD) + +if [ "$fail" -ne 0 ]; then + exit 1 +fi +echo "pin regression ok" diff --git a/scripts/task_pr_check.py b/scripts/task_pr_check.py index 19b6d92d..3ede2d2b 100644 --- a/scripts/task_pr_check.py +++ b/scripts/task_pr_check.py @@ -13,6 +13,8 @@ 不联网、不需要 token;非 pull_request 事件跳过(退出 0)。 期望目标:child → immediate_target;其余(initiative / standalone / 无卡)→ root_target(无卡即 main)。 +local 与 ci 均运行 check_pin_regression.sh(gitlink 相对 origin/main 不得倒退);非零即拒绝, +PIN_REGRESSION_SKIP=1 由该脚本降级为告警(有意降级须 owner Review)。 退出码:0 通过;1 门禁拒绝(PR_CHECK_FAILED);2 用法;6 远端无法核验(REMOTE_UNVERIFIED,fail closed)。 仅使用标准库,复用 task_triage 的 Gitea 客户端与 token 读取。token 不打印、不写入任何产物。 @@ -33,6 +35,7 @@ EXIT_USAGE = 2 PRE_MERGE_CLOSED = "已完成(合入前闭合)" CHILD_COLUMNS = ("子任务分支", "必需", "状态", "PR") DONE_STATES = ("已合入", "已延期") +PIN_REGRESSION = Path(__file__).resolve().parent / "check_pin_regression.sh" def sh(*args): @@ -216,6 +219,17 @@ def final_problems(api, slug, token, branch, meta, text): return problems +def pin_regression_problems(): + proc = subprocess.run(["bash", str(PIN_REGRESSION)], capture_output=True, text=True, + encoding="utf-8", errors="replace") + for line in (proc.stdout + proc.stderr).splitlines(): + print(f"[pin-regression] {line}") + if proc.returncode != 0: + return [f"pin regression gate failed (exit {proc.returncode}): a gitlink moves backward against " + "origin/main or cannot be verified; PIN_REGRESSION_SKIP=1 only for owner-reviewed downgrades"] + return [] + + def verdict(problems, kind, base, head): if problems: for problem in problems: @@ -264,6 +278,7 @@ def cmd_local(args): problems.append(f"parent initiative {expected} has no open PR into {meta['root_target']}") if args.final and kind == "initiative": problems.extend(final_problems(api, slug, token, branch, meta, text)) + problems.extend(pin_regression_problems()) return verdict(problems, kind, expected, head) @@ -281,7 +296,7 @@ def cmd_ci(_args): if base != expected: raise Stop(EXIT_FAILED, f"PR base {base!r} differs from expected {expected!r} " f"for {meta['kind']} branch {head}") - return verdict([], meta["kind"], base, head) + return verdict(pin_regression_problems(), meta["kind"], base, head) def main(argv=None): diff --git a/tests/test_check_pin_regression.py b/tests/test_check_pin_regression.py new file mode 100644 index 00000000..15f066ad --- /dev/null +++ b/tests/test_check_pin_regression.py @@ -0,0 +1,187 @@ +"""check_pin_regression.sh:gitlink 相对 origin/main 只能前进(PR #682 事故形态)。 + +全部离线:临时目录内的普通 Git 仓库充当子仓,origin 只指向本地 bare 仓库。 +""" +import os +import shutil +import subprocess +import sys +import tempfile +import unittest +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +SCRIPT = ROOT / "scripts" / "check_pin_regression.sh" +PR_CHECK = ROOT / "scripts" / "task_pr_check.py" +COMP = "deps/comp" + + +class PinRegressionTests(unittest.TestCase): + def setUp(self): + self.tmp = Path(tempfile.mkdtemp()) + self.addCleanup(shutil.rmtree, self.tmp, ignore_errors=True) + cfg = self.tmp / "gitconfig" + cfg.write_text("") + self.env = dict( + os.environ, + GIT_CONFIG_GLOBAL=str(cfg), + GIT_CONFIG_NOSYSTEM="1", + GIT_AUTHOR_NAME="t", + GIT_AUTHOR_EMAIL="t@example.invalid", + GIT_COMMITTER_NAME="t", + GIT_COMMITTER_EMAIL="t@example.invalid", + ) + for key in ("PIN_REGRESSION_SKIP", "PIN_REGRESSION_NO_FETCH"): + self.env.pop(key, None) + self.dev = self.tmp / "dev" + self.dev.mkdir() + self.git(self.dev, "init", "-q") + # 子仓:A ← B,另有从 A 分叉的 C。 + self.comp = self.dev / COMP + self.comp.mkdir(parents=True) + self.git(self.comp, "init", "-q") + self.a = self.commit(self.comp, "a") + self.b = self.commit(self.comp, "b") + self.git(self.comp, "checkout", "-q", "-b", "side", self.a) + self.c = self.commit(self.comp, "c") + + def git(self, cwd, *args): + return subprocess.run( + ["git", *args], cwd=cwd, env=self.env, check=True, capture_output=True, text=True + ).stdout.strip() + + def commit(self, repo, text): + (repo / "f").write_text(text) + self.git(repo, "add", "f") + self.git(repo, "commit", "-qm", text) + return self.git(repo, "rev-parse", "HEAD") + + def pin(self, sha, path=COMP): + self.git(self.dev, "update-index", "--add", "--cacheinfo", f"160000,{sha},{path}") + self.git(self.dev, "commit", "-qm", f"pin {path} {sha[:7]}") + return self.git(self.dev, "rev-parse", "HEAD") + + def compose(self, main_pin, head_pin): + """origin/main 钉 main_pin,HEAD(PR 分支)钉 head_pin。""" + main = self.pin(main_pin) + self.git(self.dev, "update-ref", "refs/remotes/origin/main", main) + if head_pin != main_pin: + self.pin(head_pin) + + def run_check(self, **extra): + return subprocess.run( + ["bash", str(SCRIPT)], cwd=self.dev, env=self.env | extra, capture_output=True, text=True + ) + + def test_same_pin_passes(self): + self.compose(self.a, self.a) + res = self.run_check() + self.assertEqual(res.returncode, 0, res.stderr) + self.assertIn(f"ok {COMP}\n", res.stdout) + self.assertIn("pin regression ok", res.stdout) + + def test_forward_pin_passes(self): + self.compose(self.a, self.b) + res = self.run_check() + self.assertEqual(res.returncode, 0, res.stderr) + self.assertIn(f"ok {COMP} (forward", res.stdout) + + def test_pr682_stale_branch_carrying_old_pin_fails(self): + self.compose(self.b, self.a) + res = self.run_check() + self.assertEqual(res.returncode, 1, res.stdout) + self.assertIn(f"pin: {COMP} moves BACKWARD", res.stderr) + self.assertIn("PR #682", res.stderr) + self.assertIn("PIN_REGRESSION_SKIP=1", res.stderr) + self.assertNotIn("pin regression ok", res.stdout) + + def test_diverged_pin_fails(self): + self.compose(self.b, self.c) + res = self.run_check() + self.assertEqual(res.returncode, 1, res.stdout) + self.assertIn(f"pin: {COMP} moves BACKWARD", res.stderr) + + def test_skip_env_downgrades_to_warning(self): + self.compose(self.b, self.a) + res = self.run_check(PIN_REGRESSION_SKIP="1") + self.assertEqual(res.returncode, 0, res.stderr) + self.assertIn("warn: PIN_REGRESSION_SKIP=1", res.stderr) + self.assertIn("requires owner review", res.stderr) + + def test_missing_origin_main_warns_and_passes(self): + self.pin(self.a) + res = self.run_check() + self.assertEqual(res.returncode, 0, res.stderr) + self.assertIn("warn: origin/main is not resolvable locally", res.stderr) + + def test_new_component_not_on_main_passes(self): + self.compose(self.a, self.a) + other = self.dev / "deps" / "other" + other.mkdir() + self.git(other, "init", "-q") + self.pin(self.commit(other, "o"), "deps/other") + res = self.run_check() + self.assertEqual(res.returncode, 0, res.stderr) + self.assertIn("ok deps/other (new, not on origin/main)", res.stdout) + + def test_missing_old_object_with_fetch_disabled_fails(self): + self.compose("1" * 40, self.a) + res = self.run_check(PIN_REGRESSION_NO_FETCH="1") + self.assertEqual(res.returncode, 1, res.stdout) + self.assertIn(f"pin: {COMP} origin/main pin {'1' * 40} is missing locally", res.stderr) + + def test_missing_old_object_unfetchable_from_local_origin_fails(self): + bare = self.tmp / "comp-origin.git" + self.git(self.tmp, "init", "-q", "--bare", str(bare)) + self.git(self.comp, "remote", "add", "origin", str(bare)) + self.compose("2" * 40, self.a) + res = self.run_check() + self.assertEqual(res.returncode, 1, res.stdout) + self.assertIn("could not be fetched", res.stderr) + + def test_missing_old_object_is_fetched_from_local_origin(self): + upstream = self.tmp / "upstream" + self.git(self.tmp, "clone", "-q", str(self.comp), str(upstream)) + newer = self.commit(upstream, "z") + bare = self.tmp / "comp-origin.git" + self.git(self.tmp, "clone", "-q", "--bare", str(upstream), str(bare)) + self.git(self.comp, "remote", "add", "origin", str(bare)) + self.compose(newer, self.b) + res = self.run_check() + self.assertEqual(res.returncode, 1, res.stdout) + self.assertNotIn("could not be fetched", res.stderr) + self.assertIn(f"pin: {COMP} moves BACKWARD", res.stderr) + self.git(self.comp, "cat-file", "-e", f"{newer}^{{commit}}") + + def test_uninitialized_component_cannot_be_verified(self): + self.compose(self.b, self.a) + shutil.rmtree(self.comp) + self.comp.mkdir() + res = self.run_check() + self.assertEqual(res.returncode, 1, res.stdout) + self.assertIn(f"pin: {COMP} is not checked out", res.stderr) + + def run_pr_check_ci(self, **extra): + env = self.env | {"GITHUB_EVENT_NAME": "pull_request", "GITHUB_BASE_REF": "main", + "GITHUB_HEAD_REF": "feature/stale", "TASK_TARGET_BRANCH": "main"} | extra + return subprocess.run([sys.executable, str(PR_CHECK), "ci"], cwd=self.dev, env=env, + capture_output=True, text=True, timeout=60) + + def test_task_pr_check_ci_hard_fails_on_regression(self): + self.compose(self.b, self.a) + res = self.run_pr_check_ci() + self.assertEqual(res.returncode, 1, res.stdout + res.stderr) + self.assertIn(f"[pin-regression] pin: {COMP} moves BACKWARD", res.stdout) + self.assertIn("[pr-check] FAIL pin regression gate failed", res.stdout) + self.assertIn("[verdict] PR_CHECK_FAILED", res.stdout) + + def test_task_pr_check_ci_skip_env_passes(self): + self.compose(self.b, self.a) + res = self.run_pr_check_ci(PIN_REGRESSION_SKIP="1") + self.assertEqual(res.returncode, 0, res.stdout + res.stderr) + self.assertIn("[pin-regression] warn: PIN_REGRESSION_SKIP=1", res.stdout) + self.assertIn("[verdict] PR_CHECK_OK", res.stdout) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_check_pins.py b/tests/test_check_pins.py index 009281ae..d8099781 100644 --- a/tests/test_check_pins.py +++ b/tests/test_check_pins.py @@ -8,6 +8,7 @@ from pathlib import Path ROOT = Path(__file__).resolve().parents[1] SCRIPT = ROOT / "scripts" / "check-pins.sh" +REGRESSION = ROOT / "scripts" / "check_pin_regression.sh" CONTAINERS = ("modules/backend", "deps/agent-native", "deps/agent-squad", "deps/agent-intent") @@ -29,6 +30,7 @@ class CheckPinsTests(unittest.TestCase): self.dev = self.tmp / "dev" (self.dev / "scripts").mkdir(parents=True) shutil.copy(SCRIPT, self.dev / "scripts" / "check-pins.sh") + shutil.copy(REGRESSION, self.dev / "scripts" / "check_pin_regression.sh") self.git(self.dev, "init", "-q") def git(self, cwd, *args): @@ -112,6 +114,21 @@ class CheckPinsTests(unittest.TestCase): self.assertEqual(res.returncode, 1) self.assertIn("has no Dev pin", res.stderr) + def test_pin_regression_against_origin_main_fails(self): + old, new = self.repo("deps/error-code-system", "e1", "e2") + self.build({"error-code-system": old}, {"deps/error-code-system": old}) + self.git(self.dev, "commit", "-qm", "pr") + stale = self.git(self.dev, "rev-parse", "HEAD") + self.link(self.dev, "deps/error-code-system", new) + self.git(self.dev, "commit", "-qm", "main") + self.git(self.dev, "update-ref", "refs/remotes/origin/main", "HEAD") + self.git(self.dev, "reset", "-q", stale) + res = self.run_pins() + self.assertEqual(res.returncode, 1, res.stderr) + self.assertIn("ok modules/backend error-code-system", res.stdout) + self.assertIn("pin: deps/error-code-system moves BACKWARD", res.stderr) + self.assertNotIn("pins ok", res.stdout) + if __name__ == "__main__": unittest.main() -- 2.54.0 From cb767f603559b3194e27a107875b45dfe1b829cf Mon Sep 17 00:00:00 2001 From: weichen Date: Thu, 22 Oct 2026 06:25:14 +0000 Subject: [PATCH 04/15] Advance native, squad and backend pins for the followups Why: native carries the M5 subagent preflight fix; backend adopts its branch tip that merges upstream main (scratch sessions) on top of the batch-2 line. Evidence: integration transport success (gitlinkf5-run.json); nested native pins follow in the next commit; pins checks re-run below. Impact: two component gitlinks. Refs: feature/audit-followups-pin-gate-m5 --- deps/agent-native | 2 +- modules/backend | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deps/agent-native b/deps/agent-native index b5b9f17d..f2fb9ffa 160000 --- a/deps/agent-native +++ b/deps/agent-native @@ -1 +1 @@ -Subproject commit b5b9f17dc78fe3445e0ea155c5c845480554b5e5 +Subproject commit f2fb9ffa6df69abf4723a162550f85faff9d7fa3 diff --git a/modules/backend b/modules/backend index 9b405957..443e7af1 160000 --- a/modules/backend +++ b/modules/backend @@ -1 +1 @@ -Subproject commit 9b4059572818a5576d0ec1257d2bcf735971e0a2 +Subproject commit 443e7af1b8cb2cb6b08dd560d6dfd0e420f6c236 -- 2.54.0 From ecfa244c0af7bd6e745daa9da98cca77e7132a6d Mon Sep 17 00:00:00 2001 From: weichen Date: Thu, 22 Oct 2026 06:34:08 +0000 Subject: [PATCH 05/15] Repin modules/backend to the nested-M5 tip Why: the backend branch advanced past its nested agent-native bump so the composed chain carries the M5 preflight end to end. Evidence: integration transport success (gitlinkb2h-run.json); pins checks and full make test re-run below. Impact: one component gitlink. Refs: feature/audit-followups-pin-gate-m5 --- modules/backend | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/backend b/modules/backend index 443e7af1..3ab7cd71 160000 --- a/modules/backend +++ b/modules/backend @@ -1 +1 @@ -Subproject commit 443e7af1b8cb2cb6b08dd560d6dfd0e420f6c236 +Subproject commit 3ab7cd71a4f1b39702e493e2ad8c625109f584a7 -- 2.54.0 From a39b4f795c756947477194fb81ee25c54690edd7 Mon Sep 17 00:00:00 2001 From: weichen Date: Thu, 22 Oct 2026 06:47:42 +0000 Subject: [PATCH 06/15] Advance squad and backend pins for the followups Why: the squad container carries its nested agent-native M5 bump; the backend container is already at its repaired tip in this history. Evidence: integration transport (gitlinks3-run.json); pins checks and full make test re-run below. Impact: one component gitlink. Refs: feature/audit-followups-pin-gate-m5 --- deps/agent-squad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/agent-squad b/deps/agent-squad index 9edb6ba8..b6d87638 160000 --- a/deps/agent-squad +++ b/deps/agent-squad @@ -1 +1 @@ -Subproject commit 9edb6ba86574a6acab8a3b032dd359a0c2b6c7ba +Subproject commit b6d87638cc794b91e6f7e6fd036a8246840988a3 -- 2.54.0 From a23a212015719e239e3b3e0b0a5364755e30ba2c Mon Sep 17 00:00:00 2001 From: weichen Date: Thu, 22 Oct 2026 06:52:30 +0000 Subject: [PATCH 07/15] Repin modules/backend to the nested-squad followups tip Why: backend's nested agent-squad advances with its nested agent-native M5 bump, keeping every container consistent with the Dev-level pins. Evidence: integration transport (gitlinkb3b-run.json); pins checks and full make test re-run below. Impact: one component gitlink. Refs: feature/audit-followups-pin-gate-m5 --- modules/backend | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/backend b/modules/backend index 3ab7cd71..92c2b4eb 160000 --- a/modules/backend +++ b/modules/backend @@ -1 +1 @@ -Subproject commit 3ab7cd71a4f1b39702e493e2ad8c625109f584a7 +Subproject commit 92c2b4eb122afc513eb1676d138d61874f105673 -- 2.54.0 From 17ba4eab35062832cfaf3a3d42fef4935c8424e0 Mon Sep 17 00:00:00 2001 From: weichen Date: Thu, 22 Oct 2026 07:09:14 +0000 Subject: [PATCH 08/15] Record the followups verification results in the task card Why: pins checks green with the new gate; all other combination targets pass; the single governance failure reproduces on clean main. Evidence: verify-followups-{pins5,rest}.json exit=0; main reproduction captured in-session. Impact: task metadata only. Refs: feature/audit-followups-pin-gate-m5 --- .task/feature-audit-followups-pin-gate-m5.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.task/feature-audit-followups-pin-gate-m5.md b/.task/feature-audit-followups-pin-gate-m5.md index 12f71ed4..a6c4d54a 100644 --- a/.task/feature-audit-followups-pin-gate-m5.md +++ b/.task/feature-audit-followups-pin-gate-m5.md @@ -106,7 +106,8 @@ ## 合入与清理 -- Review 记录:NOT_RUN(审阅智能体/平台身份、base/head、结论、问题闭合及重放依据) +- Review 记录:待执行(独立 CLI 评审,随 PR 流程) +- 已知既有失败:make test-governance 中 test_svc.SvcDbRecoveryTests.test_addr_change_keeps_database_and_auth 在干净 main(b7636742)同样失败(环境依赖),与本分支无关;CI 环境为最终裁决。其余组合目标(boundaries/pins/libs/core/backend/frontend/ui/agentcmd)全部 PASS(verify-followups-rest.json exit=0) - 冲突记录:NOT_RUN(最新目标同步、冲突处置、组合兼容核验及复审/补测) - CI 记录:NOT_RUN(当前 HEAD 完整 SHA、全部必需检查、运行链接、执行身份及 GATE 结果) - 合入记录:NOT_RUN(交付授权范围、PR、被核验 HEAD、合入 SHA、目标分支及内容核验) -- 2.54.0 From b2f8c04b151cfe736ea994058d732fccf10cfb12 Mon Sep 17 00:00:00 2001 From: weichen Date: Thu, 22 Oct 2026 07:18:08 +0000 Subject: [PATCH 09/15] Repin modules/backend to the merged followups tip Why: backend adopts upstream main (role-policy/HITL fixes) and the parallel scratch-session work while keeping the M5 nested pin, so the followups composition stays current with main. Evidence: integration transport (gitlinkb7-run.json); pins checks and combination targets re-run below. Impact: one component gitlink. Refs: feature/audit-followups-pin-gate-m5 --- modules/backend | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/backend b/modules/backend index 92c2b4eb..a75783fd 160000 --- a/modules/backend +++ b/modules/backend @@ -1 +1 @@ -Subproject commit 92c2b4eb122afc513eb1676d138d61874f105673 +Subproject commit a75783fd2d6e8cd72e99468b358789d84c4d06d7 -- 2.54.0 From a2b4cf73a3bc7c974e77f1d35218466c42b7400d Mon Sep 17 00:00:00 2001 From: weichen Date: Thu, 22 Oct 2026 07:33:47 +0000 Subject: [PATCH 10/15] Record the merged baseline and the pre-existing governance failure Why: the branch now carries authoritative main (backend role-policy and scratch-session work, frontend/ui pin advances); the only combination failure is the environment-dependent svc test that reproduces on clean main. Evidence: verify-f-pins9.json exit=0; verify-f-rest.json exit=0; verify-f-combo.json shows the single pre-existing failure. Impact: task metadata only. Refs: feature/audit-followups-pin-gate-m5 --- .task/feature-audit-followups-pin-gate-m5.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.task/feature-audit-followups-pin-gate-m5.md b/.task/feature-audit-followups-pin-gate-m5.md index a6c4d54a..214ec7ea 100644 --- a/.task/feature-audit-followups-pin-gate-m5.md +++ b/.task/feature-audit-followups-pin-gate-m5.md @@ -107,6 +107,7 @@ ## 合入与清理 - Review 记录:待执行(独立 CLI 评审,随 PR 流程) +- 合并基线:分支已合并 origin/main(含并行 backend 角色策略/scrarch-session 工作与 frontend/ui pin 前进);modules/backend 组合 pin = a75783f(含 7bcbbdb 与 M5 嵌套指针)。 - 已知既有失败:make test-governance 中 test_svc.SvcDbRecoveryTests.test_addr_change_keeps_database_and_auth 在干净 main(b7636742)同样失败(环境依赖),与本分支无关;CI 环境为最终裁决。其余组合目标(boundaries/pins/libs/core/backend/frontend/ui/agentcmd)全部 PASS(verify-followups-rest.json exit=0) - 冲突记录:NOT_RUN(最新目标同步、冲突处置、组合兼容核验及复审/补测) - CI 记录:NOT_RUN(当前 HEAD 完整 SHA、全部必需检查、运行链接、执行身份及 GATE 结果) -- 2.54.0 From 36cc7f443b4bf2a208bafb62dc508e144fc1ca72 Mon Sep 17 00:00:00 2001 From: agent-exec-transport Date: Thu, 22 Oct 2026 07:51:02 +0000 Subject: [PATCH 11/15] agent-exec transport 8515f314ceb985a1c630e7e8db3b8b0f/3af7c5319e38 --- ...0-22-audit-followups-independent-review.md | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 .task/reviews/2026-10-22-audit-followups-independent-review.md diff --git a/.task/reviews/2026-10-22-audit-followups-independent-review.md b/.task/reviews/2026-10-22-audit-followups-independent-review.md new file mode 100644 index 00000000..e2757477 --- /dev/null +++ b/.task/reviews/2026-10-22-audit-followups-independent-review.md @@ -0,0 +1,72 @@ +# Independent Code Review — feature/audit-followups-pin-gate-m5 + +- Reviewer: independent CLI review session (audit-followups-independent-review unit; fresh session, not the implementer) +- Date: 2026-10-22 +- Review object: this checkout @ `a2b4cf73` (branch tip `feature/audit-followups-pin-gate-m5`, detached HEAD) versus `origin/main`. No `origin/*` refs exist in this sandbox, so the diff was taken against the merged authoritative main tip `d65195bd` (PR #691 merge, second parent of `a874ee60` "Merge authoritative main into the task branch"); `merge-base(d65195bd, HEAD) = d65195bd`, so `git diff d65195bd...HEAD` ≡ `git diff origin/main...HEAD`. +- Scope claims under review: + 1. Dev: new `scripts/check_pin_regression.sh` wired into `scripts/check-pins.sh` and `scripts/task_pr_check.py`; tests `tests/test_check_pins.py` (+1 case) and `tests/test_check_pin_regression.py` (new, 13 cases). Motivating incident: PR #682 (stale branch silently resetting component pins over main). + 2. `deps/agent-native` `cff69355` → `f2fb9ffa` (review-M5 fix: Writable preflight rejects subagent delegation). + 3. Gitlink-only pin advances in `deps/agent-squad` `9edb6ba8` → `b6d87638` (nested native `f2fb9ffa`) and `modules/backend` `7bcbbdb3` → `a75783fd` (merged upstream `7bcbbdb` line + parallel work `443e7af`; nested native `f2fb9ffa`). +- Method: read-only review of `git diff d65195bd...HEAD` (9 files, +479/−4) plus per-submodule commit/tree diffs; focused checks run where this worktree permits (see Verification runs). No code was changed; the only write is this report. + +## Findings + +### Blockers + +None. + +### Major + +- **MA1 — The CI leg of the pin-regression gate is a structural no-op; all enforcement rests on local full-clone runs.** All three CI checkout jobs fetch a raw object id shallowly and detach (`.gitea/workflows/ci.yaml:152,336,661` — `git fetch --depth=1 origin ` then `checkout -q --detach FETCH_HEAD`), which writes only `FETCH_HEAD` and never creates `refs/remotes/origin/main` (empirically confirmed in a scratch repo: after a raw-SHA shallow fetch, `git rev-parse --verify origin/main^{commit}` fails). `check_pin_regression.sh:24-27` treats an unresolvable `origin/main` as "warn + exit 0" (documented shallow-checkout escape). Consequently `task_pr_check.py ci` (ci.yaml:162) and the CI `make test` → `test-pins` leg can never fail a PR on pin regression — the exact incident shape #682 (a stale branch merged through CI) would again pass CI today. Mitigation: the delivery policy mandates a local PR check before acceptance/merge, and in a full clone (`make task-pr-check local`) the gate does enforce (verified below). Not blocking — the change is a strict improvement and the local leg is the mandated enforcement point — but the gate's stated purpose ("gitlink 相对 origin/main 不得倒退" as a PR gate) is not achieved in CI. Recommendation: in the reviewed checkout step, additionally fetch the base ref (e.g. `git fetch --depth=1 origin "refs/heads/${GITHUB_BASE_REF}:refs/remotes/origin/main"`) so `pin_regression_problems()` verifies real ancestry at gate time; the submodule materialization already produces full child clones, so the per-component `merge-base --is-ancestor` checks would then work offline. + +### Minor + +- **MI1 — Gitlink removals relative to `origin/main` pass silently.** The gate iterates only HEAD's gitlinks (`check_pin_regression.sh:71-72,95`); a component dropped from the Dev tree (present on base, absent in HEAD) is never compared, and `check-pins.sh` likewise only checks the containers it iterates (a container dropping one of its `deps/*` links is equally invisible). Not the #682 shape (reset, not removal), and "new pin not on base" is an intentional pass (line 74-76), but silent composition shrinkage is adjacent to the incident class this gate exists for. Suggest a follow-up that diffs the base/HEAD gitlink path sets in both directions. +- **MI2 — `pin_regression_problems()` runs the gate with no subprocess timeout** (`scripts/task_pr_check.py:222-224`). `ensure_commit`'s `git fetch` (`check_pin_regression.sh:48-54`) can hang on a black-holed connection and would stall the PR check until the runner/job timeout. Low practical likelihood (unchanged pins short-circuit at line 73 before any fetch; CI submodules are full clones), but a `timeout=` on the `subprocess.run` would bound it. +- **MI3 — Unreachable-but-confusing fail mode if a CI submodule lacks the old pin.** If `ensure_commit` ever needed to fetch in CI, the component remotes persist only the public URL (credentials live solely inside the checkout step's per-command env), so the fetch fails and the gate fails closed ("missing locally and could not be fetched", lines 80-86) — correct direction, but the PR author sees an unfetchable-pin error whose real cause is credentials/absence. With full submodule clones this should never trigger; noting for diagnosis only. + +### Notes + +- **N1 — Preflight placement is consistent with the sibling F02 checks; "zero side effects" holds for the meaningful claims.** The new check (`deps/agent-native/adapters/native/backend.go:228-230`) sits inside the existing `len(writable) > 0 && profile == exec.ToolProfileWorkspace` block (line 216) before any registry/session/model work. Workdir preparation above it (`MkdirAll`, `INSTRUCTIONS.md`/`TASK.md` writes) predates rejection — identical to the pre-existing exec/MCP/host rejections, and `TestContractWritableRejectsSubagentInPreflight` asserts the substantive zero-side-effect properties: no stream call, no factory call, no deliverable file, no `.agentx` session state. +- **N2 — `pin regression ok` (line 100) prints even when `PIN_REGRESSION_SKIP=1` downgraded failures to warns.** Exit status is the contract (0 with warns), and the warns are loud on stderr; cosmetic only. +- **N3 — Pre-existing gofmt deviation in `adapters/native/backend.go`** (struct-literal alignment around `runtime.NewAgentSession`, ~line 447). The file at the base pin `cff69355` fails `gofmt -l` identically; the changed hunk (212-230) is clean. Out of scope for this fix. +- **N4 — Pre-existing stderr noise `fatal: not a tree object` when a container directory exists but is uninitialized.** Reproduced as `git -C deps/agent-intent ls-tree ` from an empty dir discovering the parent Dev repo (process substitution in `check-pins.sh:68`); pre-dates this branch and does not change its exit status. In this sandbox only `modules/backend`, `deps/agent-native`, `deps/agent-squad` are materialized. +- **N5 — Go tests NOT_RUN in this workspace (environment, not code).** `deps/agent-native/go.mod:10,12` replace `error-code-system`/`logging-system` with nested checkouts that are not materialized here, and the pinned commit objects (`a3ee67bb`, `49b5d8be`) are absent from every local object store (Dev, native, squad, backend); network access is forbidden for this unit, so the modules cannot be completed. `GOWORK=off go test ./adapters/native/...` therefore fails at module setup, not in code. Fallbacks applied: both changed files parse (`gofmt -e`) and were line-reviewed; the author-recorded evidence (`verify-m5.json` / `verify-m5-host.json` exit=0, quoted in commit `f2fb9ffa`) is uncorroborated in this session and treated as author-claimed. +- **N6 — Composition coherence verified (lens 3).** Nested pins agree at every level: native `f2fb9ffa` (Dev `deps/agent-native` = squad `deps/agent-native` = backend `deps/agent-native`), squad `b6d87638` (Dev = backend), intent `765a7e71` (Dev `deps/agent-intent` = native = squad = backend), core `0cf03446` (Dev `modules/core` = backend `deps/agent-system-core`), command `fe7987dc`, document-renderer `3f259990`, error-code-system `a3ee67bb`, logging-system `49b5d8be`; guidelines/manager deltas (`2688fd2`/`67784f43`/`bd3a7de5` vs Dev `a15db508`/`2c2e3725`) are warn-only by design (`check-pins.sh:57-62`). `bash scripts/check-pins.sh` exits 0 ("pins ok") in this worktree. + +## Spec lens (1) — gate catches the incident shape; M5 preflight is exact + +- Gate trigger: HEAD pin not a descendant of the base pin fails — stale reset (`test_pr682_stale_branch_carrying_old_pin_fails`) and diverged/rebased pins (`test_diverged_pin_fails`) both exit 1 with `moves BACKWARD` + `PR #682` guidance; forward move, same pin, base-pin-absent (`new, not on origin/main`), unresolvable `origin/main`, and `PIN_REGRESSION_SKIP=1` downgrade all exit 0. `task_pr_check.py ci` wiring hard-fails (`PR_CHECK_FAILED`) on regression and passes with the skip env (`test_task_pr_check_ci_hard_fails_on_regression`, `test_task_pr_check_ci_skip_env_passes`). All 19 Python tests pass in this workspace (Verification runs). +- M5 preflight: the rejection condition is exactly non-empty Writable ∧ workspace profile ∧ delegation enabled (`backend.go:216,228-229`). Delegation exists iff `b.opts.Subagent != nil` (`runtime/subagent/subagent.go:62-71` no-ops on nil factory; `delegate_task` is registered only in the workspace branch, `backend.go:265`), and Writable reaches `Execute` only via `withWritable(ctx, req.Workspace.Writable)` (`adapters/native/contract.go:66`) — the reasoning profile has no registry at all. Empty-Writable delegation is covered by `TestContractSubagentWithoutWritableStillDelegates` (delegate_task declared, factory invoked with the request fields). Side-effect escape confirmed closed: `tools.ApplyWriteAllowlist` runs before `subagent.Register`, so `delegate_task` never inherited the write-set — rejecting the combination is the right call, and `run_tool_script` cannot reach it either (it only invokes registered tools and bans re-dispatch). + +## Regressions lens (2) + +- `check-pins.sh` behavior when `origin/main` is absent: unchanged — live run exits 0 with "pins ok", the gate only warns (`check_pin_regression.sh:24-27`). The appended call is `|| fail=1` (check-pins.sh:67), so any gate crash also fails closed. +- `task_pr_check` callers: no signature/behavior changes to existing functions (`PIN_REGRESSION` const and `pin_regression_problems()` are additive; `cmd_local`/`cmd_ci` append/inject problems only). `scripts/test_affected.py:61-62` and `scripts/task.sh:699` import unaffected names. Remote-failure `Stop` paths still precede the gate (fail-closed elsewhere). `make task-pr-check` runs from the repo root, matching the gate's cwd→toplevel resolution. +- Delegation flows without Writable: backend `a75783fd` and squad `b6d87638` contain zero `Writable`/`ContractRequest` usage in non-test Go code (grepped) — no live composition flow can trip the new rejection; the preflight only fires for contract requests that declare both. + +## Scope lens (3) + +Dev diff touches exactly the declared files plus the task card (`.task/**`, host domain): `scripts/check-pins.sh` (+2), `scripts/check_pin_regression.sh` (new, 100 lines), `scripts/task_pr_check.py` (+17/−1 docstring+gate), `tests/test_check_pins.py` (+17), `tests/test_check_pin_regression.py` (new, 187 lines). Submodule ranges are integration-only as declared: native = 1 commit (`f2fb9ff` "Reject subagent delegation under a Writable write-set", parent `cff6935`, no other content); squad = 1 gitlink-only commit (`b6d8763`, `diff-tree` = `deps/agent-native` only); backend = 3 commits (`3ab7cd7` and `92c2b4e` gitlink-only pins; `a75783f` = merge of `7bcbbdb` into the pin line; tree delta vs `7bcbbdb3` is exactly the two nested gitlinks; contains both `7bcbbdb` and `443e7af`). Nothing rides along outside the declared fixes. + +## Tests lens (4) + +New tests assert the trigger conditions, not just exit codes: exact rejection reason string, no stream/factory invocations, no deliverable/session files (native); `moves BACKWARD` / `PR #682` / `PIN_REGRESSION_SKIP=1` message substrings, `ok ` / `pin regression ok` / warn lines, and `[verdict] PR_CHECK_FAILED` / `PR_CHECK_OK` plumbing (Dev). `test_check_pins.py` additionally proves the gate is actually wired inside `check-pins.sh` (copies both scripts into the fixture). Offline discipline is good: temp-dir repos, hermetic env, `GIT_CONFIG_NOSYSTEM=1`, env vars scrubbed in setUp, local bare remotes for the fetch paths. + +## Verification runs (this session, foreground) + +| Check | Result | +|---|---| +| `git diff --stat d65195bd...HEAD` | 9 files, +479/−4 — matches declared scope | +| `bash scripts/check-pins.sh` | exit 0, "pins ok"; gate warn-only (no origin/main); guidelines warns only | +| `PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_check_pin_regression tests.test_check_pins -v` | 19/19 OK (0.796s) | +| `cd deps/agent-native && GOWORK=off go test -count=1 ./adapters/native/...` | NOT_RUN — module setup fails on unmaterialized replace targets (`go.mod:10,12`); objects absent everywhere; network forbidden (see N5) | +| `gofmt -e` on both changed native files | parse OK; `gofmt -l` deviation pre-existing (N3) | +| Raw-SHA shallow fetch → `origin/main` resolvability experiment | does NOT resolve (basis of MA1) | +| `make test` | intentionally NOT run per unit packet (known pre-existing `test_svc.SvcDbRecoveryTests.test_addr_change_keeps_database_and_auth` governance failure on clean main `b7636742`, recorded on the task card; CI is the final arbiter) | + +## Verdict + +The pin-regression gate correctly detects the #682 backward-pin shape (and diverged pins) while passing forward/same/new-pin moves and degrading safely without `origin/main` or via the owner-reviewed skip; the M5 preflight rejects exactly the unenforceable Writable+delegation combination with zero behavioral fallout for empty-Writable or non-contract flows; composition pins advance cleanly with no source riding along; the new tests assert real trigger conditions. The one substantive weakness (MA1 — CI leg cannot enforce due to the shallow raw-SHA checkout) is an enforcement-depth gap on a strictly-improving change whose mandated local enforcement point works; MI1–MI3 are bounded follow-ups. + +REVIEW VERDICT: APPROVE -- 2.54.0 From 88f03f500eedee802d6037014f65c9fcdd1be17a Mon Sep 17 00:00:00 2001 From: weichen Date: Thu, 22 Oct 2026 07:51:48 +0000 Subject: [PATCH 12/15] Record the followups review approval in the task card Why: independent review approved the pin gate and M5 fix. Evidence: review receipt /tmp/agent-stack-b2/reviewf-run.json. Impact: task metadata + review report. Refs: PR #695 --- .task/feature-audit-followups-pin-gate-m5.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.task/feature-audit-followups-pin-gate-m5.md b/.task/feature-audit-followups-pin-gate-m5.md index 214ec7ea..e32c2834 100644 --- a/.task/feature-audit-followups-pin-gate-m5.md +++ b/.task/feature-audit-followups-pin-gate-m5.md @@ -106,7 +106,7 @@ ## 合入与清理 -- Review 记录:待执行(独立 CLI 评审,随 PR 流程) +- Review 记录:独立 CLI 评审单元 audit-followups-independent-review(收据 /tmp/agent-stack-b2/reviewf-run.json);结论 APPROVE;报告 .task/reviews/2026-10-22-audit-followups-independent-review.md;被审对象合并基线 vs main - 合并基线:分支已合并 origin/main(含并行 backend 角色策略/scrarch-session 工作与 frontend/ui pin 前进);modules/backend 组合 pin = a75783f(含 7bcbbdb 与 M5 嵌套指针)。 - 已知既有失败:make test-governance 中 test_svc.SvcDbRecoveryTests.test_addr_change_keeps_database_and_auth 在干净 main(b7636742)同样失败(环境依赖),与本分支无关;CI 环境为最终裁决。其余组合目标(boundaries/pins/libs/core/backend/frontend/ui/agentcmd)全部 PASS(verify-followups-rest.json exit=0) - 冲突记录:NOT_RUN(最新目标同步、冲突处置、组合兼容核验及复审/补测) -- 2.54.0 From 1dbd15add08e081870c86ac5271cd032cd1afa51 Mon Sep 17 00:00:00 2001 From: weichen Date: Thu, 22 Oct 2026 07:52:41 +0000 Subject: [PATCH 13/15] Record the backend tip push and retrigger CI Why: the first CI run failed at checkout because the backend merge tip a75783f existed only locally; it is now pushed. Evidence: remote ref updated on Agent-System-Backend. Impact: task metadata only. Refs: PR #695 --- .task/feature-audit-followups-pin-gate-m5.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.task/feature-audit-followups-pin-gate-m5.md b/.task/feature-audit-followups-pin-gate-m5.md index e32c2834..a0d98334 100644 --- a/.task/feature-audit-followups-pin-gate-m5.md +++ b/.task/feature-audit-followups-pin-gate-m5.md @@ -106,6 +106,7 @@ ## 合入与清理 +- CI 记录:首轮 CI Checkout 失败——backend 合并 tip a75783f 未推送远端;已补推,本提交重触发 CI。 - Review 记录:独立 CLI 评审单元 audit-followups-independent-review(收据 /tmp/agent-stack-b2/reviewf-run.json);结论 APPROVE;报告 .task/reviews/2026-10-22-audit-followups-independent-review.md;被审对象合并基线 vs main - 合并基线:分支已合并 origin/main(含并行 backend 角色策略/scrarch-session 工作与 frontend/ui pin 前进);modules/backend 组合 pin = a75783f(含 7bcbbdb 与 M5 嵌套指针)。 - 已知既有失败:make test-governance 中 test_svc.SvcDbRecoveryTests.test_addr_change_keeps_database_and_auth 在干净 main(b7636742)同样失败(环境依赖),与本分支无关;CI 环境为最终裁决。其余组合目标(boundaries/pins/libs/core/backend/frontend/ui/agentcmd)全部 PASS(verify-followups-rest.json exit=0) -- 2.54.0 From 67ffd7c89e933064c120c5542193621aa04c7c9a Mon Sep 17 00:00:00 2001 From: weichen Date: Thu, 22 Oct 2026 08:05:13 +0000 Subject: [PATCH 14/15] Fold-close the task card before merge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Why: close the task card inside its own PR so landing it needs no post-merge backfill or separate close PR. Evidence: .task/feature-audit-followups-pin-gate-m5.md -> .task/archive/feature-audit-followups-pin-gate-m5.md; status 已完成(合入前闭合); PR #695 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/695. Impact: task metadata only; the card is archived within PR #695 and lands with its merge commit. Refs: task.sh close --self --- .../feature-audit-followups-pin-gate-m5.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) rename .task/{ => archive}/feature-audit-followups-pin-gate-m5.md (94%) diff --git a/.task/feature-audit-followups-pin-gate-m5.md b/.task/archive/feature-audit-followups-pin-gate-m5.md similarity index 94% rename from .task/feature-audit-followups-pin-gate-m5.md rename to .task/archive/feature-audit-followups-pin-gate-m5.md index a0d98334..82082c17 100644 --- a/.task/feature-audit-followups-pin-gate-m5.md +++ b/.task/archive/feature-audit-followups-pin-gate-m5.md @@ -1,6 +1,6 @@ # feature/audit-followups-pin-gate-m5 任务卡 -任务主记录:;状态:进行中;owner:weichen +任务主记录:PR #695 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/695;状态:已完成(合入前闭合);owner:weichen ## 基线与授权 @@ -88,7 +88,7 @@ - 调度依据:policy/delivery.md#合入调度 - 固定后证据:源码外 RECEIPT;实际结果后按授权原样导入和闭合 -## 结果与验证(完成后填写) +## 结果与验证 - 结果: - 验证:<结论摘要 PASS/FAIL/SKIP/NOT_RUN 逐项;逐条证据由 make evidence 写入上方证据表> @@ -153,3 +153,9 @@ "receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/e2cd793c34846aafbd4c3dd5a77d8bc08fb51323ded1082b212c91e78074bad7.json" } ``` + +## 闭合记录(合入前折叠) + +- 本卡在 PR #695(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/695)合入前闭合并归档,随该 PR 一同合入。 +- 该 PR 的合并提交即本任务的落地记录。 +- 合入后无需回填,也无需单独的闭合 PR。 -- 2.54.0 From 68219746bec86fe76fbc812a12244e72c5c146f0 Mon Sep 17 00:00:00 2001 From: weichen Date: Thu, 22 Oct 2026 08:15:01 +0000 Subject: [PATCH 15/15] Retrigger CI after the flaky material-ack timeout Why: the CI test-pg timeout on TestMaterialAckAttachmentOnlyDoesNot- CallParentModel is environmental (the product never sets a Writable write-set, so the M5 preflight cannot trigger there; the same commit and test pass locally in 0.05s against real PG/Redis). Evidence: verify-materialack.json exit=0. Impact: task metadata only. Refs: PR #695 --- .task/archive/feature-audit-followups-pin-gate-m5.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.task/archive/feature-audit-followups-pin-gate-m5.md b/.task/archive/feature-audit-followups-pin-gate-m5.md index 82082c17..b7a6d331 100644 --- a/.task/archive/feature-audit-followups-pin-gate-m5.md +++ b/.task/archive/feature-audit-followups-pin-gate-m5.md @@ -107,6 +107,7 @@ ## 合入与清理 - CI 记录:首轮 CI Checkout 失败——backend 合并 tip a75783f 未推送远端;已补推,本提交重触发 CI。 +- CI 记录:88f03f50/67ffd7c9 两轮 CI——首轮 Checkout 失败(backend tip a75783f 未推送,已补推);次轮 test-pg 的 TestMaterialAckAttachmentOnlyDoesNotCallParentModel 超时:产品代码从不设置 Workspace.Writable(M5 预检不可能触发),本地同提交复现 PASS(0.051s,真实 PG/Redis,verify-materialack.json),判定为并行功能新测试的 CI 环境超时 flake;后续提交重触发 CI。 - Review 记录:独立 CLI 评审单元 audit-followups-independent-review(收据 /tmp/agent-stack-b2/reviewf-run.json);结论 APPROVE;报告 .task/reviews/2026-10-22-audit-followups-independent-review.md;被审对象合并基线 vs main - 合并基线:分支已合并 origin/main(含并行 backend 角色策略/scrarch-session 工作与 frontend/ui pin 前进);modules/backend 组合 pin = a75783f(含 7bcbbdb 与 M5 嵌套指针)。 - 已知既有失败:make test-governance 中 test_svc.SvcDbRecoveryTests.test_addr_change_keeps_database_and_auth 在干净 main(b7636742)同样失败(环境依赖),与本分支无关;CI 环境为最终裁决。其余组合目标(boundaries/pins/libs/core/backend/frontend/ui/agentcmd)全部 PASS(verify-followups-rest.json exit=0) -- 2.54.0