--- 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 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 --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 --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 --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 ``." 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. |