Gate application code behind an existing Harness FME feature flag. Detect the app's language, framework and any existing flag wrapper first; ask only what that can't resolve (target app, subject identity, gating point, treatment/config/fallback names, extension needs). Prefer an existing wrapper; otherwise propose a thin, customer-extensible facade or hook — never a new framework or second SDK client — approved before editing. Map named treatments to branches with a safe, non-boolean-coerced fallback, then add tests at the wrapper and call-site boundary. New flags delegate to create-feature-flag with the one environment actually selected; missing targeting delegates to update-flag-targeting under fresh approval. Not for flag creation alone, metrics (choose-metric, create-metric, instrument-metric), or first-time onboarding (feature-flag-onboarding). Trigger phrases: gate this code behind a flag, add flag check, wire up getTreatment, add SDK evaluation call.
85%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Skill identifier: instrument-feature-flag. Gate application code behind an existing FME flag: inspect the real app first, prefer or design a small customer-extensible wrapper, get it approved, implement the minimal edits, and test at the wrapper and call-site boundary. Related: /create-feature-flag (new flag), /update-flag-targeting (missing/insufficient targeting), /feature-flag-onboarding (first-time setup, which delegates here for code).
Works through the Harness MCP server or the Harness CLI; names are from tool-map.md. This skill only reads Harness flag state for the selected environment — flag/definition writes are delegated to /create-feature-flag and /update-flag-targeting.
| Operation | MCP | CLI |
|---|---|---|
| List flags | harness_list · fme_feature_flag · size: 50 · compact: false | harness list feature_flag --json --limit 50 |
| Get flag | harness_get · fme_feature_flag · params.feature_flag_name | harness get feature_flag <name> --json |
| List environments | harness_list · fme_environment · compact: false | harness list fme_environment --json |
| List traffic types | harness_list · fme_traffic_type · compact: false | harness list traffic_type --json |
| Get definition | harness_get · fme_feature_flag_definition · params: { feature_flag_name, environment_id } | harness get feature_flag:definition <flag> --env <env-id> --json |
Follow scope-establishment.md. Carry confirmed account/org/project, transport and application target from the request/caller; ask only for missing context before API calls. Keep Harness scope separate from the application repo/runtime — never derive one from the other. See mcp-setup.md for a supported CLI or MCP choice. List environments and require one explicitly confirmed environment before any flag/definition handoff. Resolve its ID and isProduction; if none is selected or the choice is ambiguous, ask and STOP—never inherit an all-environments default. This selection does not authorize a write.
Inspect the real application: language/framework/runtime and package manager (lockfiles/manifests), which app/package in a monorepo is plausible, the installed FME/OpenFeature/Classic-FF SDK and its version, and any existing flag wrapper/facade (grep flag-adjacent helper names, not only raw SDK calls). See sdk-patterns.md for detection signals and language-guidance.md for the native-FME/OpenFeature/Classic-FF contract table — FME native treatments are names, not booleans, and batch/config/async evaluation calls have their own result shapes; never assume a signature shared across them. Stop: Classic FF-only repo with no matching FME flag; or an OpenFeature provider whose configured target (FME project/environment) is unverified — confirm it before treating getBooleanValue/etc. as evidence about this flag. Summarize detected facts in 3–5 bullets before asking integration questions. With no application access, ask for the missing non-secret context and provide guidance only: no invented file locations, edits or test success. An absent SDK is a setup case, not an unknown-signature exception: propose a runtime-appropriate package from verified official docs, and include installation/version changes in the approval plan.
Ask only for facts Phase 2 left open, and never re-ask a fact already confirmed by inspection or already supplied by a calling skill (e.g. /feature-flag-onboarding):
file:line if more than one placement is plausible.control/errors.Get flag using the confirmed name and scope. A 404 is not proof a new flag is needed: recheck the identifier, scope and access per tool-map.md. Only after resolving genuine absence and the user's intent to create, load create-feature-flag with confirmed scope/transport, name/purpose, traffic type, treatment names/config/safe default, known description/ticket and one selected environment; carry user-confirmed facts rather than deriving new defaults. Its own approved plan controls creation. Disclose that approving resource creation does not approve later app edits; cancellation leaves a resource to report, not automatically delete. Require returned readback/IDs to match that contract, then resume here without creating again. An archived flag is not an active integration target: stop and clarify; any lifecycle change is a separate request.
For an existing flag, confirm the returned trafficType against List traffic types and the approved evaluation-subject source. A user/account/device identity mismatch is a STOP-and-clarify condition—do not infer semantic compatibility from the key's format or silently change it. Get definition in that environment and reconcile Phase 3 with the exact case-sensitive treatment names and config. Missing definitions or requested treatment changes go to update-flag-targeting only after confirming that intent, under its separate approval. A treatment not currently allocated traffic does not require a targeting change merely to write/test its code branch. Never expand the rollout as a side effect of instrumentation.
Prefer an existing wrapper/facade found in Phase 2 — extend it, don't create a second one. If none exists, default recommendation: a thin, idiomatic, application-owned facade (a module/service function for server code, a domain-specific custom hook for a component framework) that:
Never introduce a new universal/cross-flag framework, a second SDK client, or a guessed cross-language signature — verify against the installed SDK's types/version or an existing call. A direct SDK call without a wrapper is an exception, not a default: propose it only when the language/framework makes a wrapper impractical, explain the tradeoff, and get explicit approval for that specific choice. Inspect only non-secret configuration/credential references (see language-guidance.md) — never print, request, or hardcode a credential value. Show the proposed interface/location, call sites, treatment-to-branch map, readiness/error/configured-fallback behavior, dependency/configuration changes and focused test plan. Verify installed signatures and confirm missing SDK/credential prerequisites through language guidance; no declared tool retrieves SDK keys. STOP and wait for explicit approval of these edits and any dependency/version changes. Reconfirm a changed plan; a resource-create approval is not approval to edit the app.
Make the smallest edits: SDK init/wrapper only if missing, the wrapper's evaluation call, and call sites that use the wrapper. Reuse the SDK's existing client/factory lifecycle for this environment and identity — don't instantiate a client per evaluation — but this isn't a universal one-client-ever rule; follow the installed SDK's documented pattern. Map every named treatment explicitly; route control and any unmapped/unexpected value to the agreed safe fallback — never treat a non-empty treatment string as "enabled" by truthiness. A configured fallback treatment is a safety behavior, not proof the flag evaluated live — never report a fallback/offline/mocked result as a successful evaluation. Handle not-ready states, unexpected treatments and missing/malformed config using the approved contract; preserve framework hook ordering and cleanup. Do not add logging, analytics, caching or PII capture that wasn't requested and approved.
Add/update focused tests in the app's existing framework at two boundaries: (1) the wrapper — each approved treatment, identity isolation, readiness/error/configured-fallback and malformed-config behavior; (2) the call site's branches and safe fallback. Run the safe focused tests; do not install a new test framework without approval. If tests cannot run, say why and mark them not run, not passed. Mocks verify code behavior, not live evaluation. Live proof belongs to the calling onboarding workflow or a separately authorized runtime check; never automatically start onboarding, deploy, commit or open a PR.
Report application/revision, flag and selected environment IDs, approved subject-source/traffic-type contract (no sensitive values), edited locations, treatment/fallback mapping, dependency/config changes, delegated resource readbacks and tests (passed/failed/not run). Report live behavior separately as verified with named evidence or unverified. In guidance-only mode, list proposed steps and limitations instead of claiming edits. Return evidence to the caller without recursively invoking it or re-asking confirmed facts.
checkout-v2 flag" — detect and clarify, read its live definition, then propose the wrapper integration; an existing flag does not justify skipping those steps./create-feature-flag with the one chosen environment, then resumes at Phase 5.v1/v2/v3 treatments with per-treatment config and a safe fallback" — confirm each treatment's config shape and the fallback explicitly; never assume off.compact: false; all reads carry explicit scope. Tools-table list rows are page requests, not full inventories: follow pagination, using CLI --offset/--limit until completeness is established. Use List flags only for approved discovery when the flag identity is unknown; prefer confirmed IDs/direct gets.| Problem | Solution |
|---|---|
| 404 on Get flag | Recheck scope, identifier and access first. Only confirmed absence plus an explicit creation request can hand off to /create-feature-flag |
| Definition missing or targeting insufficient for the agreed treatments | Delegate to /update-flag-targeting with a fresh approval |
Treatment name in code doesn't match the live treatments array | Re-read the definition; fix the code reference, never rename the live treatment to match the code |
| Can't tell which app/package is the gating target | Ask — don't guess from the flag's name or traffic type |
| Repo uses Classic FF only, or an OpenFeature provider of unknown origin | Stop; confirm the actual flag system before writing any evaluation code |
control, killed stateharness/harness-ai
ca2ab81
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.