Generate a person audience from a plain-English brief — discover the signals behind it, score them with the math visible, and compose them into an audience whose reach is measured. The build step behind /watt:audience; routes to the leaf that fits how the audience should be built. Produces a signal stack + measured reach, or a roster — never an export, never contact data. Not a user command — /watt:audience is the front door. Use when a build-shaped ask arrives — "build me an audience of …", "I need about N people who …", "the highest-intent people in-market for X", "where do these people cluster", "a lead list of in-market companies", "match my customer list / build from my customers" — or when a /watt:explore signal pool is ready to become an audience.
68
86%
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
audience-generate — the build step behind /watt:audience — turns "who I want to reach" into a built audience: the signals behind the brief, scored with the math visible, composed into a set whose reach is measured. The user walks away with a signal stack — the boolean shape of their audience and its measured reach — or a roster: the top groups inside the audience (where those people concentrate), or the set reached by crossing the employment graph to their employers (or to the employees of named companies). Either is ready for the audience-analyze and audience-activate steps.
One spine, many ways to compose. This skill owns the shared spine every build shares — discover the signals, score them, curate the working set, land the audience — and routes by input anchor (what the user has in hand) to the leaf that owns the build:
audience-generate-search (today: a size band with greedy, maximum credible reach with broad, precision with lift, the top groups inside the audience with group, or the set reached by crossing the employment graph with traverse — the objective picks).audience-generate-list, which resolves the list first (today: resolve-only for a tight matched set, or expand for the widest identity-matched set — both a roster; lookalike, which profiles the list and hands back the signals that define it to build from; or overlay, which scores the resolved list against a pool of signals and ranks it; group and traverse aren't available there).Route; don't run the compose. Your job at this level is the shared spine below — the language, the build-only lane, and the discover-score-curate-land procedure every leaf composes with. The leaf owns the objective it composes or partitions toward and the strategy procedure that gets there.
Nothing is composed unseen. The user approves the scored working set — and every pivot after it — before any audience is built or measured; the roles, the math, and the target stay on screen the whole way. A finished-looking audience the user didn't steer is this surface's failure mode.
You own the conversation, the elicitation, the discovery, the scoring, and the rendering — all run inline on the main thread. Pass signals by trait_hash, never display name alone.
/watt:audience router — often carrying an /watt:explore signal pool as the seed (see Entry).context/discovery.md); see flow step A.context/scoring.md); see flow step B.context/adjacencies.md).audience-generate-search — build from a plain-English description; today composes to a size band with greedy, maximum credible reach with broad, or precision with lift, partitions the audience into its top groups with group, or crosses the employment graph to a qualified set with traverse — the objective picking the strategy (a signal stack, or a roster from grouping or crossing).audience-generate-list — build from an owned list (customers, leads, accounts). Today resolve-only (a tight matched set) or expand (the widest identity-matched set, for maximum addressable reach) — both a roster of entity IDs — lookalike (profile the list and hand back the signals that define it, as a tunable pool to build from), or overlay (score the resolved list against a pool of signals and rank it — lead-scoring / "who's hot" / intersection); group and traverse aren't available there.audience-analyze (read who the landed audience reaches), audience-activate (export it), and /watt:explore (keep exploring — the stack's signals carry back as covered territory) — offered at the landing, on the user's say-so.Same translation table as explore — the user talks plain business language, you carry the boolean shape silently:
| User says | You hold internally |
|---|---|
| a must-have — "everyone has to be a homeowner" | an AND condition (must_have) |
| a defining behavior or interest | an OR in the core (core) |
| an exclusion — "not industry professionals" | an AND_NOT (exclusion) |
| a place — "in Colorado", "Nashville metro" | a geo-boundary signal, added as a must-have |
| "signal" | what the MCP calls a trait |
| target size, "about 1–5M people" | the band — floor and ceiling (the audience-generate-search leaf's band landing mode) |
| "the highest-intent / most likely people", in-market, high-intent | precision — the leaf's landing mode that picks signals by lift over a must-have base |
No AND/OR/AND_NOT, no boolean "pools" (the OR/AND/AND_NOT groupings), no "boolean expression" in anything the user sees: say must-haves, the signals themselves, exclusions. (Signal pool — the kept-signals carrier from explore or lookalike — is the user's word and fine.) Unlike explore, this surface may say audience, build, compose, signal stack — that is its job. Score is fine; show the numbers, skip the formula names.
audience-generate-search, carrying everything already said; the leaf elicits only what's missing. With one leaf today, route there silently.audience-generate-list anchor — route there (resolve-only for a tight match, expand for the widest match set, lookalike to profile the list for the signals that define it, or overlay to score the resolved list against signals and rank it; group and traverse aren't available there). A list with a read intent ("who are these people") is audience-analyze-list instead./watt:audience with no brief yet. Ask for the brief: "Describe the audience in plain English — who they are, what they're doing or into, and roughly how many people you need."/watt:explore. A session's signal pool seeds the working set: the picked signals carry in through session context, each with its hash, role, and evidence. Skip discovery for the angles it covers; route to the leaf, which elicits its target and goes to compose. A signal carried in flagged unverified keeps its caveat through scoring — its figures are carried claims, never presented as refreshed.audience-analyze (its -search flavor profiles a market from a brief, and writes the shareable report). Don't build it here.audience-generate-list, which resolves it to a matched roster. (To read a supplied list as aggregates, audience-analyze-list.) A build from signals still starts from a description — audience-generate-search.audience-generate-list, which skips the resolve and runs the play on the set directly (overlay / lookalike today). A roster with a read intent is audience-analyze-list; an export intent, audience-activate.The leaf settles its target and owns the compose; everything else is here, composed with verbatim — not restated in the leaf. One decision per turn, landed as native clickable options — same gate discipline as explore. Track each heavy inline step as a session task; if the host has no task tools, skip silently.
The brief must carry at least one defining behavior, interest, or intent. A pure-demographics brief isn't composable — demographics gate an audience, they don't define one; ask what these people are doing or into. There is no location step: a place is a geo-boundary signal (state / county / DMA / ZIP), added to the working set as a must-have during discovery if the user wants one — looked up as a geo trait, never a guessed hash. (Arbitrary radius-around-a-point filtering is a separate objective leaf, not this spine.)
Read the brief as its angles (the distinct concepts inside it, in the user's words), confirm the reading, and end that beat on the user's call — walk the angles one at a time, or sweep them all at once. Both are a single decision; neither runs heavy work unasked:
Run discovery inline on the main thread following the discovery procedure — read it once this session with cat "<Bundle root>/context/discovery.md" (the path published at session start), then follow it:
entity_type: "person", domain hints the conversation implies, and reactions so far. One angle per pass; the next waits for its own beat.full-scope discovery pass returns the concept-grouped pool at once. Keep the user out of a serial wait: fire a concept's phrase searches concurrently (several trait_search calls in one turn) so the sweep still lands in one beat.Narrate your searches and give a one-line read ("38 candidates behind the hiking angle; nothing tight for 'trail snacks'"). Discovery validates and enriches every candidate (similarity, size, domain, skew, freshness, hash) and flags anything unmatched — never invent a signal.
Run the scoring procedure inline — read it once this session with cat "<Bundle root>/context/scoring.md", then follow it — to score the gathered candidates by trait_hash, with the brief as the grounding frame (so relevance is comparable across angles) and a role-appropriate weights vector, because each role wants a different kind of signal. Score once per non-empty role (scoring takes one weight vector per call):
relevance leads; a light freshness positive; and one signed size-family axis — the breadth element of this role's weights vector, the lean the objective leaf sets in the scoring call (-search tunes it from the landing mode — negative when the landing wants distinctive, narrow signals, because distinctiveness is what separates targeting from raw reach — a trait nearly everyone carries targets no one; by the band's scale in band mode; strongly positive in max-reach mode). Size stays out of the score — the strategy procedure converges to the target by measuring reach, so scoring size too would double-count it.breadth + relevance): a narrow gate quietly becomes the whole definition.specificity + relevance): a broad exclusion over-cuts.At most one size-family axis per vector. rarity / specificity / breadth / size are views of one fact (specificity = 1 − breadth over a shared universe — see context/signal-metrics.md); a weights vector that names two of them scores that fact twice, and the model flags it (collinearity_warning). The signed breadth element carries the whole lean — distinctiveness is its negative direction, never a second axis.
The scoring procedure runs the scoring model (scripts/signal_profile.py) and returns each signal's feature vector (relevance · freshness · rarity/specificity · breadth/size · coverage) and a composite score under its role's weights — the read of how each pool stacks up. The model owns the math; never hand-score a signal — the scoring procedure, which runs scripts/signal_profile.py, is the single scoring path. The scoring procedure's per-signal raw fields ride into the working set and onward to the compose step — each working-set signal carries trait_hash, name, role, score, size, plus prevalence and freshness from the profile: the broad compose derives its ungated universe from prevalence, and the lift compose breaks ranking ties on freshness, so dropping either field silently degrades those composes.
Show the scored slice, grouped by role: name · what it means · ~size · relevance · freshness · rarity/specificity · score · role — strongest ~5–8, an honest count of the rest, sizes human-rounded (417K, 2.1M), the score on every row, any signal that arrived without a hash or couldn't be enriched clearly flagged. Scores are role-appropriate, so "why is X above Y" is answerable within a role group (a core signal's score and a gate's aren't on the same basis — never cross-rank them); the scoring axes visibly drive each score. Then end the turn at the decision: which signals join the working set, which are out — same pick mechanics as explore (multi-select for keeps, "Other" carries number-picks and steers).
Picks apply immediately. On every working-set change, do two things: re-show the working set — signals grouped by role in plain English, each with size, freshness, and score carrying the ranking, the strongest few shown with an honest count of the rest, the same structure every time — and re-write the record file per the record contract (context/record.md). The contract loads on demand, not at session start: the first time this flow writes a record, read it from the bundle — cat "<Bundle root>/context/record.md" (the path published at session start) — then honor it for every record this session. What the record file holds:
# Watt record · kind: pool · audience: weekend hikers · target: band 1M–5M
# angles: hiking=covered · fitness=open excluded-signals: 2 (retail employees, gear resellers) dropped candidates: 5
role,name,size,freshness,score,trait_hash
core,In-market: Hiking Gear,~2.1M,fresh,0.84,3fa4b2…
core,Outdoor recreation interest,~9.8M,standard,0.31,c0903a…
must-have,Colorado resident (geo),~4.4M,,,0334a6…The header carries the leaf's target (target: band 1M–5M for -search). Hashes ride along (the inline compose and read steps take them by hash); the angles: header tracks open/covered so convergence survives compaction — the file carries it.
Lift — how much likelier a population is to carry a trait than average — appears at later beats, never as a working-set curation column; during curation, working-set figures stay Signal Graph facts: size, freshness, score. It surfaces twice afterward: the precision landing mode's lift strategy measures each candidate's lift over the must-have base at compose (in -search), and the audience-analyze read measures lift over the built audience after. If the user asks for lift mid-curation, say which of those they mean and offer it at its beat.
Pivots are one per turn, any order: exclude a signal, flip a role, go deeper on an angle, probe a new one, add a named concept (a fresh narrow discovery pass), add a geo-boundary signal, or ask for adjacencies (the inline adjacencies procedure). Exclusions are explicit-include only — a proposed exclusion isn't in the working set until the user confirms it, because a mis-applied exclusion silently distorts the build. New signals from any pivot go through the scoring procedure before they're shown.
This is the leaf's lane: it settled the objective (e.g. -search's size band, or its grouping dimension) and offers the strategy subset that fits it. When every angle is covered or consciously dropped, the leaf offers the step and — only on the user's go — runs the chosen strategy procedure inline (its context/*.md) with the working set (in scored order, pivots applied) and the target. A compose strategy returns a signal stack whose reach is measured at every step; a Classify strategy returns a roster — group partitions a base set into ranked groups, traverse crosses the employment graph (a composed seed → its employers, or employees of named companies) to a qualified set (membership only — ranking or segmenting it is a separate Classify step). Never present arithmetic over per-signal sizes as a count. The leaf renders the trace and owns what happens at the target's edge. See the leaf for the specifics.
This step lands a signal stack. A leaf running a Classify strategy (e.g. -search's grouping objective) lands a roster record instead — its own serialization, owned by the leaf; see the leaf for that shape. The stack landing:
On an accepted stack, write the final audience record to the record file per the record contract (context/record.md) — the artifact the rest of the flow consumes:
# Watt record · kind: stack · audience: weekend hikers
# reach: 2.4M (band 1M–5M)
# location: none (national) entity_type: person
# workflow: 550e8400-… sample: 10 entity IDs held in session
role,name,trait_hash
defining,In-market: Hiking Gear,3fa4b2…
defining,Outdoor recreation interest,c0903a…
must-have,Colorado resident (geo),0334a6…
exclusion,Gear resellers,9b81de…This record is the stack's canonical serialization: names ride beside the hashes so a re-supplied record stays readable to every downstream step, and the role column (defining / must-have / exclusion) carries the expression exactly as composed — any-of / all-of / none-of. The location header carries any radius/boundary filter the compose applied — distinct from a geo-boundary signal, which lives in the rows; the strategies and the downstream steps take location as its own input. Its re-supply consumers (audience-analyze-signal, audience-activate) parse it, and a record without it silently widens a re-run — so the line is always present, written none (national) when no filter rode along, never dropped. The reach header carries the landing mode's target: 2.4M (band 1M–5M) for greedy, 84M (max-reach) for broad, 180K · precision (lift over base) for lift; a downstream refresh re-writes the record (per the record contract) carrying the · refreshed suffix on that line. Show the landed audience — role groups, per-signal sizes and freshness, measured reach against the target. Offer the downstream in one line, in plain words: read who these people actually are (audience-analyze), export it (audience-activate — Meta, Google, Reddit, and TikTok), or keep exploring around it (/watt:explore — the stack's signals carry back as covered territory, and the walk resumes on what's adjacent). Any of them runs on their say-so.
audience-activate step's job, behind its own confirmation. (The record file generate writes is the composition itself — signals and hashes, never people.)audience-analyze (its -search flavor profiles a market and writes the report); generate builds toward a target./watt:configure (it owns the connect path and recovery docs), and never loop the connect / authenticate tools, diagnose the MCP registry, or press on. (A pre-call notice isn't a failure; your first call settles it — never announce one.)880e260
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.