From 658284d88ef309b39a16c34fee911f37f964aa94 Mon Sep 17 00:00:00 2001 From: Vadim Korolik Date: Thu, 9 Jul 2026 18:57:55 +0000 Subject: [PATCH 1/2] Add flag-and-release-change skill The "apply" counterpart to the advisory should-flag-change skill: once a PR's change is judged flag-worthy, this skill creates the guarding flag, wires the new path behind it on the PR branch, and registers an automated rollout (create-automated-rollout-config) so the change releases safely on merge. Two-phase plan->implement, portable (public LD MCP + git). Includes references/auto-release.md (release policies, simple vs policy, precedence) and references/pr-wiring.md (three-dot diff, guarding, push). Wires into README + regenerated skills.json. Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 1 + skills.json | 20 +++ .../flag-and-release-change/SKILL.md | 123 ++++++++++++++++++ .../flag-and-release-change/marketplace.json | 23 ++++ .../references/auto-release.md | 74 +++++++++++ .../references/pr-wiring.md | 70 ++++++++++ 6 files changed, 311 insertions(+) create mode 100644 skills/feature-flags/flag-and-release-change/SKILL.md create mode 100644 skills/feature-flags/flag-and-release-change/marketplace.json create mode 100644 skills/feature-flags/flag-and-release-change/references/auto-release.md create mode 100644 skills/feature-flags/flag-and-release-change/references/pr-wiring.md 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..1f957f2 --- /dev/null +++ b/skills/feature-flags/flag-and-release-change/SKILL.md @@ -0,0 +1,123 @@ +--- +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 does the work end to end: create the guarding flag in LaunchDarkly, 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 is the **"apply"** half of a two-step pipeline: + +1. **Decide** — [`should-flag-change`](../should-flag-change/SKILL.md) reads the diff and returns an advisory verdict on *whether* the change should be flagged. (Read-only. It never creates anything.) +2. **Apply** — *this skill* acts on a "yes": it creates the flag, wires the code, and sets up the auto-release. + +If nobody has run the decision step, make the call yourself using the same judgment (favor a flag for user-facing or risky changes; skip it for config-only, dependency-bump, infra, test-only, or docs changes). If a flag clearly isn't warranted, say so and stop — don't create one just because you were invoked. + +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 + +This skill requires the remotely hosted LaunchDarkly MCP server and a `git` CLI that can read (and push to) the PR's repository. + +**Required MCP tools:** +- `create-flag` — create the guarding feature flag +- `create-automated-rollout-config` — register the auto-release for the flag against the PR +- `get-flag` — verify the flag after creation + +**Recommended MCP tools:** +- `match-release-policies` — preview which release policy will govern each environment (call this *before* proposing the release plan) +- `list-release-policies` — see the project's configured release policies and what metrics auto-attach +- `list-flags` — learn the project's naming and tagging conventions + +**Optional MCP tools:** +- `update-flag-settings` — adjust flag name, description, or tags +- `toggle-flag` — only if you deliberately need the flag on in a lower environment + +## Core Concepts + +### The flag is a gate, created OFF +`create-flag` creates the flag with **targeting off in every environment**, serving its off variation. That is exactly what you want: the merged code evaluates the flag, gets the safe/old behavior, and nothing changes for users until the release turns it on. Dark launch by default. + +### Auto-release = a rollout that fires on merge +`create-automated-rollout-config` records a **per-environment** rollout plan bound to this flag and PR. Each environment gets a release type: + +| Release type | Behavior | +|--------------|----------| +| `simple` (default) | Serves `true` in that environment as soon as the config is created. Because the flag is brand new and not yet evaluated anywhere, this is effectively a no-op until the merged code ships — useful for lower environments you want fully on. | +| `policy` | Waits until the PR merges and the flag starts evaluating, then resolves that environment's configured **release policy** and performs the matching release — immediate, progressive, or guarded — automatically. | + +Use `policy` for production and any environment where you want a governed, monitored rollout. Use `simple` for dev/staging environments you just want turned on. + +See [references/auto-release.md](references/auto-release.md) for the release-policy model, precedence rules, and how to choose per environment. + +## 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... +``` + +The **three-dot** diff (`base...head`) shows exactly what the PR changes relative to its base. Read the changed files you need to understand the change and its risk. Stay in this same clone through both phases — in the implement phase you edit, commit, and push here. See [references/pr-wiring.md](references/pr-wiring.md) for the full PR workflow and guarding patterns. + +## Plan Phase + +**Create nothing in this phase.** + +1. **Understand the change.** Read the three-dot diff and the changed files. What does it do? What's the blast radius if it misbehaves — user-facing? data-writing? a new external call? +2. **Learn the conventions.** Skim existing flags (`list-flags`) for naming (`kebab-case` vs `snake_case`), tags, and temporary-vs-permanent norms. Look at how the codebase already evaluates flags (SDK, wrapper, key constants) so your wiring will match. +3. **Preview the release policy.** For each target environment, call `match-release-policies` (by `flagTags` before the flag exists, or by `flagKey` after) to see which policy would govern it and what metrics auto-attach. This tells you whether `policy` will yield a guarded/progressive/immediate release in prod. +4. **Design the minimal gate.** Usually a single boolean kill-switch around the new or rewritten code path. Don't propose more flags than the change needs. Decide the per-environment release plan (which envs are `simple`, which are `policy`). +5. **Present the plan and stop.** Summarize for the user: + - the flag (`key`, `name`, `type` — usually boolean, default/off variation, tags) and why it gates *this* change; + - the per-environment release plan and, for `policy` environments, what the matched release policy will do; + - where in the code you'll add the guard. + + Then wait. Revise on feedback. Proceed only on a clear approval. If you're missing something you genuinely need (target project key, environments, a policy that doesn't exist yet), ask a focused question rather than guessing. + +## Implement Phase + +Only after approval: + +1. **Create the flag.** Use `create-flag` with the agreed key, name, boolean kind, and tags. Set `temporary: true` unless it's meant to be long-lived. Treat an "already exists" result as success. Verify with `get-flag`. +2. **Wire the code on the PR branch.** In your clone, guard the new behavior behind the flag, matching the codebase's existing evaluation pattern. The **default/fallback value in code must be the safe, pre-change behavior**, so the feature stays off if LaunchDarkly is unreachable. Keep both branches complete. Commit and push to the PR's branch. Details and per-language patterns: [references/pr-wiring.md](references/pr-wiring.md). +3. **Register the auto-release.** Call `create-automated-rollout-config` with the `projectKey`, the `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. +4. **Verify.** Confirm the flag exists and is off (`get-flag`), the code compiles/lints, both variation paths are complete, and the rollout config was created (note the returned `config_id`). +5. **Report** what you created and why: + - flag key + LaunchDarkly link, and that it's created OFF; + - the file(s) and code path you wired; + - the per-environment release plan and the `config_id`; + - what will happen 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 — treat "already exists" as success. Don't create a duplicate. Wire the existing key. | +| No release policy matches an env | `policy` would fall back to defaults (often immediate). Tell the user; offer `simple`, or point them at the release-policy setup. | +| Guarding needs more than a boolean | Prefer a boolean kill-switch anyway. Only propose a multivariate flag if the change genuinely serves distinct variants; explain the tradeoff first. | +| Codebase has no LaunchDarkly SDK | The wiring can't evaluate a flag. Surface this — SDK install is a separate step ([onboarding/sdk-install](../../onboarding/sdk-install/SKILL.md)). | +| Approval required in an environment | The MCP tool returns an approval URL — relay it; don't try to bypass it. | + +## What NOT to Do + +- **Don't create anything in the plan phase.** Plan proposes; implement creates. +- **Don't turn the flag on for production yourself.** The automated rollout owns that. Creating the flag OFF is the point. +- **Don't wire a code default that enables the new behavior.** The fallback must be the old/safe path. +- **Don't over-flag.** One kill-switch for the change 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, and per-environment choices. +- [references/pr-wiring.md](references/pr-wiring.md): cloning, the three-dot diff, guarding patterns by SDK, and committing to the PR branch. 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..0204cf2 --- /dev/null +++ b/skills/feature-flags/flag-and-release-change/references/pr-wiring.md @@ -0,0 +1,70 @@ +# PR wiring: reading the change and guarding it in code + +The skill edits the PR's own branch: it clones the repo, reads exactly what the PR changes, +wraps the new behavior behind the flag, and pushes back to the branch so the flag wiring +lands in the same PR. + +## 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) to understand the new path and pick the right seam to guard. + +Stay in this same clone for both phases. In the implement phase you commit and push here. + +## Find the codebase's flag pattern first + +Match what already exists rather than inventing a style: + +- **SDK / wrapper** — does code call the SDK directly (`variation()`, `boolVariation()`, `useFlags()`), or through a project wrapper/service? Use whatever's there. +- **Key constants** — are flag keys string literals at the call site, or centralized in a constants file/enum? Add the new key where the others live. +- **Context construction** — how is the user/context object built and passed to evaluation? Reuse it. +- **Default values** — what fallback do existing evaluations pass? + +## Guard the new path + +Wrap the new or rewritten behavior so it only runs when the flag is on. **The in-code default +must be the safe, pre-change behavior** — if LaunchDarkly is unreachable, users get the old +path, not the new one. + +```ts +// Server-side Node example — match the codebase's actual pattern. +const useNewCheckout = await client.boolVariation('new-checkout-flow', context, false); +// default ↑ = old behavior +if (useNewCheckout) { + return newCheckoutFlow(order); // the PR's new path +} +return legacyCheckoutFlow(order); // preserved existing path +``` + +Principles that hold across languages: + +- **Both branches complete.** The flag-off path must fully preserve today's behavior; the flag-on path is the PR's change. +- **Guard at a clean seam.** Prefer one branch point around the new path over scattering flag checks through the change. +- **Don't delete the old path.** The kill-switch needs something to fall back to. +- **Keep it minimal.** You're adding a gate, not refactoring the PR. + +## Commit and push + +```bash +git add -A +git commit -m "Gate behind LaunchDarkly flag new-checkout-flow" +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. From 18837749bc4561e89f97b3f4b8c71c20e9664e3f Mon Sep 17 00:00:00 2001 From: Vadim Korolik Date: Thu, 9 Jul 2026 19:09:48 +0000 Subject: [PATCH 2/2] Reduce overlap with launchdarkly-flag-create Reframe flag-and-release-change as a PR-scoped orchestrator that composes should-flag-change (decide) + launchdarkly-flag-create (create flag & wire code) and adds the unique auto-release step. Delegate flag-creation and per-SDK guarding mechanics to flag-create instead of re-teaching them; slim pr-wiring.md to PR-specific mechanics (three-dot diff, commit/push) and point at flag-create's sdk-evaluation-patterns for guarding. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../flag-and-release-change/SKILL.md | 107 ++++++++---------- .../references/pr-wiring.md | 55 +++------ 2 files changed, 63 insertions(+), 99 deletions(-) diff --git a/skills/feature-flags/flag-and-release-change/SKILL.md b/skills/feature-flags/flag-and-release-change/SKILL.md index 1f957f2..f593ed1 100644 --- a/skills/feature-flags/flag-and-release-change/SKILL.md +++ b/skills/feature-flags/flag-and-release-change/SKILL.md @@ -10,51 +10,42 @@ metadata: # Flag & Release a PR Change -You're using a skill that takes a pull request whose change should ship behind a feature flag, and does the work end to end: create the guarding flag in LaunchDarkly, 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. +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 is the **"apply"** half of a two-step pipeline: +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: -1. **Decide** — [`should-flag-change`](../should-flag-change/SKILL.md) reads the diff and returns an advisory verdict on *whether* the change should be flagged. (Read-only. It never creates anything.) -2. **Apply** — *this skill* acts on a "yes": it creates the flag, wires the code, and sets up 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 | -If nobody has run the decision step, make the call yourself using the same judgment (favor a flag for user-facing or risky changes; skip it for config-only, dependency-bump, infra, test-only, or docs changes). If a flag clearly isn't warranted, say so and stop — don't create one just because you were invoked. +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 -This skill requires the remotely hosted LaunchDarkly MCP server and a `git` CLI that can read (and push to) the PR's repository. +- 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). -**Required MCP tools:** -- `create-flag` — create the guarding feature flag -- `create-automated-rollout-config` — register the auto-release for the flag against the PR -- `get-flag` — verify the flag after creation +**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 -**Recommended MCP tools:** -- `match-release-policies` — preview which release policy will govern each environment (call this *before* proposing the release plan) -- `list-release-policies` — see the project's configured release policies and what metrics auto-attach -- `list-flags` — learn the project's naming and tagging conventions +`create-flag` / `get-flag` / `list-flags` are used too, but via the flag-create flow — see that skill. -**Optional MCP tools:** -- `update-flag-settings` — adjust flag name, description, or tags -- `toggle-flag` — only if you deliberately need the flag on in a lower environment +## What's unique here (vs. flag-create) -## Core Concepts +`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: -### The flag is a gate, created OFF -`create-flag` creates the flag with **targeting off in every environment**, serving its off variation. That is exactly what you want: the merged code evaluates the flag, gets the safe/old behavior, and nothing changes for users until the release turns it on. Dark launch by default. +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). -### Auto-release = a rollout that fires on merge -`create-automated-rollout-config` records a **per-environment** rollout plan bound to this flag and PR. Each environment gets a release type: - -| Release type | Behavior | -|--------------|----------| -| `simple` (default) | Serves `true` in that environment as soon as the config is created. Because the flag is brand new and not yet evaluated anywhere, this is effectively a no-op until the merged code ships — useful for lower environments you want fully on. | -| `policy` | Waits until the PR merges and the flag starts evaluating, then resolves that environment's configured **release policy** and performs the matching release — immediate, progressive, or guarded — automatically. | - -Use `policy` for production and any environment where you want a governed, monitored rollout. Use `simple` for dev/staging environments you just want turned on. - -See [references/auto-release.md](references/auto-release.md) for the release-policy model, precedence rules, and how to choose per environment. +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 @@ -63,61 +54,53 @@ Work from a clone so you can read the change and push the flag wiring back to it ```bash git clone https://github.com//.git && cd git fetch origin pull//head -git diff origin/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 changes relative to its base. Read the changed files you need to understand the change and its risk. Stay in this same clone through both phases — in the implement phase you edit, commit, and push here. See [references/pr-wiring.md](references/pr-wiring.md) for the full PR workflow and guarding patterns. +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. **Understand the change.** Read the three-dot diff and the changed files. What does it do? What's the blast radius if it misbehaves — user-facing? data-writing? a new external call? -2. **Learn the conventions.** Skim existing flags (`list-flags`) for naming (`kebab-case` vs `snake_case`), tags, and temporary-vs-permanent norms. Look at how the codebase already evaluates flags (SDK, wrapper, key constants) so your wiring will match. -3. **Preview the release policy.** For each target environment, call `match-release-policies` (by `flagTags` before the flag exists, or by `flagKey` after) to see which policy would govern it and what metrics auto-attach. This tells you whether `policy` will yield a guarded/progressive/immediate release in prod. -4. **Design the minimal gate.** Usually a single boolean kill-switch around the new or rewritten code path. Don't propose more flags than the change needs. Decide the per-environment release plan (which envs are `simple`, which are `policy`). -5. **Present the plan and stop.** Summarize for the user: - - the flag (`key`, `name`, `type` — usually boolean, default/off variation, tags) and why it gates *this* change; - - the per-environment release plan and, for `policy` environments, what the matched release policy will do; - - where in the code you'll add the guard. - - Then wait. Revise on feedback. Proceed only on a clear approval. If you're missing something you genuinely need (target project key, environments, a policy that doesn't exist yet), ask a focused question rather than guessing. +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.** Use `create-flag` with the agreed key, name, boolean kind, and tags. Set `temporary: true` unless it's meant to be long-lived. Treat an "already exists" result as success. Verify with `get-flag`. -2. **Wire the code on the PR branch.** In your clone, guard the new behavior behind the flag, matching the codebase's existing evaluation pattern. The **default/fallback value in code must be the safe, pre-change behavior**, so the feature stays off if LaunchDarkly is unreachable. Keep both branches complete. Commit and push to the PR's branch. Details and per-language patterns: [references/pr-wiring.md](references/pr-wiring.md). -3. **Register the auto-release.** Call `create-automated-rollout-config` with the `projectKey`, the `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. -4. **Verify.** Confirm the flag exists and is off (`get-flag`), the code compiles/lints, both variation paths are complete, and the rollout config was created (note the returned `config_id`). -5. **Report** what you created and why: - - flag key + LaunchDarkly link, and that it's created OFF; - - the file(s) and code path you wired; - - the per-environment release plan and the `config_id`; - - what will happen on merge (e.g. "production resolves policy X → guarded rollout on merge; staging serves true immediately"). +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 — treat "already exists" as success. Don't create a duplicate. Wire the existing key. | -| No release policy matches an env | `policy` would fall back to defaults (often immediate). Tell the user; offer `simple`, or point them at the release-policy setup. | -| Guarding needs more than a boolean | Prefer a boolean kill-switch anyway. Only propose a multivariate flag if the change genuinely serves distinct variants; explain the tradeoff first. | -| Codebase has no LaunchDarkly SDK | The wiring can't evaluate a flag. Surface this — SDK install is a separate step ([onboarding/sdk-install](../../onboarding/sdk-install/SKILL.md)). | -| Approval required in an environment | The MCP tool returns an approval URL — relay it; don't try to bypass it. | +| 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 turn the flag on for production yourself.** The automated rollout owns that. Creating the flag OFF is the point. -- **Don't wire a code default that enables the new behavior.** The fallback must be the old/safe path. -- **Don't over-flag.** One kill-switch for the change beats several speculative flags. +- **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, and per-environment choices. -- [references/pr-wiring.md](references/pr-wiring.md): cloning, the three-dot diff, guarding patterns by SDK, and committing to the PR branch. +- [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/references/pr-wiring.md b/skills/feature-flags/flag-and-release-change/references/pr-wiring.md index 0204cf2..6805f98 100644 --- a/skills/feature-flags/flag-and-release-change/references/pr-wiring.md +++ b/skills/feature-flags/flag-and-release-change/references/pr-wiring.md @@ -1,8 +1,13 @@ -# PR wiring: reading the change and guarding it in code +# PR wiring: reading the change and pushing to the branch -The skill edits the PR's own branch: it clones the repo, reads exactly what the PR changes, -wraps the new behavior behind the flag, and pushes back to the branch so the flag wiring -lands in the same PR. +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 @@ -16,47 +21,23 @@ git diff origin/HEAD... # three-dot: change relative to the PR's 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) to understand the new path and pick the right seam to guard. +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. -## Find the codebase's flag pattern first - -Match what already exists rather than inventing a style: - -- **SDK / wrapper** — does code call the SDK directly (`variation()`, `boolVariation()`, `useFlags()`), or through a project wrapper/service? Use whatever's there. -- **Key constants** — are flag keys string literals at the call site, or centralized in a constants file/enum? Add the new key where the others live. -- **Context construction** — how is the user/context object built and passed to evaluation? Reuse it. -- **Default values** — what fallback do existing evaluations pass? - -## Guard the new path - -Wrap the new or rewritten behavior so it only runs when the flag is on. **The in-code default -must be the safe, pre-change behavior** — if LaunchDarkly is unreachable, users get the old -path, not the new one. - -```ts -// Server-side Node example — match the codebase's actual pattern. -const useNewCheckout = await client.boolVariation('new-checkout-flow', context, false); -// default ↑ = old behavior -if (useNewCheckout) { - return newCheckoutFlow(order); // the PR's new path -} -return legacyCheckoutFlow(order); // preserved existing path -``` - -Principles that hold across languages: +## Guard the change (delegated) -- **Both branches complete.** The flag-off path must fully preserve today's behavior; the flag-on path is the PR's change. -- **Guard at a clean seam.** Prefer one branch point around the new path over scattering flag checks through the change. -- **Don't delete the old path.** The kill-switch needs something to fall back to. -- **Keep it minimal.** You're adding a gate, not refactoring the PR. +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 +## Commit and push to the PR branch ```bash git add -A -git commit -m "Gate behind LaunchDarkly flag new-checkout-flow" +git commit -m "Gate behind LaunchDarkly flag " git push origin HEAD: ```