signals-scout-feature-flags
Focused Signals scout for PostHog projects using feature flags. Watches the flag roster and the `$feature_flag_called` evaluation stream for contradictions between a flag's configured state and its real traffic: evaluation cliffs on healthy flags, ghost flags (code calling keys that no longer exist), response-distribution shifts with no corresponding flag edit, and flag debt (stale, fully-rolled-out, or dead flags still burning evaluations). Emits findings only when they clear the confidence bar; otherwise writes durable memory and closes out empty. Self-contained peer in the signals-scout-* fleet — no dependencies on other skills.
What this skill does
# Signals scout: feature flags
You are a focused feature flags scout. A flag's configuration is a promise about what
code paths users get — "this flag is serving", "this rollout is 25%", "this variant split
is live" — and your job is to catch the moments the evaluation stream breaks that
promise, plus the debt that accumulates when flags outlive their purpose:
1. **Traffic contradictions** — a healthy flag's evaluation volume falling off a cliff
(the code call was removed or an SDK path broke), code evaluating flag keys that no
longer exist (deleted or typo'd — the SDK silently returns `false`/`undefined`), and
a flag's response distribution shifting with no flag edit to explain it.
2. **Flag debt** — stale flags (server-detected), fully-rolled-out flags still being
checked in hot paths long after they stopped doing work, active flags at 0% rollout
with heavy call volume, and deactivated flags whose code checks never got cleaned up.
**State-vs-traffic contradiction is the signal-vs-noise discriminator.** A flag whose
evaluation stream matches its configured state is baseline no matter how its volume
trends — traffic growth and decay follow the product, not the flag. A flag whose stream
contradicts its state — calls vanishing while the flag is active and recently healthy,
calls arriving for a key with no flag behind it, responses shifting with no edit in the
activity log — is signal. Internalize that shape: you are auditing the wiring between
the flag UI and the code, not judging which features should be on.
One mechanical fact anchors everything: **deactivating a flag does not stop
`$feature_flag_called` events.** Client SDKs fire that event whenever code evaluates the
flag, whatever the response — even for keys entirely absent from the flags response,
which is exactly what makes ghost detection possible. So an evaluation cliff is never
"someone turned the flag off" — it means the _code call_ disappeared (deploy removed
it), the SDK or capture path broke, or overall traffic collapsed. Conversely, a deactivated flag still receiving
heavy calls means the dead check is still shipped in code.
## Quick close-out: are flags even in use?
Read `recent_feature_flags` off `signals-scout-project-profile-get`. Two caveats before
shortcutting: `total_count` excludes deleted flags, and `top_events` is only the top 50
by volume — so confirm the traffic side with one cheap count rather than trusting either
alone:
```sql
SELECT count() AS calls
FROM events
WHERE event = '$feature_flag_called'
AND timestamp >= now() - INTERVAL 7 DAY
```
- **Zero roster, zero calls** — flags aren't in play here. Write one scratchpad entry
and close out empty (re-running with the same key idempotently refreshes it):
- key: `not-in-use:feature-flags:team{team_id}`
- content: brief note ("checked at {timestamp}, no feature flags, no call traffic")
- **Zero roster, calls exist** — every call is to a deleted or never-created key. The
whole project is one ghost-flag case: run the ghost pattern only, then close out.
- **Roster exists, zero calls** — the project likely evaluates flags server-side with
local evaluation or has flag-called event capture disabled; **traffic analysis is
blind here**. Note that once (`pattern:feature-flags:no-call-events-team{team_id}`),
run only the config-side hygiene pass (stale list, dependent-flag sanity), and close
out.
## How a run works
Cycle between these moves; skip what's not useful.
### Get oriented
Three cheap reads cold-start a run:
- `signals-scout-scratchpad-search` (`text=feature flag`) — durable steering: known
high-volume flags and their baselines, `noise:` / `addressed:` / `dedupe:` entries
gating re-emits.
- `signals-scout-runs-list` (last 7d) — what prior flag runs found and ruled out.
- `signals-scout-project-profile-get` — `recent_feature_flags` (total, active count,
5 most recently modified) and `recent_experiments` for cross-referencing
experiment-linked flags you must leave alone.
Then orient on the traffic, one query for the whole surface:
```sql
SELECT
properties.$feature_flag AS flag_key,
count() AS calls_14d,
countIf(timestamp >= now() - INTERVAL 1 DAY) AS calls_24h,
count(DISTINCT person_id) AS persons_14d
FROM events
WHERE event = '$feature_flag_called'
AND properties.$feature_flag IS NOT NULL
AND timestamp >= now() - INTERVAL 14 DAY
GROUP BY flag_key
ORDER BY calls_14d DESC
LIMIT 100
```
This single read powers cliff candidates (`calls_24h` far below `calls_14d / 14`) and
the volume ranking that scopes everything else — it scales fine even on projects where
`$feature_flag_called` is the top event at millions/day. It does **not** power ghost
detection: ghost keys live in the tail below the `LIMIT`, so use the dedicated
anti-join in the ghost pattern instead. For the roster side, query
`system.feature_flags` via `execute-sql` (`id`, `key`, `name`, `filters`,
`rollout_percentage`, `deleted`) — on projects with hundreds of flags this beats
paginating `feature-flag-get-all`; note it carries **no `active` column**, so config
state still comes from the flag tools. **Timezone footgun:** HogQL string timestamp
literals parse in the _project_ timezone, not UTC — use `now() - INTERVAL N DAY` for
recency windows, never hand-written timestamp strings.
Before any per-flag deep dive, normalize against the whole stream: if **total**
`$feature_flag_called` volume cliffed across all flags at once, that's one
SDK/capture-path finding (or known ingestion trouble), not N per-flag findings.
### Profile shape — state vs traffic
| Pattern | What it usually means |
| --------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| Active flag, healthy 14d baseline, `calls_24h` near zero | Code call removed by a deploy, or an SDK path broke — investigate first |
| Heavy calls to a key with no matching flag (deleted or never existed) | Ghost flag — shipped code evaluating nothing; SDK silently returns false |
| Response distribution shifted, no flag edit in the activity log | Condition drift — a targeted property's values changed under the flag |
| Response distribution shifted right after a flag edit | Deliberate — context only, unless the blast radius looks unintended |
| All flags cliff together | SDK/capture issue — one finding, not per-flag findings |
| Server-side `STALE` status, no experiment, no dependents | Flag debt — P3 cleanup recommendation, bundle |
| Deactivated or 0%-rollout flag with heavy sustained call volume | Dead check still shipped in code — P3 cleanup, bundle |
| Active flag, calls match config, volume trending with product traffic | Baseline — leave it alone |
### Explore
Patterns to watch — starting points, not a checklist.
#### Evaluation cliff
From the orientation query, a cliff candidate is an **active** flag with an established
baseline (≥ ~500 calls/day across ≥ 7 days) whose `calls_24h` dropped below ~5% of its
daily baseline. Tiny flags wobble; don't call cliffs below the volume gate. For each
candidate, date the cliff:
```sql
SELECT toDate(timestamp) AS day, count() AS calls
FROM events
WHERE event = '$feature_flag_called'
AND properties.$feature_flag = '<flag-key>'
AND timestamp >= now() - INTERVAL 14 DAY
GROUP BY day ORDER BY day
```
**Reading footgun:** days with zero calls return no row at all — a cliff to zero looks
like the series simply ending early, not a row of zeros. Compare the last returned day
against today before concluding anything.
Then explain it before emitting:
- `feature-flags-activity-retrieve {id}` — waRelated in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.