cleanup-feature-flags · v1.2.2 · 2026-10-14 · sha256 0d860d26ac257c98
cleanup-feature-flags v1.2.2A
Immutable. This exact content never changes and is served at /api/v1/blob/0d860d26ac257c98.
---
name: cleanup-feature-flags
description: >-
Remove a launched Harness FME feature flag from application code, keeping the treatment FME
serves today, and open a pull request. Use when asked to remove a flag from code, hardcode the
winning treatment after a rollout, or pay down flag debt for a specific flag. Do not use for
finding stale flags (discover-feature-flags) or archiving and deleting flags in FME
(manage-flag-lifecycle). Trigger phrases: remove flag from code, hardcode treatment, flag
cleanup, flag debt, clean up feature flag.
metadata:
author: Harness
version: 1.2.2
mcp-server: harness-mcp
summary: Remove a launched FME flag from code and open a PR; archive is handed to manage-flag-lifecycle
license: Apache-2.0
compatibility: Requires the Harness MCP server or the Harness CLI
---
# Cleanup Feature Flags
Remove one launched FME flag from application code and keep the branch FME serves today. The work ends with a PR. FME is read-only here; archive happens later in `manage-flag-lifecycle`.
Related: `discover-feature-flags` finds candidates (stale audit); `manage-flag-lifecycle` archives after the change deploys.
## Tools
Works through the Harness MCP server or the Harness CLI; names are from [tool-map.md](../../../references/fme/tool-map.md).
| Operation | MCP | CLI |
|-----------|-----|-----|
| List environments | `harness_list` · `fme_environment` · `compact: false` | `harness list fme_environment --json` |
| Get flag | `harness_get` · `fme_feature_flag` · `params: { feature_flag_name }` | `harness get feature_flag <flag> --json` |
| List flag definitions | `harness_list` · `fme_feature_flag_definition` · `params: { feature_flag_name }` · `filters: { offset: 0, limit: 100 }` · `compact: false` | `harness list feature_flag:definition <flag> --json` |
| List experiments | `harness_list` · `fme_experiment` · `filters: { parent_type: "FEATURE_FLAG", parent_name, status: ["ACTIVE", "PAUSED"], offset: 0, limit: 100 }` (apply [pagination completeness checks](../../../references/fme/tool-map.md#pagination)) · `compact: false` | `harness list experiment --parent-type FEATURE_FLAG --parent-name <flag> --status ACTIVE --json`, then again with `--status PAUSED` |
## Instructions
Load [readiness.md](references/readiness.md) before giving a verdict. Load [sdk-patterns.md](../../../references/fme/sdk-patterns.md) before searching code.
### Phase 1: Establish scope
Follow [scope-establishment.md](../../../references/scope-establishment.md). Fully paginate **List environments** per [pagination](../../../references/fme/tool-map.md#pagination) before confirming the critical environments: by default those with `isProduction`, plus any the user adds. The critical set must be non-empty and explicitly confirmed back to the user before any later phase. If no environment is marked `isProduction` and the user hasn't named any, STOP and ask which environments are critical — do not treat an empty critical set as vacuously ready.
### Phase 2: Pick the flag
If the user named a flag, **Get flag** to confirm it exists and note its `status` and `rolloutStatus`. If they want candidates instead, hand off to `discover-feature-flags` (stale audit) and come back with one flag.
### Phase 3: Forward treatment and verdict
1. Fully paginate **List flag definitions** for the flag with explicit MCP `filters.limit: 100` and advancing `filters.offset` until a short page (`size` is ignored). Incomplete coverage stops code removal; it cannot establish a missing definition or the live forward treatment. Resolve the forward treatment for each critical environment per [readiness.md](references/readiness.md#forward-treatment-per-environment). If any call site uses `WithConfig`, also resolve and compare each critical environment's `configurations` value for that treatment — identical treatment names with differing configs are **blocked**, not ready.
2. Run the [experiment check](../../../references/fme/write-safety.md#experiment-check) with **List experiments**, covering every page of ACTIVE and PAUSED results before a verdict. An incomplete scan stops code removal. Here ACTIVE means **blocked**: code removal ends the experiment. PAUSED means **caution**.
3. Give the verdict per [readiness.md](references/readiness.md#verdict). Stop on **blocked**.
Never infer the forward treatment from the code's default or fallback value.
### Phase 4: Find call sites
In the application repo, identify the SDK and search for the flag key, batch calls, flag sets, config-file entries and tests per [sdk-patterns.md](../../../references/fme/sdk-patterns.md). For each hit, record `file:line`, which branch is the forward one, and any side effects (tracking, metrics, logging).
- If you find **dynamic keys** (`"prefix-" + id`, `` `flag-${id}` ``), the verdict is **blocked**. Stop and ask the user how to proceed; automated removal can't be complete.
- If you find no hits, the flag may live in another repo or only in a flag set. That is **caution**.
### Phase 5: Present the plan and wait
Show:
- the forward treatment for each critical environment and why;
- each call site with the branch to keep and the code to delete;
- the verdict, with every caution reason;
- the last impression for each critical environment.
If the forward treatment is `control` or a killed `defaultTreatment`, say plainly that the newer code path will be deleted. Edit nothing until the user explicitly confirms this plan.
### Phase 6: Edit and verify
1. Work on a new branch in the application repo.
2. At each call site, keep the forward branch and inline the config value if `WithConfig` is used. Remove the dead branches, plus any flag-only constants, wrappers, imports, tests, fixtures, localhost or offline entries, and docs. Don't refactor unrelated code.
3. Search again for the key and for the batch and flag-set patterns. Any remaining hits must be explained: homonyms, other services, or fixtures the user wants to keep.
4. Run the repo's own build, lint and tests. Fix any failures before moving on.
### Phase 7: Open the PR
Confirm with the user before pushing or opening the PR. Use the repo's normal tooling and its own PR template and conventions (for example `.github/pull_request_template.md` or CONTRIBUTING). Don't impose a format. Whatever the template, the description must capture:
- the flag key and the treatment (or config value) that was kept;
- the FME evidence: org and project, critical environments checked, the forward treatment (and config value, if `WithConfig` is used) in each, the verdict and any caution the user acknowledged, the experiment check result, and the last impression in each critical environment;
- what was removed: branches, tests, config, imports;
- how it was verified: the clean search and the build and test run;
- **which repo(s)/service(s) were searched** — a clean search here does not prove the flag is unused elsewhere;
- the follow-up: archive the flag with `manage-flag-lifecycle` once this change is deployed everywhere the flag is evaluated, plus any other repos or services that may still reference it.
### Phase 8: Hand off
Report the PR link and the follow-up: "After this deploys, use `manage-flag-lifecycle` to archive `<flag>`." Don't archive or delete here.
## Examples
- "Remove `new-checkout-flow` from this repo": verdict, plan, confirm, edit, verify, PR, then the archive hand-off.
- "Hardcode the winning treatment for `dark-mode`": resolve the forward treatment from the live definitions, not from the code.
- "This flag is killed in prod, clean it up": the forward treatment is `defaultTreatment`; warn that the new path is deleted.
- "Which flags can we clean up?": hand off to `discover-feature-flags` (stale audit).
- "Archive the flag too": PR here first, then `manage-flag-lifecycle` after the deploy.
## Performance Notes
- One complete paginated **List flag definitions** inventory covers the environments; it may require multiple calls. A page is not the whole inventory.
- Search the flag key first, then the batch and flag-set patterns. Wrappers often hide the literal key.
- Keep the diff to the flag. Unrelated refactors make the PR harder to review and revert.
## Troubleshooting
| Issue | Action |
|-------|--------|
| Critical environments disagree | **blocked**. Ask the user to align targeting (`update-flag-targeting`) or narrow the critical set. |
| An environment has no definition | Its forward treatment is `control`, so the fallback branch is live there. Treat it as **caution** and confirm. |
| Flag is already archived but still in code | Allowed. The forward treatment is `control`; confirm the fallback branch is what users get today. |
| ACTIVE experiment on the flag | **blocked**. Finish it first (`manage-experiments`). |
| Code uses `WithConfig` | Hardcode the treatment's config value from the definition, not just the name. If critical environments share the treatment name but have different config values, that's **blocked**, not ready — hardcoding would silently diverge environment behavior. |
| No explicit critical environment set | **Stop and ask.** Don't default to "ready" on an empty or unconfirmed critical set. |
| Only one repo searched | Record that scope in the PR and summary. Don't claim the flag is fully unused until other repos/services are confirmed or searched. |
| Dynamic flag keys | **blocked**. Stop and ask the user. |
| No call sites found | **caution**. Check other repos and flag sets before claiming the flag is unused. |