Skip to content

v0.0.50

Choose a tag to compare

@github-actions github-actions released this 15 Sep 12:31
· 10 commits to main since this release
4e055b5
  • fix(gate): enforce mergeBy consistently across all four roles — PR merge checks previously read mergeBy only on production, denied archive merges unconditionally, and ignored mergeBy on integration/preview entirely; decidePrMerge now reads mergeBy for every role with dedicated i18n guidance, keeping each role's established default (integration/preview = anyone, production/archive = user). Behavior change to note: archive merges are no longer denied unconditionally — they are denied unless the archive role explicitly sets mergeBy: "anyone" —— 四角色统一按 mergeBy 判定:此前 PR 合并检查只对生产分支读取 mergeBy、对归档分支无条件拒绝、对集成分支与预览分支完全忽略该配置;现 decidePrMerge 对四个角色统一读取 mergeBy 并给出对应国际化提示,各角色沿用既有默认(integration/preview = anyone,production/archive = user)。需注意的行为变化:归档分支不再是"一律拒绝",而是"除显式配置 mergeBy: "anyone" 外均拒绝"。

  • fix(config,gate): strictly anchor featurePattern — featurePattern regex previously lacked start/end boundary anchors, allowing spoofed branch names like malicious/feature/foo to pass as valid feature branches; default pattern and pattern matching now enforce strict boundary anchors (^(?:...)$) —— 严格首尾锚定 featurePattern 正则:此前分支角色判断未对 featurePattern 实施首尾锚定,导致带有前缀的伪装分支名(如 malicious/feature/foo)被误判为合法 feature 分支;现默认正则与匹配层均严格约束 ^(?:...)$。

  • fix(platform): identify Cursor payloads when cursor_version is omitted — Cursor beforeShellExecution and top-level { command, cwd } payloads lacking cursor_version were previously misclassified as Claude Code, resulting in exit 2 client crashes instead of structured exit 0 with permission JSON; detectPlatform now inspects top-level command attributes and Cursor hook lifecycle events to reliably identify Cursor and return the standard { permission: "deny", ... } JSON payload —— 完善 Cursor 平台识别机制:部分 Cursor 事件未携带 cursor_version 特征导致被误判为 Claude Code 并返回 exit 2 报错;现通过顶层命令结构与生命周期事件精准识别 Cursor,拦截时严格遵循 exit 0 与结构化 JSON 拒绝协议。

  • fix(wire): preserve JSONC comments and existing hooks during wire and unwire — reading/writing JSON configuration previously discarded comments via standard JSON.parse/JSON.stringify, and unwiring blanket-deleted entire hook matcher blocks; wire now uses comment-json so comments survive a rewrite (the file is re-serialized with a uniform 2-space indent), and filtering is fine-grained: only the guard's own hook entries are removed, while sibling hooks, pre-existing empty entries, and non-object entries are left intact; unwiring antigravity keeps the top-level key whenever user-authored content remains in it, except when the guard command sits outside every event array, in which case the key is removed as a whole (legacy migration semantics) —— 保留 JSONC 注释并实现细粒度 Hook 卸载:接线层引入 comment-json 读写用户配置文件,注释在重写后保留(文件统一以 2 空格缩进重新序列化);卸载只摘除守卫自身的 hook 条目,同 matcher 下的用户自定义 Hook、用户原有的空条目与非对象条目均原样保留;antigravity 卸载时若顶层键内仍有用户自建内容则保留该键,仅当守卫命令落在所有事件数组之外时才整键移除(沿用历史迁移语义)。

  • fix(pi): block instead of failing open when the guard subprocess is timed out — the extension's own 10s subprocess timeout raced the guard's internal 10s git budget and killed it first, so a slow git turned into "allow"; the outer timeout is now 45s (above the guard's worst-case budget), a timeout kill is reported as timedOut and mapped to { block: true }, and killSignal: SIGKILL guarantees the kill cannot be ignored —— Pi 扩展超时改为阻断:此前扩展自身的 10s 子进程超时与守卫内部 10s 的 git 预算同量级且先到,慢 git 会被杀成"放行";现外层超时提高到 45s(高于守卫最坏预算),超时被杀标记为 timedOut 并映射为 { block: true },killSignal: SIGKILL 保证杀进程不可被忽略。

  • fix(repo): guarantee the 5s/10s runner timeouts actually fire — execFile used the default SIGTERM, so a child process ignoring that signal kept the guard hanging forever; the git/gh/glab runners now kill with SIGKILL —— 保证 5s/10s 熔断真正生效:execFile 原先使用默认 SIGTERM,忽略该信号的子进程会让守卫永久挂起;现 git/gh/glab 三个 runner 一律用 SIGKILL 终结。

  • fix(index,cli): keep non-git commands out of the timeout path and audit timeout denials — the DSH plugin probed the repository before knowing whether the command was git-related, so a hung git denied unrelated commands such as npm test, and timeout denials were not recorded at all; the plugin now has the same classify fast path as the CLI, and timeout denials are written to audit.jsonl whenever a repository root is known (a findRepoRoot timeout has no repository to attribute to and is reported on stderr instead) —— DSH 插件此前在判断命令是否 git 相关之前就探测仓库,git 卡死会连带拒绝 npm test 这类无关命令,且超时拒绝不留痕;现插件与 CLI 采用同一 classify 快路径,超时拒绝在能定位到仓库根时写入 audit.jsonl(findRepoRoot 自身超时时没有可归属的仓库,改为在 stderr 报告)。

  • test(matrix): count skipped cases as failures — the decision matrix silently skipped rows whose sandbox branch was missing, so a broken sandbox could shrink coverage without turning the run red; skips are now reported and counted into FAIL —— 矩阵把跳过计为失败:此前沙箱缺分支的行会被静默跳过,沙箱损坏会悄悄缩小覆盖而不变红;现跳过会被报告并计入 FAIL。

  • chore(package): ship third-party notices for bundled code — comment-json, esprima and array-timsort are inlined into lib/, so their licenses now travel with the package (THIRD-PARTY-NOTICES.md) —— 随包附第三方声明:comment-json、esprima、array-timsort 被内联进 lib/,其许可文本随包分发(THIRD-PARTY-NOTICES.md)。

  • fix(repo,pi): add timeout control for external CLI and git runner child processes — gh and glab CLI calls previously lacked timeout bounds and could hang indefinitely on network failure or auth prompts; runners now enforce a 5-second timeout for external CLIs and a 10-second timeout for git/pi child processes —— 外部子进程增加超时熔断:gh/glab 等外部 CLI 子进程增加 5 秒超时保护,本地 git 与 Pi 扩展增加 10 秒超时,防止网络卡顿或鉴权挂死导致守卫进程永久阻塞。

  • fix(repo,gate): fail closed when worktree status query fails — getWorktreeStatus previously caught git failures (e.g. index.lock locks) and returned a clean status, allowing dirty worktrees to bypass clean-worktree gates; it now returns null on failure, and worktree gates fail closed by rejecting sensitive operations when worktree status cannot be determined —— 修复工作区状态查询失败时的假干净漏洞:getWorktreeStatus 在 git 报错时不再虚假返回干净状态,改为返回 null;门禁在工作区状态不可知时遵循安全失败(fail-closed)原则坚决阻断。

  • ci(release): run test:git-matrix and test:realflow in release verification — the 169-case git decision matrix and realflow E2E tests are now executed within the release workflow verification suite before tagging and publishing —— 发布流水线补充 Git 决策矩阵与真机流回归:在发版校验阶段加入 test:git-matrix 与 test:realflow,防止缺陷版本流入发布环节。

  • test(pi): enforce strict branch assertions in test-pi-extension.sh — post-state branch immutability checks previously used || echo which masked failures and reported PASS even if a branch was illegally moved; the script now tracks assertion failures and exits with a non-zero exit code on failure —— 严密 Pi 扩展测试断言:移除屏蔽非零退出码的容错逻辑,分支被篡改时立即输出错误并中断退出。

  • fix(wire): keep user data intact during fine-grained filtering — the filtering introduced in this batch silently deleted non-object array entries and user-authored empty entries on every wire/unwire; filtering now recycles only the entries that became empty because the guard's own command was removed, antigravity unwiring walks every event array under its top-level key, and the key is kept whenever user-authored content remains in it —— 修复本批引入的非破坏性回归:细粒度过滤会在每次 wire/unwire 时静默删除数组中的非对象条目与用户原有的空条目;现仅回收"因摘除守卫自身命令而变空"的条目,antigravity 卸载遍历顶层键内所有事件数组,且键内仍有用户内容时保留该键。

  • fix(repo,gate,cli): fail closed when a git query times out — the 5s/10s timeouts added in this batch funneled "killed by timeout" into the same bucket as ordinary failures, so branch/upstream lookups that timed out degraded to “unprotected” and allowed the command; runners now surface timedOut, repo helpers raise RunnerTimeoutError, and the gate, DSH plugin and CLI check paths all deny with a dedicated message instead of failing open —— git 查询超时改为保守拒绝:本批新增的 5s/10s 超时把"超时被杀"与普通失败混在一起,分支/上游查询一旦超时会静默降级为"不受保护"而放行;现 runner 显式暴露 timedOut,仓库查询抛 RunnerTimeoutError,门禁、DSH 插件与 CLI check 三条路径均以专属文案拒绝,不再 fail-open。

  • test(matrix): stop counting guard crashes as legitimate allows — scripts/test-git-matrix.sh mapped every exit code other than 2 to allow, so an internal crash (exit 1) could masquerade as a real allow; only 0 (allow) and 2 (deny) are protocol results now, and anything else is counted as FAIL —— 矩阵不再把守卫崩溃记成放行:此前 check() 把所有非 2 退出码都记为 allow,守卫内部崩溃(exit 1)会被伪装成合法放行;现只有 0(放行)与 2(拦截)属协议内结果,其余一律计 FAIL 并单列计数。

  • test(matrix): cover platform auto-detection — the verification matrix always passed --platform, so the new Cursor heuristics had no end-to-end coverage; the Cursor block now also runs gitflow-guard check without --platform and asserts the {"permission":"deny"} payload for a payload without cursor_version —— 矩阵补自动判别覆盖:此前验证矩阵恒传 --platform,新的 Cursor 启发式没有端到端兜底;现 Cursor 区块额外以不带 --platform 的方式实弹断言无 cursor_version 的 payload 仍返回 Cursor 拒绝协议。

  • ci(release): provide the E2E git identity per step — the release workflow wrote a global git identity into the runner (git config --global), leaking into every later git invocation of the job; the two E2E suites now receive identity through step-level environment variables —— 发布工作流不再写全局 git 配置:此前用 git config --global 给 runner 写入全局身份,会波及该作业后续所有 git 调用;现改为步骤级环境变量,仅对两个 E2E 套件生效。

  • docs(readme): document whole-string anchoring of featurePattern — patterns are compiled as ^(?:…)$, so prefix/substring-style patterns no longer match and prefix spoofing (malicious/feature/x) is rejected; the regex-safety bullet says so in all 11 languages, and docs/design.md records the rule with its anti-spoofing rationale —— 11 语言 README 与 docs/design.md 补记 featurePattern 整串锚定语义:模式按 ^(?:…)$ 编译,前缀/子串式写法会失配,前缀伪装(malicious/feature/x)不再被认作 feature 分支。

  • test(wire,repo,gate,platform,cli): close the coverage gaps found in review — non-object and pre-existing empty hook entries survive wire+unwire, an entry mixing user and guard commands unwires to the user command only (codex/zcode), antigravity multi-event unwire keeps user content, a real 50 ms timeout boundary plus RunnerTimeoutError propagation, repoTimeout deny, simulatedClean with a failing git status denies instead of faking a clean tree, conversation_id/generation_id platform detection, and the anchored pattern is asserted in status output —— 补齐评审发现的覆盖缺口:非对象条目与用户原有空条目在 wire+unwire 后保留、单条目内用户与守卫命令混排时只摘守卫(codex/zcode)、antigravity 多事件卸载保留用户内容、真实 50ms 超时边界与 RunnerTimeoutError 传播、repoTimeout 拒绝、simulatedClean 遇 git status 失败时拒绝而非伪造干净、conversation_id/generation_id 平台判别、以及 status 输出中的锚定回归。

  • docs(agents): drop two orphan documents — .agents/hooks/HOOKS.md (no inbound reference anywhere; it documented OpenCode's hooks.yaml mechanism that OpenCode removed in 1.18+, and its client table omitted CodeBuddy / ZCode / Cursor) and docs/blog/gitflow-guard-intro.zh.md (no inbound reference; an early draft still claiming six supported clients) —— 删除两个孤儿文档:.agents/hooks/HOOKS.md(全库无引用;其中记载的 OpenCode hooks.yaml 机制已被 OpenCode 1.18+ 移除,且客户端表缺 CodeBuddy / ZCode / Cursor)与 docs/blog/gitflow-guard-intro.zh.md(全库无引用;早期草稿仍写"六个客户端")。

  • docs(e2e): give the scattered client documents a single entry point — docs/e2e/README.md now carries an index table mapping every wired client to its protocol reference (.agents/hooks/references/<client>.md), its test-case file and its real-device evidence file; clients without physical evidence are marked 待测 instead of linking a file that does not exist. Its Antigravity row no longer claims two protocol differences are still open — AGY-D2/AGY-D3 were fixed and the evidence file records it. docs/e2e/pi.md and the e2e-cases skill now name the in-repo scripts (scripts/test-git-matrix.sh, scripts/test-git-realflow.sh, scripts/test-pi-extension.sh) instead of script names from the retired external test harness —— 给分散的客户端文档加唯一入口:docs/e2e/README.md 新增索引表,逐客户端指向官方协议、测试用例与实机证据三处;尚无物理证据的客户端标"待测",不再给出指向不存在文件的链接。表中 Antigravity 行不再声称"2 处协议差异待修复"——AGY-D2/AGY-D3 均已修复且证据文件有记录。docs/e2e/pi.md 与 e2e-cases skill 中的脚本引用改为仓库内真实入口(scripts/test-git-matrix.sh、scripts/test-git-realflow.sh、scripts/test-pi-extension.sh),不再沿用已退役外部测试场的脚本名。

  • docs(readme): correct the decision-matrix case count from 135 to 169 — the matrix grew by 34 cases in 0.0.47, when the git-alias family (inline -c alias.*, git config alias.*, out-of-band alias channels) was added, but all 11 language READMEs, the E2E overview, docs/e2e/pi.md and a CI comment still quoted the pre-0.0.47 number; the dated evidence files under docs/e2e/TestResult/ are archives and are left untouched —— 订正决策矩阵用例数 135 → 169:实际增长发生在 0.0.47,由 git 别名族用例(内联 -c alias.*、git config alias.*、带外别名通道)一次补入 34 条,而 11 语言 README、E2E 总览、docs/e2e/pi.md 与 CI 注释仍停留在 0.0.47 之前的旧值;docs/e2e/TestResult/ 下的证据文件是归档(只增不改),本 PR 一个字节都不动。

  • docs(design): correct the platform inventory in the project-structure section — it still described platform.ts as eight platforms and omitted Cursor; the module is now described as the 7 stdin-hook platforms, with DSH (in-process plugin) in index.ts and Pi (in-process extension) in pi.ts —— 订正 docs/design.md 结构章节的平台清单:原文写"八平台"且漏掉 Cursor;现表述为该层覆盖 7 个 stdin-hook 平台,DSH(进程内插件)归 index.ts、Pi(进程内扩展)归 pi.ts。

  • test(docs): add mechanical doc-consistency checks (tests/doc-consistency.spec.ts) whose scope is stated exactly — (a) the 11 READMEs plus docs/e2e/README.md and docs/e2e/pi.md must each state the decision-matrix case count, and it must equal the number of case rows in scripts/test-git-matrix.sh; (b) the index rows of docs/e2e/README.md must match WIRE_CLIENTS in both directions, every link in a row must resolve, and the evidence column must be exactly TestResult/<client>.md when that file exists and carry no evidence link when it does not; (c) repository-directory paths referenced in AGENTS.md (src/ tests/ scripts/ bin/ docs/ pi/ opencode/ .agents/ .github/ and the per-client config directories) must exist. Every group carries a lower-bound guard, so a check that silently collapses into a no-op turns red instead of green. Out of scope by design: root-level file names, placeholder tokens, build output, .agents/skills/** bodies, and the dated archives under docs/e2e/TestResult/ —— 新增文档一致性机械校验(tests/doc-consistency.spec.ts),范围如实声明:① 11 语言 README 与 docs/e2e/README.md、docs/e2e/pi.md 都必须提到矩阵用例数且等于 scripts/test-git-matrix.sh 用例表行数;② docs/e2e/README.md 的索引行与 WIRE_CLIENTS 双向一致,行内每个链接都必须可解析,证据列在有 TestResult/<客户端>.md 时必须恰好是它、无该文件时不得带证据链接;③ AGENTS.md 引用的仓库目录路径(src/ tests/ scripts/ bin/ docs/ pi/ opencode/ .agents/ .github/ 及各客户端配置目录)必须存在。三组均带下界守卫:校验若塌缩成空转(正则失效、被校验文本被改写)会变红而不是静默通过。按设计不在范围内:根目录文件名、占位符 token、构建产物、.agents/skills/** 正文、docs/e2e/TestResult/ 下的日期归档。

  • docs(skills): stop restating scale figures in the E2E skills — e2e-cases declared "All 6 clients", "the 135-case decision matrix" and listed only four stdin-hook platforms, and e2e-run said "the 135-case ... decision matrix"; those snapshots were written at 0.0.29 and never moved again while the roster (6 → 9 clients) and the matrix (135 → 169 cases) did — a stale "6 clients" would tell an agent to test six of nine after a kernel change. The skills now point at the sources of truth (WIRE_CLIENTS in src/wire.ts, the platform list in src/platform.ts, the matrix in scripts/test-git-matrix.sh) instead of quoting figures, so adding a client or a case never requires editing skill text again —— skills 不再复述规模数字:e2e-cases 原文写死"All 6 clients""135-case decision matrix"且平台清单只列 4 个,e2e-run 原文写死"135-case";这些快照停在 0.0.29,而客户端数(6 → 9)与矩阵规模(135 → 169)早已变化——过期的"6 个客户端"会让 agent 在内核改动后只测 9 个里的 6 个。现改为指向唯一事实源(src/wire.ts 的 WIRE_CLIENTS、src/platform.ts 的平台清单、scripts/test-git-matrix.sh 的矩阵),今后加客户端或加用例都不必再改 skill 文本。