首页
/ get-shit-done 配置键白名单机制解析:workflow._auto_chain_active 为何不再被 config-set 拒绝

get-shit-done 配置键白名单机制解析:workflow._auto_chain_active 为何不再被 config-set 拒绝

2026-09-07 15:32:52作者:温玫谨Lighthearted

本文以 get-shit-done(GSD)的一个 changeset 修复(PR #3197)为主体,讲解 gsd-tools config-set workflow._auto_chain_active 从被拒绝到被接受的完整过程:一个内部运行时状态键(RUNTIME_STATE_KEYS)因 SDK 与 CJS 双端 schema 未同步而报出 "Unknown config key",以及修复如何通过三层校验、manifest 单一事实源和 CI 一致性断言从根本上杜绝此类漂移。读完后你能定位 GSD 配置键校验的调用链、理解双端 schema 的同步机制,并复现/验证这一回归测试。

问题背景:一个内部运行时状态键被 config-set 拒绝

workflow._auto_chain_active 是 GSD 的内部运行时状态键(runtime-state key),用于追踪「自主链式执行(autonomous chaining)」是否处于激活状态。它在 GSD 的多个工作流执行链中被反复读写:

  • get-shit-done/references/planning-config.md(第 268 行)中,它被登记为:

    类型 默认值 取值 说明
    workflow._auto_chain_active boolean false true, false Internal: tracks whether autonomous chaining is active
  • 写入方来自多个 workflow。例如 get-shit-done/workflows/discuss-phase/modes/chain.md 中的链式推进步骤会执行 gsd-sdk query config-set workflow._auto_chain_active true / ... false;执行器 agent 也会在 执行器定义 中读取它:AUTO_CHAIN=$(gsd-sdk query config-get workflow._auto_chain_active 2>/dev/null || echo "false")

  • 它参与检查点(checkpoint)的自动放行逻辑:如 get-shit-done/references/checkpoints.md(第 11 行)所述,当 workflow._auto_chain_activeworkflow.auto_advancetrue 时,human-verify 会自动批准、decision 自动选中第一个选项,而 human-action 仍会停止(认证门控无法自动化)。

关键问题在于:这类键以下划线前缀_auto_chain_active)标记为「内部状态」,用户并不期望手动设置它,但它必须能被 config-set 合法写入——否则工作流自身执行到该步骤时就会失败。这正是 PR #3197 要修复的场景。

changeset(.changeset/fix-3197-gsd-tools-config-whitelist.md)记录了缺陷:

gsd-tools config-set workflow._auto_chain_active no longer rejectedworkflow._auto_chain_active is an internal runtime-state key written by plan-phase, execute-phase, discuss-phase, and transition workflows. PR #3162 added it to RUNTIME_STATE_KEYS in the SDK's config-schema.ts but did not mirror the change to the CJS config-schema.cjs used by gsd-tools.cjs. Users routed through gsd-tools.cjs continued to see "Unknown config key" (#3033).

也就是说:#3162 在 SDK 侧把该键加入了 RUNTIME_STATE_KEYS,但没有同步到 CJS 侧的 config-schema.cjs,于是走 gsd-tools.cjs 入口的用户仍然撞上 "Unknown config key"(原始缺陷报告为 #3033)。

配置键校验的三道关卡

GSD 对 config-set <key.path> <value> 的校验并非「非黑即白」,而是由三个集合依次判定。以最典型的 CJS 校验器为例(get-shit-done/bin/lib/config-schema.cjs):

function isValidConfigKey(keyPath) {
  if (VALID_CONFIG_KEYS.has(keyPath)) return true;   // 1. 静态合法键(精确匹配)
  if (RUNTIME_STATE_KEYS.has(keyPath)) return true; // 2. 运行时状态键(#3197 新增的判断)
  return DYNAMIC_KEY_PATTERNS.some((p) => p.test(keyPath)); // 3. 动态键(正则模式)
}

三层语义分别是:

  1. VALID_CONFIG_KEYS — 用户可配置的静态键,精确字符串匹配(如 workflow.auto_advancegit.create_tagmodel_profile)。
  2. RUNTIME_STATE_KEYS — 内部运行时状态键,通常不由用户手设,但工作流需要 config-set 写入。当前集合只有 workflow._auto_chain_active 一项。
  3. DYNAMIC_KEY_PATTERNS — 带命名空间的动态键,用正则匹配,例如 agent_skills.<agent-type>review.models.<cli-name>features.<feature_name> 等。

这三个集合的真实取值都来自单一 manifest sdk/shared/config-schema.manifest.json。其中 runtimeStateKeys 段(第 101–103 行)为:

"runtimeStateKeys": [
  "workflow._auto_chain_active"
]

dynamicKeyPatterns 段则列出了 agent_skillsreview.modelsfeaturesclaude_md_assembly.blocksmodel_profile_overridesmodelsdynamic_routingmodel_overridesreview.max_prompt_tokens_per_reviewer 等正则模式。

isValidConfigKey 全部落空时,CJS/SDK 侧会抛出 Unknown config key: <key>,并附带基于「最长公共前缀」的键名纠错建议(见 sdk/src/query/config-mutation.tsisValidConfigKey,其中 CONFIG_KEY_SUGGESTIONS 先于 LCP 兜底提供更精确的提示)。缺陷的本质就是:workflow._auto_chain_active 落在第二关(RUNTIME_STATE_KEYS)应当命中,但 CJS 侧的 RUNTIME_STATE_KEYS 是空的,于是掉进了 Unknown config key 分支。

根因:SDK 与 CJS 双端 schema 漂移

GSD 存在两条 config-set 的执行路径,历史上各自维护一份 schema 字面量:

路径 入口 校验器 schema 来源(#3197 时期)
CJS get-shit-done/bin/gsd-tools.cjs get-shit-done/bin/lib/config.cjsisValidConfigKey config-schema.cjs 内联
SDK gsd-sdk query config-set sdk/src/query/config-mutation.tsconfigSet config-schema.ts 内联
  • CJS 路径:gsd-tools.cjsconfig.cjscmdConfigSet(第 410 行 if (!isValidConfigKey(keyPath)))→ config-schema.cjsisValidConfigKey
  • SDK 路径:config-mutation.tsconfigSet(第 281 行 const validation = isValidConfigKey(keyPath))→ config-schema.tsisValidConfigKeyPath

PR #3162 只改了 SDK 侧 config-schema.ts,把 workflow._auto_chain_active 加进了 RUNTIME_STATE_KEYS;CJS 侧的 config-schema.cjs 没有跟随。结果就是:同一句 config-set workflow._auto_chain_active true,走 SDK 入口能成功,走 gsd-tools.cjs 入口却报 "Unknown config key"——这就是 #3033 的现象,也是双端 schema 字面量「漂移」的典型后果。

修复方案:把 RUNTIME_STATE_KEYS 纳入 CJS 校验

changeset 明确列出了 #3197 的四处改动,均可在仓库源码中一一对应:

  1. config-schema.cjs 增加 RUNTIME_STATE_KEYS,并与 VALID_CONFIG_KEYS 一同导出get-shit-done/bin/lib/config-schema.cjs):

    module.exports = { VALID_CONFIG_KEYS, RUNTIME_STATE_KEYS, DYNAMIC_KEY_PATTERNS, isValidConfigKey };
    
  2. 更新 isValidConfigKey() 接受运行时状态键——即在 VALID_CONFIG_KEYS 命中之后、DYNAMIC_KEY_PATTERNS 之前,插入 if (RUNTIME_STATE_KEYS.has(keyPath)) return true;第 27 行)。

  3. SDK 的 config-mutation.ts 改为导入并校验同一集合sdk/src/query/config-mutation.ts./config-schema.js 导入 RUNTIME_STATE_KEYS,并在 isValidConfigKey 第 165 行做 RUNTIME_STATE_KEYS.has(keyPath) 判断),使双端判定逻辑对齐。

  4. 新增 CI 一致性断言,确保两侧的 RUNTIME_STATE_KEYS 集合保持同步(见下节)。

修复后,config-set 成功写入的返回值形如(config-mutation.ts 第 446–454 行):

{ data: { updated: true, key: 'workflow._auto_chain_active', value: true } }

值得强调的是取值语义:true/false 会被 parseConfigValue第 208–216 行)强制转换为原生布尔,避免把字符串 "true" 写进 config.json

纵深:manifest 单一事实源与结构性防漂移(#3536)

changeset 描述的 #3197 是「立即修复」,而当前仓库的源码结构显示,随后一次重构(Phase 2 Cycle 5,#3536)把这种漂移从「需要 CI 拦截」升级成了「结构上不可能发生」。从当前源码结构看:

  • manifest 成为唯一事实源sdk/shared/config-schema.manifest.json 同时承载 validKeysruntimeStateKeysdynamicKeyPatterns 三份数据,其 _comment 字段说明:validKeys 是 CJS 与 SDK 两侧并集,二者由 tests/config-schema-sdk-parity.test.cjs 强制集合相等。

  • SDK 侧sdk/src/configuration/index.ts 直接从 manifest 读出 VALID_CONFIG_KEYSRUNTIME_STATE_KEYS

    export const VALID_CONFIG_KEYS: ReadonlySet<string> = new Set(_schemaManifest.validKeys);
    export const RUNTIME_STATE_KEYS: ReadonlySet<string> = new Set(_schemaManifest.runtimeStateKeys);
    

    sdk/src/query/config-schema.ts 已变成一个「薄重导出适配器」,不再含任何内联键字面量。

  • CJS 侧get-shit-done/bin/lib/configuration.generated.cjs 是「GENERATED FILE — DO NOT EDIT」,同样从 manifest 装载:

    const SCHEMA_MANIFEST = loadConfigurationManifest('config-schema.manifest.json');
    const VALID_CONFIG_KEYS = new Set(SCHEMA_MANIFEST.validKeys);
    const RUNTIME_STATE_KEYS = new Set(SCHEMA_MANIFEST.runtimeStateKeys);
    

    get-shit-done/bin/lib/config-schema.cjs 则只是 require 这个生成文件,把三个集合透传出去。

这样一来,#3197 要防的「SDK 加了、CJS 没加」在结构上被消除——两侧都从同一份 manifest 派生,不存在两份可独立漂移的字面量。CI 断言(tests/config-schema-sdk-parity.test.cjs 的「CJS RUNTIME_STATE_KEYS matches manifest runtimeStateKeys exactly」)退化为「确保没人和 manifest 脱钩」的守卫。这也是从源码结构可以推断出的设计意图:与其让每个修复去追平两端,不如把两端收敛到一个数据源。

回归测试与验证

针对本次修复的回归测试是 tests/bug-3197-gsd-tools-config-whitelist.test.cjs,共三条用例,走的是真实的 gsd-tools.cjs(CJS 路径):

// 用例 1:通过 CJS 路径设置 workflow._auto_chain_active=true 成功
const result = runGsdTools(['config-set', 'workflow._auto_chain_active', 'true'], tmpDir);
assert.ok(result.success, `config-set workflow._auto_chain_active true should succeed, got:...`);

// 用例 2/3:设置 true / false 后,断言 .planning/config.json 中
// config.workflow._auto_chain_active 的确切布尔值

三条用例分别验证:(1) 不再被拒绝(result.success 为真);(2) 设置 true 后磁盘 config.jsonworkflow._auto_chain_active === true;(3) 设置 false 后为 false。测试注释直接点明根因——「RUNTIME_STATE_KEYS was added to sdk/…/config-schema.ts in #3162 but not to get-shit-done/bin/lib/config-schema.cjs」,与上文根因分析一致。

配套的一致性守卫 tests/config-schema-sdk-parity.test.cjs 则从 manifest 出发断言:CJS 的 VALID_CONFIG_KEYSRUNTIME_STATE_KEYSDYNAMIC_KEY_PATTERNS 与 manifest 完全相等,且 SDK 的 config-schema.ts 是「重导出壳」而非「重新声明的 Set」。

相关代码路径速查

关注点 路径 说明
本次修复的 changeset .changeset/fix-3197-gsd-tools-config-whitelist.md 主体文档,PR #3197
CJS 入口 get-shit-done/bin/gsd-tools.cjs config-set 命令入口
CJS 校验器 get-shit-done/bin/lib/config.cjs / config-schema.cjs cmdConfigSetisValidConfigKey
CJS 生成源 get-shit-done/bin/lib/configuration.generated.cjs 从 manifest 装载三个集合
SDK 校验器 sdk/src/query/config-mutation.ts configSetisValidConfigKey
SDK schema 适配器 sdk/src/query/config-schema.ts 重导出 + isValidConfigKeyPath
单一事实源 manifest sdk/shared/config-schema.manifest.json runtimeStateKeys 在此
键的登记与语义 get-shit-done/references/planning-config.md / checkpoints.md 类型/默认值/自动放行逻辑
写入方示例 agents/gsd-executor.md / workflows/discuss-phase/modes/chain.md 谁在读写该键
回归测试 tests/bug-3197-gsd-tools-config-whitelist.test.cjs 三条用例走 CJS 路径
一致性守卫 tests/config-schema-sdk-parity.test.cjs 双端集合与 manifest 相等
版本记录 CHANGELOG.md / docs/RELEASE-v1.41.0.md #3197 于 v1.41.0 随版发布

小结

  • 缺陷workflow._auto_chain_active(内部运行时状态键)在 #3162 只被加入 SDK 侧 RUNTIME_STATE_KEYS,未同步到 CJS 侧,导致走 gsd-tools.cjsconfig-set 报 "Unknown config key"(#3033)。
  • 修复(#3197):CJS 侧 config-schema.cjs 增加并导出 RUNTIME_STATE_KEYSisValidConfigKey() 新增运行时状态键判定;SDK config-mutation.ts 对齐校验;新增 CI 一致性断言。
  • 结构演进(#3536)sdk/shared/config-schema.manifest.json 成为 SDK 与 CJS 双端共同的单一事实源,双端集合由其派生,从结构上消除了这类「加一端忘另一端」的漂移。
  • 验证tests/bug-3197-gsd-tools-config-whitelist.test.cjs(功能回归)与 tests/config-schema-sdk-parity.test.cjs(集合一致性)共同保证修复不被回退。
登录后查看全文
热门项目推荐
相关项目推荐