Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ All notable changes to this repository will be documented in this file.

- Add `release-autopilot/launchdarkly-automations-cleanup-settings` and `release-autopilot/launchdarkly-automations-archival-settings` 0.1.0-experimental (Claude Code plugin 1.0.7, Cursor plugin 1.0.8): read and change the auto-cleanup and auto-archival sections of the project Automations page through the hosted MCP tools `get-automations` (`section: "cleanup"` or `"archival"`) and `update-automations` (a `cleanup` or `archival` object), and explain why cleanup did not open a pull request or a flag was not archived. Every write is confirmed first. Turning auto-archival on stays with a person, who confirms its risk on the Automations page. On a server without the tools, both skills guide the browser steps. Onboarding and the settings skill point to the new skills for changes after setup. Pairs with launchdarkly/mcp#462.
- `release-autopilot/launchdarkly-release-autopilot-onboarding` 0.4.0-experimental and `release-autopilot/launchdarkly-release-autopilot-settings` 1.5.0-experimental: each repository's own settings decide what runs in it. On accounts that use the project Automations settings page, the account settings are not read, and a capability a repo does not turn on is off. Accounts that have not moved yet still use the account settings too, so the account tools and the account write stay. Onboarding now turns on each picked capability in the repo write, and the settings skill sends both capabilities on a new mapping and diagnoses a PR from the repo's effective setting and `enabledOverride` instead of stopping at the account setting (EMSR-2650). Bring your own key, auto-cleanup, and auto-archival link to `/projects/{projectKey}/settings/automations` instead of `/settings/vega` and `/settings/lifecycle`, which stay as the fallback on accounts without the Automations page (EMSR-2676). The onboarding prerequisites and README say to install the LaunchDarkly plugin first, with the Claude Code and Cursor steps and the first plugin versions that include the skill (EMSR-2634). Evals check the Automations link and that the mapping turns auto-flagging on.
- `feature-flags/launchdarkly-flag-cleanup` 1.1.0-experimental and `feature-flags/launchdarkly-flag-discovery` 1.1.0-experimental: read LaunchDarkly's code removal verdict from `get-removal-readiness` (`details.lifecycle`). A `blocked` verdict now means only a `not_ready_for_code_removal` blocker: the skills give each reason from `details.lifecycle.codeRemovalBlockers` and name the critical environments where the flag is not `launched` (`details.lifecycle.notLaunchedEnvironments`). When `details.lifecycle.readyToArchive` is `true`, the skills suggest archiving the flag and do not remove code. `caution` is no longer permission to remove code: the skills present each warning and ask the user to confirm first, and for `code_removal_readiness_unavailable` they ask the user to confirm in LaunchDarkly that the flag is launched in every critical environment. When `details.lifecycle.codeAlreadyRemoved` is `true` (every code removal check passes, but LaunchDarkly finds no code references: the code is removed, or it is in a repository LaunchDarkly does not scan), the skills suggest archiving the flag after the cleanup is deployed everywhere. The skills never offer to archive a flag while the repository still references it, and they follow `details.lifecycle.readyForCodeRemoval` instead of applying the default checks again (EMSR-2643).
- `feature-flags/launchdarkly-flag-cleanup` 1.1.0-experimental and `feature-flags/launchdarkly-flag-discovery` 1.1.0-experimental: use the same readiness rule as the LaunchDarkly **Ready for cleanup** view. A flag is ready for code removal only when it is temporary, at least 30 days old, not a prerequisite, has code references, and is `launched` (targeting ON, one variation served to every context, evaluated in the last 7 days, not changed in the last 7 days) in every critical environment. A flag that is OFF in every critical environment is not `launched`, so the cleanup skill no longer hardcodes its `offVariation`: it stops and continues only if the user explicitly confirms that the code behind the other variations is abandoned. Discovery lists inactive flags with no code references as ready to archive, not ready for code removal, and no longer tells users to turn a flag off before removal (EMSR-2643).
- `observability/investigate` 0.3.0 and `observability/alert-investigation` 0.2.0: rewritten around the hosted MCP's current tools (`get-changes`, `get-trace`, `get-error-group`, `search-service-map`, `search-log-patterns`, `get-alert-firing-history`, `get-investigation`, `get-incidents`, `search-aggregations` with `compareTo` and breakdowns). `investigate` adds a "first five minutes" sequence: scope and onset, chronic or noise, what changed, what is over-represented against a baseline (by host, version, route and flag variation), evidence, blast radius; and warns about percentiles over few sampled rows. `alert-investigation` starts from `get-alerts` and `get-alert-firing-history` (replay, chronic check, automatic investigation) instead of alert variables injected into the conversation. The 50-row rule is gone; companions use the stable `search-*` tool names and current arguments. The guides also use the fields from launchdarkly/mcp#449 (firing-history `summary` and `chronic`, get-changes `alert_episode` and evaluated-only flag changes, get-trace `self_ms`/`critical_path`/`top_self_time`, project-wide `search-flag-evaluations` with `include` and the FLAG FILTERS keys, the `low_sample` warning, get-error-group `include` and `_pivots`, `sort: "spike"`, and the `ingest` block with `completeness_note`), each with a fallback for servers that predate it.
- Claude Code plugin 1.0.6, Cursor plugin 1.0.7: Release Autopilot skills and evals use the MCP tool names that match the Automations settings UI: `get-automations`, `update-automations`, and `delete-automations-repo-settings` replace `get-release-autopilot`, `update-release-autopilot`, and `delete-release-autopilot-repo-settings` (`launchdarkly-release-autopilot-settings` 1.4.1-experimental, `launchdarkly-release-autopilot-onboarding` 0.3.1-experimental). Arguments are unchanged. On a server that still exposes the old names, the skills use those; if a call says the tool was removed, they ask the user to reconnect the MCP server. Skill names are unchanged. Pairs with the launchdarkly/mcp rename.
- Claude Code plugin 1.0.4, Cursor plugin 1.0.5: resolve the directory policy finding "Uses a credential from the user's machine". `experiments/launchdarkly-experiment-guide` no longer looks for `LD_API_KEY` or `LAUNCHDARKLY_ACCESS_TOKEN` in the environment; for the REST path it asks the user to paste an API access token for the session. `flag-and-release-change` and `flag-release` no longer say credentials are injected by the environment; they use the user's existing git access and the MCP server's OAuth, and never read tokens from the machine.
Expand Down
4 changes: 2 additions & 2 deletions skills.json
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@
"name": "launchdarkly-flag-cleanup",
"description": "Safely remove a feature flag from code while preserving production behavior. Use when the user wants to remove a flag from code, delete flag references, or create a PR that hardcodes the winning variation after a rollout is complete.",
"path": "skills/feature-flags/launchdarkly-flag-cleanup",
"version": "1.0.0-experimental",
"version": "1.1.0-experimental",
"license": "Apache-2.0",
"compatibility": "Requires the remotely hosted LaunchDarkly MCP server",
"tags": [
Expand Down Expand Up @@ -361,7 +361,7 @@
"name": "launchdarkly-flag-discovery",
"description": "Audit your LaunchDarkly feature flags to understand the landscape, find stale or launched flags, and assess removal readiness. Use when the user asks about flag debt, stale flags, cleanup candidates, flag health, or wants to understand their flag inventory.",
"path": "skills/feature-flags/launchdarkly-flag-discovery",
"version": "1.0.0-experimental",
"version": "1.1.0-experimental",
"license": "Apache-2.0",
"compatibility": "Requires the remotely hosted LaunchDarkly MCP server",
"tags": [
Expand Down
54 changes: 47 additions & 7 deletions skills/feature-flags/launchdarkly-flag-cleanup/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ license: Apache-2.0
compatibility: Requires the remotely hosted LaunchDarkly MCP server
metadata:
author: launchdarkly
version: "1.0.0-experimental"
version: "1.1.0-experimental"
---

# LaunchDarkly Flag Cleanup
Expand All @@ -19,7 +19,7 @@ If you haven't already identified which flag to clean up, use the [flag discover
This skill requires the remotely hosted LaunchDarkly MCP server to be configured in your environment.

**Required MCP tools:**
- `get-removal-readiness`: detailed safety check (orchestrates flag config, cross-env status, dependencies, code references, and expiring targets in parallel)
- `get-removal-readiness`: detailed safety check (orchestrates flag config, cross-env status, dependencies, code references, expiring targets, and LaunchDarkly's own code removal verdict in parallel)
- `get-flags`: fetch flag configuration for a specific environment

**Optional MCP tools:**
Expand Down Expand Up @@ -61,14 +61,49 @@ Use `get-removal-readiness` to get a detailed safety assessment. This single too
- Dependent flags (prerequisites)
- Expiring targets
- Code reference statistics
- LaunchDarkly's code removal verdict from the project's flag lifecycle settings (`details.lifecycle`)

The tool returns a readiness verdict:

**`safe`**: No blockers or warnings. Proceed with removal.
**`safe`**: No blockers or warnings, and LaunchDarkly marks the flag ready for code removal (`details.lifecycle.readyForCodeRemoval` is `true`). Continue to the "Ready for cleanup" check below.

**`caution`**: No hard blockers but warnings exist (e.g., code references in other repos, expiring targets scheduled, flag marked as permanent). Present warnings and let the user decide.
**`caution`**: No hard blockers, but warnings exist (e.g., expiring targets scheduled, `code_already_removed`, or `code_removal_readiness_unavailable`). `caution` is not permission to remove code. Present each warning and ask the user to confirm before you continue. For `code_removal_readiness_unavailable`, the tool could not get LaunchDarkly's code removal verdict: ask the user to confirm in LaunchDarkly that the flag is launched in every critical environment.

**`blocked`**: Hard blockers prevent safe removal (e.g., dependent flags, actively receiving requests, targeting is on with active rules). Present blockers: the user must resolve them first.
**`blocked`**: LaunchDarkly does not consider the flag ready for code removal. The only blocker is `not_ready_for_code_removal`. Present it: the user must resolve it first. Do not remove code.

For a `not_ready_for_code_removal` blocker, tell the user why LaunchDarkly does not consider the flag ready for code removal. The blocker detail and `details.lifecycle.codeRemovalBlockers` give each reason: `prerequisite` (other flags use it as a prerequisite), `notTemporary`, `newerThanMinimumAge`, or `notLaunchedInEveryEnvironment`. `details.lifecycle.notLaunchedEnvironments` names the critical environments where the flag is not `launched`, and `details.lifecycle.environments` gives the lifecycle status in each one.

**If `details.lifecycle.readyToArchive` is `true`, suggest archiving the flag instead.** LaunchDarkly considers the flag inactive in every critical environment and without code references. Do not remove code and do not hardcode a variation.
- If you found no references in Step 1, offer to archive it with `archive-flag`.
- If you found references in Step 1, do not offer to archive it: an archived flag is absent, so that code gets its fallback value. Tell the user that LaunchDarkly reports no code references for this flag, so code references may not be set up for this repository. The flag is not ready for code removal either. Stop.

**If `details.lifecycle.codeAlreadyRemoved` is `true`,** the flag passes every LaunchDarkly check for code removal, but LaunchDarkly finds no code references for it. The flag is launched in every critical environment (or, when the project ignores evaluations for code removal with `ignoreEvalsForCodeRemoval`, fully rolled out and unchanged in every one). The code is already removed, or it is in a repository that LaunchDarkly does not scan. The verdict is `caution` with a `code_already_removed` warning.
- If you found no references in Step 1, the code removal is done. Offer to archive the flag with `archive-flag` only after the user confirms that the cleanup is deployed everywhere the flag is evaluated. Until then, SDKs that run the old code still evaluate the flag and would get their fallback value.
- If you found references in Step 1, do not offer to archive it. The flag passes every other lifecycle check, so continue with code removal. Tell the user that code references may not be set up for this repository.

#### Also apply LaunchDarkly's "Ready for cleanup" rule

The verdict alone is not enough. Use the same rule as the **Ready for cleanup** view in the LaunchDarkly flag list. A flag is ready for code removal only when all of these are true:

1. The flag is **temporary**.
2. The flag was **created at least 30 days ago**.
3. **No other flag uses it as a prerequisite.**
4. The flag is **`launched` in every critical environment**. `launched` means:
- targeting is **ON**,
- every context gets the **same single variation** (no percentage rollout, and no rules or individual targets that serve a different variation),
- an SDK **evaluated the flag in the last 7 days**, and
- the flag's targeting was **not changed in the last 7 days**.
5. The flag has **code references**. This is true here, because you found them in Step 1.

The day counts are the defaults. A project can change them, and it can turn off the temporary, prerequisite, and single-variation checks, in its flag lifecycle settings.

`details.lifecycle.readyForCodeRemoval` from `get-removal-readiness` is LaunchDarkly's result for this rule, with the project's own lifecycle settings and critical environments. Follow that result. Do not apply the default day counts or checks again, because the project can change them. `details.lifecycle.environments` gives the lifecycle status in each critical environment.

You cannot check evaluations or lifecycle status yourself: `get-flags` returns targeting, not evaluations. If `details.lifecycle` is missing or `details.lifecycle.unavailable` is `true`, stop. Ask the user to confirm in LaunchDarkly's **Ready for cleanup** view that the flag is listed, and continue only after they confirm. Use `get-flags` only to read targeting (ON or OFF, and the variation served) in each critical environment.

**A flag that is OFF in a critical environment is not `launched`, so it is not ready for code removal.** This is true even when every critical environment is OFF and serves the same `offVariation`. A flag that is off everywhere often guards a feature that was never released. If you hardcode the `offVariation`, you delete the code for that feature. LaunchDarkly lists a flag that is off everywhere, has had no evaluations recently, and has no code references as **ready to archive**, not as ready for code removal.

If `details.lifecycle.readyForCodeRemoval` is `false`, or targeting is OFF in a critical environment, stop and tell the user why, even when the verdict is `safe`. One exception: if `details.lifecycle.codeAlreadyRemoved` is `true` and you found references in Step 1, continue. LaunchDarkly passes every other check for this flag, and `readyForCodeRemoval` is `false` only because LaunchDarkly does not scan this repository. Do not offer to continue unless the user explicitly asks to remove the flag anyway. For a flag that is off everywhere, continue only if the user explicitly confirms that the code behind the other variations is abandoned and must be deleted. Record that confirmation in the PR description.

### Step 3: Determine the Forward Value

Expand All @@ -77,7 +112,7 @@ Use `get-flags` to fetch the flag configuration in each critical environment. Th
| Scenario | Forward Value |
|----------|---------------|
| All critical envs ON, same fallthrough, no rules/targets | Use `fallthrough.variation` |
| All critical envs OFF, same offVariation | Use `offVariation` |
| All critical envs OFF, same offVariation | **NOT READY**: the flag is not `launched` (see Step 2). Stop and inform the user. Use `offVariation` only after the user explicitly confirms that the code behind the other variations is abandoned. |
| Critical envs differ in ON/OFF state | **NOT SAFE**: stop and inform the user |
| Critical envs serve different variations | **NOT SAFE**: stop and inform the user |

Expand All @@ -88,7 +123,7 @@ Before modifying any code, present a summary to the user and wait for confirmati
1. **The forward value** — which variation will be hardcoded and why (based on the flag's current state).
2. **All code references found** — file paths and line numbers from Step 1.
3. **Planned changes** — for each reference, describe what will be removed and what will be kept.
4. **Readiness verdict** — the result from `get-removal-readiness` (safe, caution, or blocked) and any warnings.
4. **Readiness verdict** — the result from `get-removal-readiness` (safe, caution, or blocked), any warnings, and whether the flag meets the "Ready for cleanup" rule in Step 2. If the flag is off in every critical environment, say so and say that the user confirmed the code behind the other variations is abandoned.
5. **LaunchDarkly action** — confirm the flag will be archived after code changes are complete.

**Do not proceed with code changes until the user explicitly confirms.**
Expand Down Expand Up @@ -152,6 +187,10 @@ Before considering the job done:
|-----------|--------|
| Flag not found in LaunchDarkly | Inform user, check for typos in the key |
| Flag already archived | Ask if code cleanup is still needed (flag is gone from LD but code may still reference it) |
| Flag is OFF in every critical environment | Not ready for code removal (not `launched`). Stop and tell the user. Continue with `offVariation` only if the user explicitly confirms that the code behind the other variations is abandoned |
| `blocked` with `details.lifecycle.readyToArchive: true` | Do not remove code. Suggest archiving the flag with `archive-flag` only if you found no references in Step 1 |
| `details.lifecycle.codeAlreadyRemoved: true` and no references found | Code removal is done. Offer to archive the flag with `archive-flag` after the user confirms the cleanup is deployed everywhere |
| `caution` with `code_removal_readiness_unavailable` | Do not remove code until the user confirms in LaunchDarkly that the flag is launched in every critical environment |
| Multiple SDK patterns in codebase | Search all patterns: `variation()`, `boolVariation()`, `variationDetail()`, `allFlags()`, `useFlags()`, plus any wrappers |
| Dynamic flag keys (`flag-${id}`) | Warn that automated removal may be incomplete: manual review required |
| Different default values in code vs LD | Flag as inconsistency in the PR description |
Expand All @@ -162,6 +201,7 @@ Before considering the job done:
- Don't change code unrelated to flag cleanup.
- Don't refactor or optimize beyond flag removal.
- Don't remove flags still being actively rolled out.
- Don't treat a flag that is OFF everywhere as ready for removal. It is not `launched`, and it may guard a feature that was never released.
- Don't guess the forward value: always query LaunchDarkly.

## After Cleanup
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "launchdarkly-flag-cleanup",
"description": "Safely automate feature flag cleanup workflows using LaunchDarkly MCP server",
"version": "1.0.0-experimental",
"version": "1.1.0-experimental",
"author": "LaunchDarkly",
"repository": "https://github.com/launchdarkly/ai-tooling",
"skills": ["./"],
Expand Down
Loading
Loading