skills/conf-theme-clustering/SKILL.md
Cluster a conference's event records into a small set of coarse themes with finer sub-clusters, an explicit outlier bucket, and soft (multi-membership) affinities — using the hybrid embed-then-label pipeline (embed abstracts, reduce, density-cluster, then LLM-label the clusters) when embedding libraries are available, and an LLM-reasoned hierarchical fallback when they are not. Embeddings do the grouping; the LLM only names the groups. Conference-agnostic. Use when turning structured event records into a navigable theme map for preference elicitation and scheduling, when you need 6-8 reasonable themes rather than 20 muddy ones, or when overlapping talks must belong to more than one theme. Trigger keywords - theme clustering, cluster talks, embed then label, soft membership, outlier talks, conference themes, topic map.
npx skillsauth add lyndonkl/claude conf-theme-clusteringInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
The job is to turn a few hundred event records into a map a person can actually reason over: a handful of coarse themes, each openable into sub-clusters, with the genuinely odd talks set aside in an outlier bucket rather than forced into a theme, and with each talk allowed to belong to more than one theme because real talks do. The map is the substrate everything downstream stands on — the elicitor grounds its questions in these themes, and the scheduler uses theme coverage to reward breadth.
The method that holds up is hybrid, not pure-LLM-per-item and not the conference's own track labels. Embeddings do the grouping because that step must be cheap and stable across runs; an LLM does only the labeling because that is the step that needs to read like a human wrote it. Running an LLM to decide every talk's cluster is costly and varies run to run; using the conference's published tracks throws away the cross-track structure that is often the most interesting thing in the program.
Four findings shape the design, and each is a guardrail later: embeddings group / LLM labels; keep the outliers (density clustering hands you an outlier set — that is a feature); cluster coarse, then fine (6–8 abstract themes separate far more cleanly than 20 fine-grained ones); and soft multi-membership (a single hard cluster mis-represents a talk like "evaluating agentic RAG" that legitimately lives in evaluation and agents and retrieval).
clusters.json{
"method": "embed-hdbscan | llm-reasoned",
"generated_on": "YYYY-MM-DD",
"stability_note": "how stable these clusters are; what would move a talk",
"coarse_themes": [
{
"id": "T1",
"label": "human-readable theme name",
"gloss": "one sentence: what unifies this theme",
"size": 0,
"representative_event_ids": ["..."],
"subclusters": [
{ "id": "T1.1", "label": "...", "gloss": "...", "event_ids": ["..."] }
]
}
],
"outliers": [ { "event_id": "...", "why": "why it fits no theme — often why it is interesting" } ]
}
affinities.json{
"generated_on": "YYYY-MM-DD",
"top_k": 3,
"affinities": {
"<event_id>": [ { "theme_id": "T1", "affinity": 0.0 }, { "theme_id": "T4", "affinity": 0.0 } ]
}
}
Every event appears in affinities with its top-k theme affinities (descending). An outlier still gets affinities — its top affinity is just low, which is exactly what marks it as an outlier.
When embedding libraries are available (resources/cluster.py):
label + gloss. This is the only LLM step in the grouping, and it touches clusters, not items.The division of labor is the whole point: the deterministic step decides who is grouped with whom (stable, cheap), the language step decides what to call the group (fluent, human).
Aim for 6–8 coarse themes — few enough to hold in your head and to ground elicitation questions in. Get the coarse layer by clustering at low resolution (or merging fine clusters up). Then, within each coarse theme, expose sub-clusters for the talks that share a tighter sub-topic. Do not ship 20 flat clusters: at that resolution the boundaries blur and the elicitor cannot present a clean "A vs B" contrast. Coarse-with-drill-down beats flat-and-many.
The talks that fit no dense region are not noise to be hidden — they are frequently the cross-disciplinary, against-the-grain sessions that a curious attendee most wants surfaced. Put them in outliers with a why, and make sure they survive into elicitation (the elicitor's selection-bias probes draw from exactly here). Forcing an outlier into the nearest theme both corrupts that theme and buries the talk.
A hard assignment says a talk is only about evaluation. Reality is fuzzier. Give every event its top-k theme affinities (k≈3): the cosine similarity to each theme centroid (or HDBSCAN soft-membership probabilities), normalized. Now "I'm interested in agents" and "I'm interested in evaluation" both surface the agentic-eval talk, instead of it hiding in whichever single bucket it landed. The coarse map stays clean for navigation; the affinities carry the overlap.
When sentence-transformers/umap-learn/hdbscan are unavailable, or N is small enough that density clustering is unstable, cluster by reasoning over the already-extracted axes.topic and abstracts:
Set method: "llm-reasoned" and a stability_note flagging that the grouping is reasoning-derived (less reproducible than embeddings). The fallback produces the same two output contracts — downstream stages cannot tell which method ran except by reading method.
Always set method and write a stability_note: how reproducible the clusters are, and what would move a talk between themes. Clustering is a modeling choice, not ground truth; the note is what lets the elicitor and the human treat theme boundaries as provisional rather than as fact.
□ Step 1: Load events.json; assemble each event's text (enriched abstract ?? raw abstract + title + topics).
□ Step 2: Choose the method. Try resources/cluster.py (embed-hdbscan). On missing libs / tiny N, use the
LLM-reasoned fallback. Record the choice in method.
□ Step 3: Produce the grouping — coarse clusters with a noise/outlier set.
□ Step 4: Collapse/merge to 6-8 coarse themes; expose sub-clusters inside each.
□ Step 5: LLM-label each coarse theme and sub-cluster (label + gloss) from representative members.
□ Step 6: Build the outlier bucket with a why per event.
□ Step 7: Compute top-k soft affinities for every event (including outliers).
□ Step 8: Write clusters.json + affinities.json + a human-readable clusters.md tour. Set stability_note.
Danger: Asking an LLM to assign every talk to a cluster — costly, and the assignment shifts run to run. Guardrail: The grouping step is deterministic (embeddings/topics); the LLM names clusters, not items. Red flag: A per-event LLM "which cluster?" call in the grouping loop.
Danger: Forcing every talk into a theme corrupts the theme and hides the most interesting sessions.
Guardrail: Maintain outliers as a first-class bucket with a why; pass it forward.
Danger: One-cluster-per-talk makes overlapping talks invisible to all but one interest. Guardrail: Every event carries top-k affinities. The coarse map is for navigation; affinities carry the overlap.
Danger: 20 flat clusters blur boundaries and break clean elicitation contrasts. Guardrail: 6–8 coarse themes, sub-clusters on demand.
Danger: Treating provisional clusters as ground truth.
Guardrail: Set method + stability_note; downstream stages treat boundaries as provisional.
Danger: Scope creep — re-deriving axes or making schedule picks. Guardrail: Consume the EventRecords as given; output only the map + affinities.
| Step | Operation | Who does it | Output | |---|---|---|---| | Embed | text → vector | embedding model | per-event vectors | | Reduce | high-dim → low-dim | UMAP | reduced vectors | | Cluster | density grouping | HDBSCAN | clusters + noise(outliers) | | Label | name the cluster | LLM | label + gloss | | Soft-assign | top-k affinities | cosine / soft-membership | affinities.json |
| Situation | Method | Note |
|---|---|---|
| Embedding libs present, N ≳ 30 | embed-hdbscan via resources/cluster.py | most stable |
| No libs, or N small | llm-reasoned fallback | record lower stability |
| Either | always | 6–8 coarse + sub-clusters + outliers + soft affinities |
Coarse theme T3 "Evaluation & Reliability" (gloss: "measuring whether agents/models actually work and keeping them working"), sub-cluster T3.2 "Agent eval harnesses". The talk "Evaluating Agentic RAG" lands in T3.2 by hard clustering — but its affinities are [{T3,0.71},{T5_Agents,0.66},{T2_Retrieval,0.58}], so it also surfaces when the attendee says "I care about agents" or "I care about retrieval." The hard map stays clean; the soft affinities carry the truth.
"What Octopuses Teach Us About Distributed Cognition" matches no dense region. It goes to outliers with why: "biology/cognition crossover; no topical neighbors in this program". It still gets affinities (top affinity ~0.3 to "Agents"), and the elicitor later offers it as a deliberate selection-bias probe — the kind of talk a narrowed model of the attendee would never surface on its own.
development
Build a personal conference schedule as a constraint-optimization problem — hard constraints (no time overlap, room-to-room travel time, capacity/registration, the attendee's own must-attends and blackouts) plus a user-owned weighted objective trading interest against breadth, pacing (maximize contiguous free time), and serendipity. Surfaces unbreakable conflicts (two high-value overlapping talks the model cannot rank) as decisions for the human rather than silently picking, and reports what each choice traded away. Conference-agnostic. Use to turn a preference profile plus a theme map into a day-by-day plan, to resolve overlapping sessions, or to balance a packed vs paced schedule. Trigger keywords - schedule optimization, conference schedule, constraint optimization, overlapping talks, contiguous free time, conflict surfacing, packed vs paced.
development
Parse a heterogeneous conference program (markdown, HTML, PDF-derived text, or JSON) into normalized event records with per-field confidence scores and independent classification axes (topic, depth, format, prerequisites, recorded, capacity). Detects the program's format before extracting, treats every inferred field as uncertain (present vs inferred vs missing), and flags thin or missing abstracts so downstream enrichment can target them. Conference-agnostic. Use when ingesting a conference or event schedule into a structured store, normalizing a talk/session list, or extracting per-session metadata with calibrated confidence. Trigger keywords - program ingestion, parse schedule, session extraction, event records, conference program, talk metadata, per-field confidence.
development
Build a personalized preference profile from a small number of well-chosen, cluster-grounded questions instead of a long survey. Represents the person's interests as an uncertainty region over the theme map, picks the single highest-information-gain choice-based question (contrasting real talks from different clusters), balances exploiting known interests against exploring uncertain ones, deliberately injects outlier probes to fight selection bias, and stops as soon as the schedule would be stable. Also elicits the user-owned objective weights and hard constraints. Interactive — runs where it can actually ask the person. Conference-agnostic. Use to turn a theme map into a preference profile, to decide what to ask a conference attendee, or to elicit scheduling priorities. Trigger keywords - preference elicitation, ask few questions, information gain, choice-based questions, selection bias probe, objective weights, attendee preferences.
testing
The multi-agent communication and orchestration discipline for a staged pipeline whose stages each carry calibrated uncertainty. Agents talk through files with defined schemas and status fields (never free text); the orchestrator passes input paths plus an explicit output path plus a return contract, verifies each artifact's confidence before advancing a stage gate, holds and integrates worker outputs rather than passing them through, freezes its protected control logic against a checksum, maintains a diversity floor, hardens the fragile handoffs not the resilient hubs, and names the substitution effect every optimized score produces. Conference-agnostic; preloaded by a pipeline orchestrator. Use when coordinating a staged agent pipeline, designing the orchestrator-worker contract, or deciding when to advance, reconcile, or escalate. Trigger keywords - pipeline orchestration, stage gates, structured agent communication, invariant guard, confidence propagation, conflict surfacing.