Repository navigation
v0.0.50
-
fix(gate): enforce
mergeByconsistently across all four roles — PR merge checks previously readmergeByonly on production, denied archive merges unconditionally, and ignoredmergeByon integration/preview entirely;decidePrMergenow readsmergeByfor 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 setsmergeBy: "anyone"—— 四角色统一按mergeBy判定:此前 PR 合并检查只对生产分支读取mergeBy、对归档分支无条件拒绝、对集成分支与预览分支完全忽略该配置;现decidePrMerge对四个角色统一读取mergeBy并给出对应国际化提示,各角色沿用既有默认(integration/preview=anyone,production/archive=user)。需注意的行为变化:归档分支不再是"一律拒绝",而是"除显式配置mergeBy: "anyone"外均拒绝"。 -
fix(config,gate): strictly anchor
featurePattern—featurePatternregex previously lacked start/end boundary anchors, allowing spoofed branch names likemalicious/feature/footo 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_versionis omitted — CursorbeforeShellExecutionand top-level{ command, cwd }payloads lackingcursor_versionwere previously misclassified as Claude Code, resulting inexit 2client crashes instead of structuredexit 0with permission JSON;detectPlatformnow 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;wirenow usescomment-jsonso 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; unwiringantigravitykeeps 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
timedOutand mapped to{ block: true }, andkillSignal: SIGKILLguarantees 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 —
execFileused 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 toaudit.jsonlwhenever a repository root is known (afindRepoRoottimeout 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,esprimaandarray-timsortare inlined intolib/, 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 —
ghandglabCLI 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 —
getWorktreeStatuspreviously caught git failures (e.g.index.locklocks) and returned a clean status, allowing dirty worktrees to bypass clean-worktree gates; it now returnsnullon 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-matrixandtest:realflowin 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|| echowhich 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,
antigravityunwiring 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 raiseRunnerTimeoutError, and the gate, DSH plugin and CLIcheckpaths all deny with a dedicated message instead of failing open —— git 查询超时改为保守拒绝:本批新增的 5s/10s 超时把"超时被杀"与普通失败混在一起,分支/上游查询一旦超时会静默降级为"不受保护"而放行;现 runner 显式暴露timedOut,仓库查询抛RunnerTimeoutError,门禁、DSH 插件与 CLIcheck三条路径均以专属文案拒绝,不再 fail-open。 -
test(matrix): stop counting guard crashes as legitimate allows —
scripts/test-git-matrix.shmapped every exit code other than 2 toallow, 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 runsgitflow-guard checkwithout--platformand asserts the{"permission":"deny"}payload for a payload withoutcursor_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, anddocs/design.mdrecords 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),
antigravitymulti-event unwire keeps user content, a real 50 ms timeout boundary plusRunnerTimeoutErrorpropagation,repoTimeoutdeny,simulatedCleanwith a failinggit statusdenies instead of faking a clean tree,conversation_id/generation_idplatform detection, and the anchored pattern is asserted instatusoutput —— 补齐评审发现的覆盖缺口:非对象条目与用户原有空条目在 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'shooks.yamlmechanism that OpenCode removed in 1.18+, and its client table omitted CodeBuddy / ZCode / Cursor) anddocs/blog/gitflow-guard-intro.zh.md(no inbound reference; an early draft still claiming six supported clients) —— 删除两个孤儿文档:.agents/hooks/HOOKS.md(全库无引用;其中记载的 OpenCodehooks.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.mdnow 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.mdand thee2e-casesskill 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-casesskill 中的脚本引用改为仓库内真实入口(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.mdand a CI comment still quoted the pre-0.0.47 number; the dated evidence files underdocs/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.tsas eight platforms and omitted Cursor; the module is now described as the 7 stdin-hook platforms, with DSH (in-process plugin) inindex.tsand Pi (in-process extension) inpi.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 plusdocs/e2e/README.mdanddocs/e2e/pi.mdmust each state the decision-matrix case count, and it must equal the number of case rows inscripts/test-git-matrix.sh; (b) the index rows ofdocs/e2e/README.mdmust matchWIRE_CLIENTSin both directions, every link in a row must resolve, and the evidence column must be exactlyTestResult/<client>.mdwhen 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 underdocs/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-casesdeclared "All 6 clients", "the 135-case decision matrix" and listed only four stdin-hook platforms, ande2e-runsaid "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_CLIENTSinsrc/wire.ts, the platform list insrc/platform.ts, the matrix inscripts/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 文本。