diff --git a/README.md b/README.md index add15c2..01c9b94 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ Agent Skills are modular, text-based playbooks that teach an agent how to perfor | `feature-flags/launchdarkly-flag-targeting` | Control targeting, rollouts, rules, and cross-environment config | | `feature-flags/launchdarkly-flag-cleanup` | Safely remove flags from code using LaunchDarkly as the source of truth | | `feature-flags/launchdarkly-guarded-rollout` | Configure guarded rollouts with progressive traffic, metric monitoring, and rollback | +| `feature-flags/flag-and-release-change` | Gate a PR's change behind a flag and set up its automated release (the "apply" step after `should-flag-change`) | ### AgentControl diff --git a/skills.json b/skills.json index d635a55..dd12e51 100644 --- a/skills.json +++ b/skills.json @@ -187,6 +187,26 @@ "license": "Apache-2.0", "compatibility": "Requires SDK installed (parent Step 5) and LaunchDarkly project access" }, + { + "name": "flag-and-release-change", + "description": "Gate a pull request's change behind a LaunchDarkly feature flag and set up its automated release. Use once a change has been judged flag-worthy (e.g. by should-flag-change) to create the flag, wire the new code path behind it on the PR branch, and register an automated rollout so the change releases safely when the PR merges. Keywords: flag a PR, wrap change in a flag, dark launch, kill switch, auto-release, automated rollout, release policy, staged rollout.", + "path": "skills/feature-flags/flag-and-release-change", + "version": "0.1.0", + "license": "Apache-2.0", + "compatibility": "Requires the remotely hosted LaunchDarkly MCP server and a git CLI with access to the PR's repository", + "tags": [ + "launchdarkly", + "feature-flags", + "feature-management", + "flag-creation", + "pull-request", + "auto-release", + "automated-rollout", + "release-policy", + "dark-launch", + "mcp" + ] + }, { "name": "investigate", "description": "Analyzes observability data \u2014 logs, traces, errors, sessions, and metrics \u2014 to find root cause and actionable evidence. Use when the user reports a bug, an unexpected behavior, or asks about patterns across application data.", diff --git a/skills/feature-flags/flag-and-release-change/SKILL.md b/skills/feature-flags/flag-and-release-change/SKILL.md new file mode 100644 index 0000000..f593ed1 --- /dev/null +++ b/skills/feature-flags/flag-and-release-change/SKILL.md @@ -0,0 +1,106 @@ +--- +name: flag-and-release-change +description: "Gate a pull request's change behind a LaunchDarkly feature flag and set up its automated release. Use once a change has been judged flag-worthy (e.g. by should-flag-change) to create the flag, wire the new code path behind it on the PR branch, and register an automated rollout so the change releases safely when the PR merges. Keywords: flag a PR, wrap change in a flag, dark launch, kill switch, auto-release, automated rollout, release policy, staged rollout." +license: Apache-2.0 +compatibility: Requires the remotely hosted LaunchDarkly MCP server and a git CLI with access to the PR's repository +metadata: + author: launchdarkly + version: "0.1.0" +--- + +# Flag & Release a PR Change + +You're using a skill that takes a pull request whose change should ship behind a feature flag, and drives it end to end: create the guarding flag, wire the new behavior behind it on the PR's branch, and register an **automated rollout** so the change releases safely once the PR merges. + +This skill is a **PR-scoped orchestrator**. It composes two existing skills and adds the release step — its own job is the PR workflow (read the diff, work in a clone, push to the branch), the plan→implement sequencing, and the auto-release: + +| Step | Owned by | This skill's role | +|------|----------|-------------------| +| Decide *whether* to flag | [`should-flag-change`](../should-flag-change/SKILL.md) (advisory, read-only) | Act on a "yes"; make the call yourself if it wasn't run | +| Create the flag + wire the code | [`launchdarkly-flag-create`](../launchdarkly-flag-create/SKILL.md) | Invoke it against the change; don't re-teach flag creation or SDK patterns | +| Register the auto-release | **this skill** ([references/auto-release.md](references/auto-release.md)) | The new capability | + +Don't duplicate the flag-create mechanics here — defer to that skill for how flags are created (OFF by default, `temporary`, naming) and how the new path is guarded per SDK. This skill adds the PR/CI wrapper and the release. + +You work in two phases — **plan**, then **implement** — and you check in with the user in between. **Never create or modify anything during the plan phase.** + +## Prerequisites + +- The remotely hosted LaunchDarkly MCP server. +- A `git` CLI that can read and push to the PR's repository. +- The [`launchdarkly-flag-create`](../launchdarkly-flag-create/SKILL.md) skill available (this skill delegates flag creation and code wiring to it). + +**MCP tools this skill relies on directly:** +- `create-automated-rollout-config` — register the auto-release for the flag against the PR *(unique to this skill)* +- `match-release-policies` — preview which release policy governs each environment (call before proposing the release plan) +- `list-release-policies` — see the project's release policies and the metrics they auto-attach + +`create-flag` / `get-flag` / `list-flags` are used too, but via the flag-create flow — see that skill. + +## What's unique here (vs. flag-create) + +`launchdarkly-flag-create` creates a flag and wires it into a codebase you're editing directly. This skill differs in three ways, and that's all it adds: + +1. **It's driven by a PR.** You read the change as a three-dot diff and push the flag wiring back to the PR's branch, rather than editing a working copy in place. +2. **It plans first.** A plan phase proposes the flag + release with no side effects; you implement only after approval. +3. **It sets up the release.** After the flag exists and the code is wired, it registers an automated rollout so the merge triggers the right release automatically — see [references/auto-release.md](references/auto-release.md). + +Everything else — created-OFF semantics, `temporary` defaults, naming conventions, safe in-code defaults, per-SDK guarding patterns — is flag-create's job. Use it. + +## Working With the Pull Request + +Work from a clone so you can read the change and push the flag wiring back to its branch. Credentials are provided by the environment — never ask for, print, or store tokens. + +```bash +git clone https://github.com//.git && cd +git fetch origin pull//head +git diff origin/HEAD... # three-dot: change relative to the PR's base +``` + +The three-dot diff (`base...head`) shows exactly what the PR introduces. Read the changed files you need to understand the change and its risk. Stay in this clone through both phases — in implement you edit, commit, and push here. Full PR mechanics (clone, three-dot diff, commit/push to the branch): [references/pr-wiring.md](references/pr-wiring.md). + +## Plan Phase + +**Create nothing in this phase.** + +1. **Confirm it should be flagged.** If [`should-flag-change`](../should-flag-change/SKILL.md) already ran, act on its verdict. Otherwise apply the same judgment: favor a flag for user-facing or risky changes; skip config-only, dependency-bump, infra, test-only, or docs changes. If a flag clearly isn't warranted, say so and stop. +2. **Understand the change and conventions.** Read the three-dot diff and changed files — what does it do, what's the blast radius? Then follow **flag-create's Step 1** to learn how this codebase already uses flags (SDK, wrapper, key constants, naming). Don't reinvent that exploration here. +3. **Preview the release policy.** For each target environment, call `match-release-policies` (by `flagTags` before the flag exists, or `flagKey` after) to see which policy would govern it and what metrics auto-attach. This tells you what a `policy` release will actually do on merge. +4. **Design the minimal gate + release plan.** Usually a single boolean kill-switch around the new path (flag-create's [flag-types](../launchdarkly-flag-create/references/flag-types.md) covers the choice). Decide the per-environment release plan — which envs are `simple`, which are `policy`. Don't propose more flags than the change needs. +5. **Present the plan and stop.** Summarize: the flag (`key`, `name`, boolean, tags) and why it gates *this* change; the per-environment release plan and what each `policy` env's matched policy will do; where in the code the guard goes. Then wait. Revise on feedback; proceed only on clear approval. Ask a focused question if you're genuinely missing something (project key, environments, a missing policy) rather than guessing. + +## Implement Phase + +Only after approval: + +1. **Create the flag and wire the code** using [`launchdarkly-flag-create`](../launchdarkly-flag-create/SKILL.md) (its Steps 3–4). That skill creates the flag OFF with the agreed key/tags and adds the guarding evaluation with a safe default matching the codebase's pattern. Treat an "already exists" flag result as success. +2. **Commit and push to the PR branch.** This is the PR-specific part flag-create doesn't cover: commit the wiring and push to the PR's existing branch so it lands in the same PR — don't open a new PR or touch the base branch. See [references/pr-wiring.md](references/pr-wiring.md). +3. **Register the auto-release.** Call `create-automated-rollout-config` with `projectKey`, `flagKey`, the per-environment `environments` array (each with its `releaseType`), and the PR reference (`repoFullName` + `prNumber`, or `prUrl`). This binds the rollout to the merge. Details: [references/auto-release.md](references/auto-release.md). +4. **Verify.** `get-flag` shows the flag created and OFF; the code compiles/lints; both variation paths are complete; the rollout config was created (record the returned `config_id`). +5. **Report** what you created and why: flag key + LaunchDarkly link (created OFF); the file(s)/code path wired; the per-environment release plan + `config_id`; and what happens on merge (e.g. "production resolves policy X → guarded rollout on merge; staging serves true immediately"). + +## Edge Cases + +| Situation | Action | +|-----------|--------| +| Change isn't flag-worthy | Explain why (config-only, dep bump, infra, test-only, docs) and stop. Don't create a flag. | +| Flag already exists | Reuse it — "already exists" is success. Wire the existing key; don't duplicate. | +| No release policy matches an env | `policy` falls back to defaults (often immediate). Tell the user; offer `simple`, or point at release-policy setup. | +| Guarding needs more than a boolean | Prefer a boolean kill-switch. Only go multivariate if the change serves distinct variants; see flag-create's [flag-types](../launchdarkly-flag-create/references/flag-types.md). | +| Codebase has no LaunchDarkly SDK | Wiring can't evaluate a flag — SDK install is separate ([onboarding/sdk-install](../../onboarding/sdk-install/SKILL.md)). | +| Approval required in an environment | The MCP tool returns an approval URL — relay it; don't bypass it. | + +## What NOT to Do + +- **Don't create anything in the plan phase.** Plan proposes; implement creates. +- **Don't re-document flag creation or SDK guarding here** — that's [`launchdarkly-flag-create`](../launchdarkly-flag-create/SKILL.md). Link to it. +- **Don't turn the flag on for production yourself.** The automated rollout owns that; creating the flag OFF is the point. +- **Don't over-flag.** One kill-switch beats several speculative flags. +- **Don't handle or print credentials.** Git access is injected. +- **Don't skip `match-release-policies`.** Proposing `policy` without knowing what it resolves to is guessing. + +## References + +- [references/auto-release.md](references/auto-release.md): the automated-rollout / release-policy model, `simple` vs `policy`, precedence, per-environment choices. *(Core of this skill.)* +- [references/pr-wiring.md](references/pr-wiring.md): PR mechanics — clone, three-dot diff, committing to the PR branch. +- [`launchdarkly-flag-create`](../launchdarkly-flag-create/SKILL.md): flag creation + per-SDK guarding patterns this skill delegates to. diff --git a/skills/feature-flags/flag-and-release-change/marketplace.json b/skills/feature-flags/flag-and-release-change/marketplace.json new file mode 100644 index 0000000..6d62e1a --- /dev/null +++ b/skills/feature-flags/flag-and-release-change/marketplace.json @@ -0,0 +1,23 @@ +{ + "name": "flag-and-release-change", + "description": "Gate a PR's change behind a LaunchDarkly feature flag and set up its automated release", + "version": "0.1.0", + "author": "LaunchDarkly", + "repository": "https://github.com/launchdarkly/ai-tooling", + "skills": ["./"], + "tags": [ + "launchdarkly", + "feature-flags", + "feature-management", + "flag-creation", + "pull-request", + "auto-release", + "automated-rollout", + "release-policy", + "dark-launch", + "mcp" + ], + "requirements": { + "mcp-servers": ["@launchdarkly/mcp-server"] + } +} diff --git a/skills/feature-flags/flag-and-release-change/references/auto-release.md b/skills/feature-flags/flag-and-release-change/references/auto-release.md new file mode 100644 index 0000000..2263808 --- /dev/null +++ b/skills/feature-flags/flag-and-release-change/references/auto-release.md @@ -0,0 +1,74 @@ +# Auto-release: automated rollout configs & release policies + +This is the mechanism behind the "release" half of the skill. The goal: once the PR +merges and the guarding flag starts evaluating, the change rolls out **on its own**, +the way the team has decided similar changes should roll out — no human toggling a flag. + +## The pieces + +- **Flag** — the boolean gate you created (OFF everywhere). Nothing happens to users until a release turns it on. +- **Release policy** — a project-level rule that says *how* a matching flag should be released in a given environment: immediately, progressively (staged %), or as a guarded rollout with metrics and auto-rollback. Policies match on criteria like environment and flag tags, and can auto-attach the metrics a guarded rollout should watch. +- **Automated rollout config** — the per-flag, per-PR record (created by `create-automated-rollout-config`) that ties the flag to the merge and says, per environment, whether to release immediately (`simple`) or to defer to the environment's release policy (`policy`). + +## `simple` vs `policy` + +`create-automated-rollout-config` takes an `environments` array; each entry is +`{ environmentKey, releaseType }`. + +- **`simple`** (default): serve `true` in that environment as soon as the config is created. Because a freshly created flag isn't evaluated anywhere yet, this is effectively a no-op until the merged code ships — then that environment is simply "on." Use it for dev/staging environments you want fully enabled without ceremony. + +- **`policy`**: wait until the PR merges and the flag begins evaluating, then resolve that environment's configured release policy and perform the matching release — immediate, progressive, or guarded — automatically. Use it for production and any environment where you want a governed, monitored rollout with the safety net the team already defined. + +A typical plan: `staging → simple`, `production → policy`. + +## Preview before you propose + +Always call `match-release-policies` before recommending a `policy` environment, so you +(and the user) know what `policy` will actually do: + +- **Before the flag exists** — pass `projectKey`, `environmentKey`, and the proposed `flagTags`. This does client-side matching against the project's policies and previews the winner. +- **After the flag exists** — pass `projectKey`, `environmentKey`, and `flagKey`. This hits the server-side release-settings endpoint and returns the authoritative resolved policy. + +It returns the `winningPolicy`, the `winningReleaseMethod` (immediate / progressive / guarded), and any `autoAttachedMetricKeys` / `autoAttachedMetricGroupKeys`. Use `list-release-policies` to see every policy in the project and what each attaches. + +**If nothing matches**, `policy` falls back to project defaults (often an immediate release). Tell the user — they may want to pick `simple` instead, or set up a release policy first. + +## Precedence + +When a `policy` environment resolves what to do on merge, precedence is: + +**explicit overrides → matched release policy → project/demo defaults** + +So an operator override wins over the policy, and the policy wins over the fallback default. +You generally don't set overrides from this skill; you rely on the policy, which is why +previewing it matters. + +## Registering the config + +Call `create-automated-rollout-config` in the implement phase: + +```json +{ + "projectKey": "default", + "flagKey": "new-checkout-flow", + "environments": [ + { "environmentKey": "staging", "releaseType": "simple" }, + { "environmentKey": "production", "releaseType": "policy" } + ], + "repoFullName": "acme/storefront", + "prNumber": 482 +} +``` + +The guarding flag must already exist. Provide the PR reference (`repoFullName` + `prNumber`, +or `prUrl`) so the rollout is bound to the right merge. The call returns `created`, +`config_id`, and the normalized per-environment plan — record `config_id` in your report. + +## Relationship to guarded rollouts + +When a `policy` environment resolves to a **guarded** release method, the merge triggers the +same kind of progressive, metric-monitored rollout described in the +[`launchdarkly-guarded-rollout`](../../launchdarkly-guarded-rollout/SKILL.md) skill — the +difference is that here it's driven automatically by the policy on merge, rather than started +by hand. If a change needs a *bespoke* rollout that no policy expresses, set that environment +to `simple` here and drive the guarded rollout manually with that skill after merge. diff --git a/skills/feature-flags/flag-and-release-change/references/pr-wiring.md b/skills/feature-flags/flag-and-release-change/references/pr-wiring.md new file mode 100644 index 0000000..6805f98 --- /dev/null +++ b/skills/feature-flags/flag-and-release-change/references/pr-wiring.md @@ -0,0 +1,51 @@ +# PR wiring: reading the change and pushing to the branch + +This covers only what's specific to operating on a **pull request**: reading exactly what the +PR changes, and pushing the flag wiring back to its branch so it lands in the same PR. + +How to actually guard the new path in code — SDK calls, wrapper patterns, safe defaults, per +language — is **not** here. That's owned by +[`launchdarkly-flag-create`](../../launchdarkly-flag-create/SKILL.md) and its +[SDK Evaluation Patterns](../../launchdarkly-flag-create/references/sdk-evaluation-patterns.md). +Use those; don't reinvent them. + +## Clone and read the change + +Credentials are injected by the environment — never ask for, print, or store tokens. + +```bash +git clone https://github.com//.git && cd +git fetch origin pull//head +git diff origin/HEAD... # three-dot: change relative to the PR's base +``` + +Use the **three-dot** form (`base...head`). It shows only what this PR introduces, not +unrelated commits that landed on the base since the branch forked. Read the changed source +files (not just the diff) so you pick a clean seam to guard. + +Stay in this same clone for both phases. In the implement phase you commit and push here. + +## Guard the change (delegated) + +Follow flag-create's Step 4 and its SDK patterns to wrap the new behavior behind the flag. +The one principle worth repeating because it's a release-safety invariant: **the in-code +default must be the safe, pre-change behavior**, so an unreachable LaunchDarkly leaves users on +the old path. Prefer a single branch point around the new path over scattered flag checks, and +don't delete the old path — the kill-switch needs something to fall back to. + +## Commit and push to the PR branch + +```bash +git add -A +git commit -m "Gate behind LaunchDarkly flag " +git push origin HEAD: +``` + +Push to the PR's existing branch so the wiring appears in the same PR. Don't open a new PR, +force-push, or touch the base branch. + +## After wiring + +Register the auto-release (`create-automated-rollout-config`) so the merge triggers the +rollout — see [auto-release.md](auto-release.md). Then verify the code compiles/lints and +`get-flag` shows the flag created and OFF before reporting.