From 59b9e3685bf3875d662f25f54177b94f8e2f6695 Mon Sep 17 00:00:00 2001 From: loong10k <20489781+loong10k@users.noreply.github.com> Date: Thu, 24 Sep 2026 00:42:43 +0800 Subject: [PATCH] =?UTF-8?q?chore(openspec):=20=E5=BD=92=E6=A1=A3=203=20?= =?UTF-8?q?=E4=B8=AA=E5=85=A8=E5=8B=BE=20change=EF=BC=88heredoc-attributio?= =?UTF-8?q?n=20/=20release-chain=20/=20save-scope=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 全部任务已勾选且已随 v0.15.3/v0.15.4 发布: - 2026-09-23-fix-heredoc-git-attribution (10/10) - 2026-09-23-fix-release-chain-and-gate-attribution (13/13) - 2026-09-23-save-scope-lock-and-doc-parity (14/14) delta specs 并入主 specs(含 hook-protocol 更新);EOF 空行归一。 in-flight 仅剩 gate-toolchain-unverified(7.3 宿主侧人工项,正当保留)。 验证:run_all 144/0/0 · openspec strict 18/18 · diff clean (unittest 6 个预存环境红不在此批:无 ruff F821 族 + 多会话状态族,上批已对照证实非本批引入) --- .../proposal.md | 0 .../specs/hook-protocol/spec.md | 0 .../tasks.md | 0 .../design.md | 0 .../proposal.md | 0 .../specs/hook-protocol/spec.md | 0 .../tasks.md | 0 .../design.md | 0 .../proposal.md | 0 .../specs/doc-behavior-parity/spec.md | 0 .../specs/hook-protocol/spec.md | 0 .../tasks.md | 0 openspec/specs/doc-behavior-parity/spec.md | 22 ++++++ openspec/specs/hook-protocol/spec.md | 79 ++++++++++++++++++- 14 files changed, 97 insertions(+), 4 deletions(-) rename openspec/changes/{ => archive}/2026-09-23-fix-heredoc-git-attribution/proposal.md (100%) rename openspec/changes/{ => archive}/2026-09-23-fix-heredoc-git-attribution/specs/hook-protocol/spec.md (100%) rename openspec/changes/{ => archive}/2026-09-23-fix-heredoc-git-attribution/tasks.md (100%) rename openspec/changes/{ => archive}/2026-09-23-fix-release-chain-and-gate-attribution/design.md (100%) rename openspec/changes/{ => archive}/2026-09-23-fix-release-chain-and-gate-attribution/proposal.md (100%) rename openspec/changes/{ => archive}/2026-09-23-fix-release-chain-and-gate-attribution/specs/hook-protocol/spec.md (100%) rename openspec/changes/{ => archive}/2026-09-23-fix-release-chain-and-gate-attribution/tasks.md (100%) rename openspec/changes/{ => archive}/2026-09-23-save-scope-lock-and-doc-parity/design.md (100%) rename openspec/changes/{ => archive}/2026-09-23-save-scope-lock-and-doc-parity/proposal.md (100%) rename openspec/changes/{ => archive}/2026-09-23-save-scope-lock-and-doc-parity/specs/doc-behavior-parity/spec.md (100%) rename openspec/changes/{ => archive}/2026-09-23-save-scope-lock-and-doc-parity/specs/hook-protocol/spec.md (100%) rename openspec/changes/{ => archive}/2026-09-23-save-scope-lock-and-doc-parity/tasks.md (100%) create mode 100644 openspec/specs/doc-behavior-parity/spec.md diff --git a/openspec/changes/2026-09-23-fix-heredoc-git-attribution/proposal.md b/openspec/changes/archive/2026-09-23-fix-heredoc-git-attribution/proposal.md similarity index 100% rename from openspec/changes/2026-09-23-fix-heredoc-git-attribution/proposal.md rename to openspec/changes/archive/2026-09-23-fix-heredoc-git-attribution/proposal.md diff --git a/openspec/changes/2026-09-23-fix-heredoc-git-attribution/specs/hook-protocol/spec.md b/openspec/changes/archive/2026-09-23-fix-heredoc-git-attribution/specs/hook-protocol/spec.md similarity index 100% rename from openspec/changes/2026-09-23-fix-heredoc-git-attribution/specs/hook-protocol/spec.md rename to openspec/changes/archive/2026-09-23-fix-heredoc-git-attribution/specs/hook-protocol/spec.md diff --git a/openspec/changes/2026-09-23-fix-heredoc-git-attribution/tasks.md b/openspec/changes/archive/2026-09-23-fix-heredoc-git-attribution/tasks.md similarity index 100% rename from openspec/changes/2026-09-23-fix-heredoc-git-attribution/tasks.md rename to openspec/changes/archive/2026-09-23-fix-heredoc-git-attribution/tasks.md diff --git a/openspec/changes/2026-09-23-fix-release-chain-and-gate-attribution/design.md b/openspec/changes/archive/2026-09-23-fix-release-chain-and-gate-attribution/design.md similarity index 100% rename from openspec/changes/2026-09-23-fix-release-chain-and-gate-attribution/design.md rename to openspec/changes/archive/2026-09-23-fix-release-chain-and-gate-attribution/design.md diff --git a/openspec/changes/2026-09-23-fix-release-chain-and-gate-attribution/proposal.md b/openspec/changes/archive/2026-09-23-fix-release-chain-and-gate-attribution/proposal.md similarity index 100% rename from openspec/changes/2026-09-23-fix-release-chain-and-gate-attribution/proposal.md rename to openspec/changes/archive/2026-09-23-fix-release-chain-and-gate-attribution/proposal.md diff --git a/openspec/changes/2026-09-23-fix-release-chain-and-gate-attribution/specs/hook-protocol/spec.md b/openspec/changes/archive/2026-09-23-fix-release-chain-and-gate-attribution/specs/hook-protocol/spec.md similarity index 100% rename from openspec/changes/2026-09-23-fix-release-chain-and-gate-attribution/specs/hook-protocol/spec.md rename to openspec/changes/archive/2026-09-23-fix-release-chain-and-gate-attribution/specs/hook-protocol/spec.md diff --git a/openspec/changes/2026-09-23-fix-release-chain-and-gate-attribution/tasks.md b/openspec/changes/archive/2026-09-23-fix-release-chain-and-gate-attribution/tasks.md similarity index 100% rename from openspec/changes/2026-09-23-fix-release-chain-and-gate-attribution/tasks.md rename to openspec/changes/archive/2026-09-23-fix-release-chain-and-gate-attribution/tasks.md diff --git a/openspec/changes/2026-09-23-save-scope-lock-and-doc-parity/design.md b/openspec/changes/archive/2026-09-23-save-scope-lock-and-doc-parity/design.md similarity index 100% rename from openspec/changes/2026-09-23-save-scope-lock-and-doc-parity/design.md rename to openspec/changes/archive/2026-09-23-save-scope-lock-and-doc-parity/design.md diff --git a/openspec/changes/2026-09-23-save-scope-lock-and-doc-parity/proposal.md b/openspec/changes/archive/2026-09-23-save-scope-lock-and-doc-parity/proposal.md similarity index 100% rename from openspec/changes/2026-09-23-save-scope-lock-and-doc-parity/proposal.md rename to openspec/changes/archive/2026-09-23-save-scope-lock-and-doc-parity/proposal.md diff --git a/openspec/changes/2026-09-23-save-scope-lock-and-doc-parity/specs/doc-behavior-parity/spec.md b/openspec/changes/archive/2026-09-23-save-scope-lock-and-doc-parity/specs/doc-behavior-parity/spec.md similarity index 100% rename from openspec/changes/2026-09-23-save-scope-lock-and-doc-parity/specs/doc-behavior-parity/spec.md rename to openspec/changes/archive/2026-09-23-save-scope-lock-and-doc-parity/specs/doc-behavior-parity/spec.md diff --git a/openspec/changes/2026-09-23-save-scope-lock-and-doc-parity/specs/hook-protocol/spec.md b/openspec/changes/archive/2026-09-23-save-scope-lock-and-doc-parity/specs/hook-protocol/spec.md similarity index 100% rename from openspec/changes/2026-09-23-save-scope-lock-and-doc-parity/specs/hook-protocol/spec.md rename to openspec/changes/archive/2026-09-23-save-scope-lock-and-doc-parity/specs/hook-protocol/spec.md diff --git a/openspec/changes/2026-09-23-save-scope-lock-and-doc-parity/tasks.md b/openspec/changes/archive/2026-09-23-save-scope-lock-and-doc-parity/tasks.md similarity index 100% rename from openspec/changes/2026-09-23-save-scope-lock-and-doc-parity/tasks.md rename to openspec/changes/archive/2026-09-23-save-scope-lock-and-doc-parity/tasks.md diff --git a/openspec/specs/doc-behavior-parity/spec.md b/openspec/specs/doc-behavior-parity/spec.md new file mode 100644 index 0000000..76cb240 --- /dev/null +++ b/openspec/specs/doc-behavior-parity/spec.md @@ -0,0 +1,22 @@ +# doc-behavior-parity Specification + +## Purpose +把文档中的可执行断言钉在行为测试上:凡是文档声明的绕过语义、命令形态、默认忽略规则与退出码 +契约,MUST 有测试与真实实现互证,使「文档与行为矛盾」在 CI 现形,而不是依赖会话考古发现。 +## Requirements +### Requirement: Executable documentation claims SHALL be pinned by tests + +文档中可机器判定的行为断言 MUST 有对应测试互证,至少覆盖:绕过豁免的值词表、命令行生态标识 +与别名、点前缀默认忽略语义、markdown 探活命令形态、CVE 退出码契约。断言 MUST 从文档文本与 +实现两侧取值比对(而非只测实现),使文档更新与行为变更脱节时测试变红。 + +#### Scenario: Documentation and behavior diverge + +- **WHEN** 文档中的某条可执行断言(如退出码集合、别名集合)与实现不一致 +- **THEN** 对应测试失败并点名该断言 + +#### Scenario: Documented bypass values match the parser + +- **WHEN** gate 指令文案声明的豁免值词表与 env 解析器接受的词表不一致 +- **THEN** 测试失败 + diff --git a/openspec/specs/hook-protocol/spec.md b/openspec/specs/hook-protocol/spec.md index f8a2809..66034dd 100644 --- a/openspec/specs/hook-protocol/spec.md +++ b/openspec/specs/hook-protocol/spec.md @@ -2,9 +2,7 @@ ## Purpose 定义 CodeGuard 与宿主的事件、输出和退出码契约:区分观察性反馈与 Git 硬门禁,保留 fail-open 的同时明确未验证状态,并将检查绑定到拟提交或推送的内容。 - ## Requirements - ### Requirement: Hook-host contract SHALL live in hooks/__protocol__.md The canonical description of how codeguard-plugin hooks communicate with Codex CLI, ZCode, and Kimi Code (exit codes, JSON output formats, fail-open convention, three-host compatibility) SHALL live in `hooks/__protocol__.md`. No hook script or docstring SHALL contradict it; any change to exit codes or JSON schema SHALL update the document in the same commit. @@ -53,12 +51,12 @@ PreToolUse 硬拦截(exit 2)的 stderr 报告 MUST 包含一条整调用声 ### Requirement: The guard SHALL treat one-level interpreter indirection as guarded -`is_guarded()` MUST 覆盖直接命令与一层解释器间接:解释器(bash/sh/zsh/python/node)执行的脚本文件文本、或 `-c` 内联代码,按与直接命令相同的分隔符切段规则扫描到 `git commit|push` 段首命令词时 MUST 判为拦截。切段扫描前 MUST 先展开 `$(...)` 与反引号内层文本(shell 语义下它们会被真实执行;`ro=$(git push …)` 曾静默放行),段首归一化 MUST 覆盖 shell 控制引导词(`if`/`then`/`else`/`elif`/`while`/`until`/`do`/`!`——`if git push; then`、`for x; do git push; done` 曾静默放行)。`resolve_project_roots()` MUST 用同一套归一化判定收集仓库边界(含 `git -C ` 显式仓边界)——判定不同源时会出现"命中拦截但 roots=[] → 静默放行"的击穿(0.8.2 实测)。更深的动态构造(如 subprocess 参数拼接)MUST 在文档中声明为能力边界而非承诺。 +`is_guarded()` MUST 覆盖直接命令与一层解释器间接。**Shell 解释器(bash/sh/zsh)**:脚本文件文本或 `-c` 内联代码按与直接命令相同的分隔符切段规则扫描到 `git commit|git push` 段首命令词时 MUST 判为拦截;切段扫描前 MUST 先展开 `$(...)` 与反引号内层文本(shell 语义下它们会被真实执行;`ro=$(git push …)` 曾静默放行),段首归一化 MUST 覆盖 shell 控制引导词(`if`/`then`/`else`/`elif`/`while`/`until`/`do`/`!`——`if git push; then`、`for x; do git push; done` 曾静默放行)。**非 Shell 解释器(python/node 等)**:正文 MUST NOT 按 shell 切段归因——模板字符串/帮助文本里的 git 命令字面量样例不是调用(bump-plugin.mjs:164-165 帮助文本实测误报、发版工具被不可绕过地锁死);归因 MUST 只认 subprocess/exec 调用形态(`execFileSync("git", …)`、`subprocess.run(["git", …])`、`os.system("…")` 等调用点的 git 参数位),命中调用形态时 MUST 判为拦截并在 resolve 阶段按「不可建模」UNVERIFIED 阻断。`resolve_project_roots()` MUST 用同一套归一化判定收集仓库边界(含 `git -C ` 显式仓边界)——判定不同源时会出现"命中拦截但 roots=[] → 静默放行"的击穿(0.8.2 实测)。更深的动态构造(如 subprocess 参数拼接)MUST 在文档中声明为能力边界而非承诺。 #### Scenario: A wrapper script performs the commit - **WHEN** 调用形如 `bash runner.sh` 且脚本体内含 `git commit` 或 `git push` -- **THEN** 钥入判定命中,硬门禁按正常流程跑 linter 并可能 exit 2 +- **THEN** 判定命中,硬门禁按正常流程跑 linter 并可能 exit 2 #### Scenario: Command substitution or shell control structure hides the git call @@ -70,6 +68,16 @@ PreToolUse 硬拦截(exit 2)的 stderr 报告 MUST 包含一条整调用声 - **WHEN** 调用为 `echo "git push 是危险命令"` 或脚本内容仅为 `echo hi` - **THEN** 判定不命中,钩子静默放行 +#### Scenario: Help text samples in a non-shell script are not guarded + +- **WHEN** `node scripts/tool.mjs` 的正文含模板字符串帮助文本 `cd && git add -A && git commit -m "x" && git push` +- **THEN** 判定不命中(字面量样例不是调用),钩子放行 + +#### Scenario: A non-shell script really invokes git via subprocess + +- **WHEN** `python3 work.py` 的正文含 `subprocess.run(["git", "push"])` 或 `node run.mjs` 的正文含 `execFileSync("git", ["git commit" …])` 形态的调用 +- **THEN** 判定命中,且 resolve 阶段按「间接 Git 操作不能可靠建模」UNVERIFIED 阻断(不静默放行) + ### Requirement: Soft and hard gates SHALL share the skipGate escape and neither may fall back to scanning outside a git repository UserPromptSubmit 与 PreToolUse MUST 共用同一条仓库级豁免(`git config codeguard.skipGate`);两门在命中豁免时 MUST 记账一次供 Stop 汇总。UserPromptSubmit 在当前目录不属于任何 git 仓库时 MUST 输出一行跳过说明并 exit 0,MUST NOT 把非 git 目录(尤其是多仓工作区根)回退为扫描对象。 @@ -107,3 +115,66 @@ PreToolUse 放行工具故障或无法物化快照时 MUST 使用 JSON additiona #### Scenario: A tool cannot run at the Git gate - **WHEN** 工具缺失或配置导致无法获得检查结论 - **THEN** 保留 exit 0 的兼容放行,并明确未验证而不是 PASS + +### Requirement: Heredoc bodies SHALL be attributed by owner semantics + +命令文本中 heredoc 正文的 git 归因 MUST 按归属语义区分:数据程序 + 引号定界符的正文是全字面量 +(文档样例/模板字符串),MUST NOT 视为 shell 语法切段出 git 副作用;数据程序 + 无引号的正文会做 +命令替换展开,`$(...)` 与反引号跨度的内层文本 MUST 保留扫描(其会被外层真实执行);Shell 解释器 +(bash/sh/zsh)接收的正文是内层 shell 代码,MUST 按既有切段规则建模;python/node 等非 Shell 解释器 +接收的正文不是 shell 语法,MUST NOT 按切段归因。遮蔽处理 MUST 先于命令替换展开执行, +否则引号定界正文的字面 `$(...)` 会被误判为可执行替换。 + +#### Scenario: Sample text in a python heredoc is not guarded + +- **WHEN** `python3 - <<'PY'` 的正文含三引号字符串 `'''cd a && git add && git commit && git push'''`(文档样例) +- **THEN** 判定不命中,钩子放行(此前整调用被拦且写入步骤不执行) + +#### Scenario: Command substitution in an unquoted data heredoc is guarded + +- **WHEN** `cat <