mcpField
F
flag-cleanup

flag-cleanup

github.com/growthbook开发2541 次安装4 周前更新
代码仓库

关于 flag-cleanup

供 Claude Code 使用的技能,用于归档或永久删除 GrowthBook 过时功能标志。它会检查依赖和行为变化、查找代码引用,并在删除前引导清理代码。

主要功能

  1. 01归档标志或永久删除
  2. 02检查规则、实验和草稿版本
  3. 03比较归档前后的行为
  4. 04通过 Code References 或 Grep 查找引用
  5. 05删除前引导修改代码

适用场景

  1. 01确认标志不再需要后将其退役
  2. 02在代码库中查找并替换标志引用
  3. 03清理前检查生产环境行为变化

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/growthbook/skills --skill flag-cleanup

安装后在 Claude Code 中输入/flag-cleanup确认已加载。

详细介绍

flag-cleanup

归档或删除过时的功能标志。有两种方式:归档(可撤销,软禁用)或删除(永久操作;始终先经过归档这一安全关卡)。此技能会协调代码清理与 Agent 的通用 Read/Edit 工具;如果已配置 GrowthBook 的 Code References API,则会显示其中的调用位置,否则会通过 Grep 搜索用户当前目录。

所有 API 调用都通过随附的辅助脚本完成:${CLAUDE_PLUGIN_ROOT}/scripts/gb-call。它需要 GB_API_KEY——在 shell 中设置,或由 /growthbook:gb-setup 写入 ~/.config/growthbook/.env。如果未设置或无效,gb-call 的错误消息会提示你返回 /growthbook:gb-setup。

必需输入

开始前向用户收集以下信息。提示用户补充缺失的信息。

  • Flag ID — kebab-case 键。如果用户提供的是描述,先转到 flag-search 进行解析。
  • 操作 — archive(可撤销)或 delete(永久操作;包括归档)。根据措辞推断: “archive”/“disable” → archive;“delete”/“remove”/“clean up”/“get rid of” → delete。执行变更前请确认。

工作流

使用此清单跟踪进度。请勿跳过步骤或调整顺序;每一步都是下一步的前置条件。

- [ ] 1. Fetch flag, verify safety preconditions (incl. bulk experiment check)
- [ ] 2. Compute defaultValue + detect behavior-change divergences
- [ ] 3. Find call sites (Code References API → Grep fallback w/ cwd check)
- [ ] 4. Walk the user through inlining, batched by file
- [ ] 5. Archive the flag (revision-and-publish; branch to 5a on 403, 5b on 409)
- [ ] 6. Verify-or-rollback gate (delete path only)
- [ ] 7. Delete the flag (delete path only)
- [ ] 8. Report, including reverse-prerequisite limitation warning
1. 获取功能标志并验证安全前提条件
gb-call GET /api/v2/features/<flag-id>

从响应中提取:archived(布尔值)、defaultValue、valueType、environmentSettings(哪些环境已启用)、rules(完整数组,包括任何 experiment-ref 规则)、holdout(仅供参考)、project。

如果返回 404,请停止:“没有 ID 为 <flag-id> 的功能标志。”建议使用 flag-search 列出功能标志。

运行安全检查。如果触发以下任一项,请停止:

  • archived === true,且用户要求归档 → 已归档。请重新确认意图(“你说要归档;此功能标志已经归档——你是想删除它吗?”),或直接正常退出。

  • archived === true,且用户要求删除 → 这是之前清理会话的后续操作(或用户已在 UI 中归档)。询问:

    此功能标志已归档——看来你是在继续之前未完成的清理。你已经在代码库的调用位置内联了该功能标志的值吗?

    • 是 → 我会跳过行为变更检查和代码清理流程;我们直接进入删除前的验证或回滚关卡。
    • 否 → 我仍会先带你完成第 2–4 步,因为代码中可能还存在对此功能标志的引用。

    如果回答是,跳到第 6 步(由于 archived: true → true 不会更改服务端状态,因此第 5 步的归档操作不执行;当 feature.archived 与请求的值相同时,处理程序会提前返回)。

  • 此功能标志设置了 neverStale: true。通过过时状态端点确认:

    gb-call GET '/api/v2/stale-features?ids=<flag-id>'
    

    如果响应包含 staleReason: "never-stale",请停止:

    此功能标志已标记为 neverStale——有人明确表示不应自动清理它(例如紧急关闭开关、运维切换项、许可证限制等)。如需继续,请先在 GrowthBook UI 的 <host>/features/<flag-id> 中移除 neverStale 标记,然后重新运行我。

  • 存在指向 running 状态实验的有效 experiment-ref 规则。一次批量调用即可覆盖常见情况:

    gb-call GET '/api/v1/experiments?trackingKey=<flag-id>&status=running'
    

    这是可行的,因为按照惯例,experiment-launch 会将 trackingKey === flag-id。如果响应数组非空,请停止:

    此功能标志关联到实验 <exp-id>(<exp-name>),该实验仍在运行。请先停止该实验(使用 experiment-stop),然后重新运行我。

    防御性复查。 如果批量调用返回空数组,但功能标志的 rules 数组中存在 experiment-ref 规则,则该实验可能是手动关联的,且 experimentId ≠ trackingKey。逐条遍历 experiment-ref 规则,获取每个实验作为备用检查:

    gb-call GET /api/v1/experiments/<experiment-id-from-rule>
    

    如果任何一个返回 status: "running",请使用相同消息停止。 临时发布检查。 获取关联实验时,也要留意任何满足 status: "stopped" 且 enableTemporaryRollout: true 的实验——这不会阻止清理(实验已停止),但步骤 2 必须使用胜出值作为内联替换值。提醒用户:

    “实验 <exp-id> 正在进行临时发布——当前所有用户看到的都是胜出值。步骤 2 将使用胜出值作为内联替换值,而不是 defaultValue。”

  • 引用了该标志的草稿实验。 优先级低于运行中的实验,但值得警告并确认。使用相同的批量查询,但更改状态筛选条件:

    gb-call GET '/api/v1/experiments?trackingKey=<flag-id>&status=draft'
    

    如果结果非空,则发出警告:

    注意:此标志是草稿实验 <exp-id> 的 trackingKey。删除该标志不会立即造成问题,但会导致该实验无法成功启动。是否继续?

  • 该标志存在未解决的草稿修订。 归档会丢弃草稿:

    gb-call GET /api/v2/features/<flag-id>/revisions/latest
    

    如果返回 404,则不存在草稿——继续。如果返回了草稿,则停止:

    此标志上有一个处于活动状态的草稿修订(<version>)。清理此标志会丢弃这些待处理的更改。请先在 GrowthBook UI 的 <host>/features/<flag-id> 上发布或丢弃草稿。

  • /stale-features 未将其标记为陈旧,且用户要求删除。 发出警告,但不要停止:

    提醒:GrowthBook 未将此标志标记为陈旧。它仍在 <env> 中启用,且具有活动规则。是否继续?

2. 计算内联替换值并检测行为变化

归档后,所有规则都将停止生效——该标志会向所有调用方返回 defaultValue。内联替换值通常是 defaultValue,但并非总是如此——请参阅下方的临时发布情况。

先检查是否存在活动的临时发布。 对于标志中的每条 experiment-ref 规则,获取关联实验:

gb-call GET /api/v1/experiments/<experiment-id>

如果 experiment.status === "stopped" 且 experiment.enableTemporaryRollout === true:

  • 实验的 releasedVariationId 会告诉你哪个变体正在为 100% 的流量提供服务。

  • 在 experiment-ref 规则的 variations 数组中找到该变体的值。

  • 所有用户当前看到的都是该值,而不是 defaultValue。

  • 将其用作内联替换值。向用户明确发出警告:

    “此标志正在进行临时发布。当前所有用户看到的都是胜出值 <winner_value>。清理后,所有用户都会切换到 defaultValue: <default_value>。如果两者不同,正确的替换值是内联写入 <winner_value>,而不是 <default_value>。”

对于 valueType: "json",展示原始的 JSON 编码字符串,并让用户自行调整内联结构。

这一步要回答的核心问题是:清理是否会改变生产环境中的行为?

遍历 rules 数组,并标记之前与 defaultValue 不同的情况:

  • 向目标细分群体提供不同值的 force 规则。
  • 活动的 rollout 规则(coverage > 0)——即使没有条件也要标记。
  • experiment-ref 规则:获取关联实验。如果实验已停止且正在临时发布,则胜出值就是差异所在(如上所述)。如果实验仍在正常分配流量(实验已停止,但没有临时发布),用户会分配到不同变体——归档会使所有人都切换到 defaultValue,这会改变处理组用户的行为。

展示一个表格:

Behavior-change check for <flag-id>:
  defaultValue (post-cleanup):  "true"

  Pre-cleanup divergences (these stop applying after archive):
    production:  rule 1 (force, "beta testers" saved-group) served "false" → those users will now get "true"
    production:  rule 2 (rollout, 25% via id)              served "false" → 25% of traffic will shift to "true"
    staging:     no divergence — all traffic gets "true" today

Is this the intent?

如果表格为空(没有差异),则此次清理纯属形式上的操作,用户可以继续,无需进一步确认。如果任何规则存在差异,则停止并确认——这会导致实际的行为变化。

3. 查找调用位置

优先尝试 Code References:

gb-call GET /api/v1/code-refs/<flag-id>

响应格式为 {codeRefs: [{ repo, branch, platform, refs: [{filePath, startingLineNumber, lines, flagKey}] }, ...]}。每个(repo,branch)对应一份文档——如果 Code References 已从多个分支推送,同一个功能可能会有多条记录。

  • 响应非空 → 按文件分组,向用户展示行级引用;如果涉及多个分支,则附上分支信息。注明数据来自 Code References(让用户知道这些数据可能与本地编辑相比已经过时)。

  • 响应为空 → 可能是未配置 Code References,也可能是该标志确实没有被引用。在回退到 Grep 之前,确认工作目录:

    Code References 未返回任何结果。我会在当前目录(<pwd>)中搜索 <flag-id>。使用此标志的代码是否位于此处?还是应该搜索其他位置?

    确认后,使用 Grep 工具在项目中搜索标志 ID(kebab-case 键,精确匹配)。仅搜索相关文件类型(源代码,不包括 vendored 目录)。

如果 Code References 和 Grep 都未返回结果,则提示:

找不到 <flag-id> 的任何代码引用。如果你确定该标志没有在任何地方使用,请继续执行步骤 5。否则,请再次确认工作目录,或查看 <host>/features/<flag-id> 中的 Code References,检查是否有过时数据。

4. 引导用户内联替换

按文件批量处理,而不是按行处理。 一个文件中可能有多处引用同一标志;要求用户在同一个文件中分别批准 N 次 Edit 会带来不便。对于每个包含引用的文件:

  • 使用 Read 工具加载完整文件(一次即可)。
  • 展示每个文件的摘要:文件路径、引用数量以及引用该标志的行。
  • 提议使用一次 Edit,将文件中的所有引用替换为 defaultValue(或用户确认的内联值)。
  • Edit 将通过 Claude Code 的常规权限流程执行——用户审核并批准整份文件的更改即可。
  • 用户也可以说“跳过此文件”(稍后处理)或“只编辑一部分”(仅对此文件改为逐条引用执行 Edit)。

跟踪已处理和已跳过的文件。用户表示“内联处理完成”(或没有调用位置)后,继续执行。

如果用户希望完全推迟代码清理(“现在只归档标志,我之后再清理代码”),允许这样做——归档可以撤销,用户可以稍后重新运行此技能,在删除之前完成代码清理。

5. 归档标志
echo '{"archived": true}' \
  | gb-call POST /api/v2/features/<flag-id> -

服务端会发生什么。 这不是元数据补丁。设置 archived 会在服务端触发 createAndPublishRevision——创建并原子发布一个新版本。适用于任何 v2 发布的故障模式同样适用:

  • 2xx → 已归档。继续执行步骤 6(删除路径)或步骤 8(仅归档路径)。
  • 403,响应正文包含 "approval required" → 执行步骤 5a。
  • 409 → 执行步骤 5b(合并冲突;在我们发出 GET 和归档 POST 之间,另一位参与者更改了该标志)。
  • 其他 4xx → 停止并显示响应正文。

归档事件会出现在版本历史记录中——在步骤 8 中展示此信息。

5a. 归档需要审批

与 flag-targeting 相同的三选一分支,但归档和删除之间有一个关键的不对称之处:

你的组织要求在归档此标记前先获得审批。要继续:

A. 标准审核流程(推荐)——我会为此更改申请审核;团队成员会在 GrowthBook UI 的 <host>/features/<flag-id> 页面批准;你重新运行我即可继续。

B. 组织范围的绕过设置——管理员在 Settings → General → Approvals 中启用“REST API 始终绕过审批要求”。此设置同时授权归档和最后的删除步骤。

C. 按令牌绕过——使用具有 bypassApprovalChecks 权限的 PAT。**此权限授权归档,但不授权删除。**按令牌设置的权限仅用于绕过审核工作流,不会覆盖破坏性操作限制。如果你的最终目标仅是归档,这种方式可行。如果你想删除,仍需要选项 B 中的组织范围设置(或由管理员在 UI 中删除)。在用户选择 C 之前,明确告知这一不对称之处——不要让他们到第 7 步才发现。

对于路径 A,为草稿申请审核并停止:

echo '{"comment":"Auto-requested by flag-cleanup"}' \
  | gb-call POST /api/v2/features/<flag-id>/revisions/<version>/request-review -

不要尝试 submit-review——API 会拒绝自我审批。

5b. 归档时发生合并冲突

归档 POST 返回 409 表示草稿的基础版本已过期——在我们第 1 步(GET)和第 5 步(POST)之间,团队成员(或另一次 Agent 调用)发布了对此标记的更改。**不要自动变基。**停止并提示:

无法应用对 <flag-id> 的归档——此标记自我上次查看后已发生更改。请按以下步骤解决:

  • 在 GrowthBook UI 中打开 <host>/features/<flag-id>。
  • 协调此更改(将待处理的归档变基到最新版本之上,或放弃此次更改并重新运行我,以基于新的实时状态重新开始)。

冲突解决后,重新运行我。

原样展示冲突响应正文。POST /api/v2/features/<id>/revisions/<version>/rebase 端点可用于选择性解决冲突;v1 会出于与 flag-targeting 相同的原因保持谨慎——解决合并冲突需要人工判断每个冲突字段。

6. 验证或回滚关卡(仅适用于删除路径)

归档成功后,停止并向用户确认。在提示词中明确说明删除不可逆,而不只是放在用户可能没看过的防护措施中:

此标记已归档。目前此操作完全可逆——在 UI 中取消归档(或回复“rollback”),一切都会恢复原状。一旦删除,此标记及其所有修订版本都将永久消失,无法撤销。

删除前:

  • 检查代码库中是否有任何内容因此失效。运行测试;部署到暂存环境;验证应用是否能在使用 defaultValue 替代旧规则时正确运行;或者执行你们通常在移除标记后进行的检查。
  • 确认无误后,回复“proceed”,我就会删除。
  • 回复“rollback”,我就会取消归档。

这是此技能的关键关卡。永久删除是一条单向路,用户应至少经历一个验证周期,暂时保持标记已归档但未删除的状态。即使用户催促,也不要将这两个步骤合并。

**如果用户选择“rollback”:**通过同一端点设置 archived: false,撤销归档:

echo '{"archived": false}' \
  | gb-call POST /api/v2/features/<flag-id> -

此操作与归档一样,会经过相同的修订和发布流程——同样可能遇到需要审批和合并冲突的故障模式。确认操作顺利完成,告知用户结果,然后结束。

7. 删除标记(仅删除路径)
gb-call DELETE /api/v2/features/<flag-id>
  • 2xx,且包含 {deletedId} → 继续汇报。
  • 403 → 几乎可以确定是 restApiBypassesReviews 设置已关闭。该标记已归档,只是无法通过 API 删除。请向用户说明:

    贵组织要求先启用“REST API 始终绕过审批要求”设置,才能通过 API 删除标记。该标记已归档;你可以:

    • 请管理员启用该设置(设置 → 常规 → 审批),然后重新运行我以完成删除。
    • 在 GrowthBook UI 中手动删除,地址为 <host>/features/<flag-id>(归档的标记仍会列在那里)。

    按令牌设置的 bypassApprovalChecks 并不能授权此操作——它有意只用于绕过审查工作流,而非覆盖破坏性操作限制。

  • 其他 4xx → 停止并显示响应正文。

服务端清理。 删除会移除功能记录、所有修订、所有修订日志,并解除所有在 linkedFeatures 中关联了此标记的实验。实验本身不会被删除——它们会继续存在,但跟踪键已失效。请在第 8 步说明这一点,让用户知道这些实验仍然存在。

8. 汇报

打印摘要:

  • 标记 ID 和最终状态(archived 或 deleted)。
  • 对于归档:已创建修订历史记录(用户的审计日志中会显示该记录)。
  • 对于删除:已解除关联的实验(但未删除);它们的跟踪键现在指向一个不存在的标记。
  • 找到的调用位置数量、已编辑的文件数量,以及跳过的调用位置。
  • 使用的内联替换值(该标记的 defaultValue),以便用户了解具体情况。
  • UI 链接:<host>/features/<flag-id>——仅对已归档的标记有用(已删除的标记会返回 404)。将 GB_API_URL 中的 api. 替换为 app.,据此推导 <host>。
  • 限制警告:“请注意:我无法检测其他标记是否将此标记用作前置条件。如果不确定,请在删除前检查 GrowthBook UI 中的‘使用者’面板;也可以先归档、再删除前检查,因为归档操作可撤销。”
  • 保留组警告(如果存在 feature.holdout):“此标记关联了保留组 <holdout-id>。该保留组的 linkedExperiments 可能仍引用指向此标记的实验;这些实验的跟踪键现在已失效。如果实验分析依赖保留组状态完整,请在 GrowthBook UI 中审查该保留组。”
  • 建议的后续操作:重新运行 Code References(CLI/Action),以便控制面板反映清理后代码库的状态。

Guardrails

  • 对线上标志,规范流程是在删除前先归档。 除非组织范围内设置了 restApiBypassesReviews,否则后端会拒绝删除未归档的功能。即使启用了该设置,为了留出安全确认时间,也应优先采用「归档→验证→删除」。
  • 归档会触发修订并发布。 通过 POST /v2/features/<id> 设置 archived: true,会在服务端创建并发布一个修订。适用于任何 v2 发布的故障模式同样适用,包括需要审批(5a)和合并冲突(5b)。
  • bypassApprovalChecks 对归档和删除的作用并不对称。
    • 归档:按令牌设置的 bypassApprovalChecks 有效(updateFeatureV2.ts 会检查组织范围和按令牌设置)。
    • 删除:按令牌设置的 bypassApprovalChecks 无效(deleteFeature.ts 中有明确注释:「绕过审核工作流,不是覆盖破坏性操作限制」)。
    • 只有组织范围内的 restApiBypassesReviews 才能授权这两种操作。向用户展示审批选项时,不要混淆二者。
  • neverStale: true 标志是有意设为永久的。 如果用户要求清理此类标志,请暂停操作;用户需要先在 UI 中明确移除此标志。
  • 正在运行的实验会阻止清理。 如果某个标志的 experiment-ref 规则指向一个状态为 running 的实验,则不能清理该标志;停止实验是另一项决策,应交由 experiment-stop 处理。
  • 未解决的草稿会阻止清理。 显示该草稿,并先询问用户是要丢弃还是发布。
  • 永久删除不可逆。 归档和删除之间应暂停操作,让用户确认没有任何问题。即使用户催促,也不要合并这两个步骤。在面向用户的提示中说明操作不可逆,而不只是写在本指南中。
  • 代码清理需要按文件审批,并应分批处理。 按文件归类调用点,每个文件提议一次 Edit。不要跨文件批量处理;不要绕过 Agent 正常的 Edit 工具权限流程。
  • Code References 可能已过期。 数据反映的是上次 CLI/GitHub Action 运行时的情况,而非当前工作树。如果用户表示有些编辑尚未反映在 Code References 中,应以工作树为准(或重新运行 CLI)。
  • /code-refs 为空不代表「代码中未使用」。 可能是尚未设置 Code References,也可能是用户的工作目录不是正确的代码库。在认定标志没有被引用之前,务必先确认工作目录。
  • 一次批量查询实验胜过按规则逐个查询 N 次。 首选使用 listExperiments?trackingKey=<flag-id>&status=running;仅当批量调用返回空结果但存在 experiment-ref 规则时,才按规则逐个获取(处理手动配置的边缘情况)。
  • 归档后,所有规则都会停止计算——调用方将获得 defaultValue。 第 2 步的目的不是计算每个用户的行为,而是通过列出与 defaultValue 不同的规则,检测清理是否会改变生产环境中的行为。
  • 反向依赖检测不在范围内。 API 未提供「哪些内容依赖此标志」的反向查询功能。应在报告中说明这一限制,不要默默漏掉这种情况。
  • 删除时不会明确清理 Holdout 关联。 功能删除处理程序不会更新 Holdout 记录。如果删除的标志关联了 Holdout,其 linkedExperiments 可能会残留过期引用。当存在 feature.holdout 时,应在报告中提醒用户。
  • 删除会解除实验关联,但不会删除实验。 曾引用此标志的实验会继续存在,其 trackingKey 值会过期。在删除流程的报告中提及这一点。
  • 删除后不要推送更新后的 Code References。 CLI/Action 会在下次扫描代码库时自然执行此操作。提前清除引用可能会掩盖代码实际上尚未更新、仍处于损坏状态的情况。

使用的端点

  • GET /api/v2/features/<id> — 获取标志状态(规则、defaultValue、archived、environmentSettings、holdout、project)
  • GET /api/v2/stale-features?ids=<id> — 确认是否过时 + 检查 neverStale 原因
  • GET /api/v1/experiments?trackingKey=<flag-id>&status=running(以及 &status=draft)— 批量查找关联到此标志的实验
  • GET /api/v1/experiments/<id> — 针对手动关联这一边缘情况进行防御性交叉检查的回退方案
  • GET /api/v2/features/<id>/revisions/latest — 检测尚未解决的草稿(404 表示没有)
  • GET /api/v1/code-refs/<id> — 如果已配置 Code References,则获取行级调用位置;为空时返回 {codeRefs: []}(而不是 404)
  • POST /api/v2/features/<id> — 归档(请求体:{archived: true})或取消归档({archived: false});触发修订并发布
  • DELETE /api/v2/features/<id> — 永久删除;对于线上标志,需要 restApiBypassesReviews(未启用时会被拒绝)
  • POST /api/v2/features/<id>/revisions/<version>/request-review — 仅在 5a 审批分支中使用

交接

  • flag-search — 在清理前查找并审计过时标志(flag-search 中的 Path C 是自然的调用方)。
  • flag-graph — 在归档或删除标志之前,检查依赖该标志的内容。
  • flag-targeting — 如果用户实际上想改变标志的行为,而不是移除它。
  • flag-revisions — 检查必须在归档前解决的未完成草稿。
  • flag-publish — 处理归档时需要审批(5a)和合并冲突(5b)这两个分支。
  • experiment-stop — 清理关联到运行中实验的标志前必须先执行。
  • 手动 UI 步骤(无技能):取消归档已归档但尚未删除的标志 — 在 <host>/features/<flag-id> 完成。技能的回滚步骤适用于当前会话中的情况;在会话之外,用户需改用 UI。

评论

支持 Markdown