Flow Detector
Detect end-to-end flows for a vague task. Output is structured candidates, not questions.
Activation Triggers
This skill activates when:
- The user runs
/clarify "<task>" or invokes @clarifier.
- The user describes a task using vague verbs: send, notify, sync, handle, process, integrate, support, fire, dispatch, push, alert, log.
- The user mentions a ticket reference and the description doesn't fully pin down the implementation surface.
Core Principle
Every candidate flow is a complete answer, not a piece of one. The user picks between whole flows, not between individual dimensions. Do not emit candidates that differ only in trivial parameters (timeout values, log level, retry count) — those are defaults inside one flow.
Process
1. Read the task description
Extract:
- The verb (what is being done)
- Any explicit entity mention (User, Tenant, Order, etc.)
- Any explicit trigger mention (on signup, after payment, etc.)
- Any explicit recipient mention (to the user, to the team, etc.)
2. Ground in the codebase
Before generating candidates, scan the repo for evidence:
- Entities: search for likely model files —
User, Tenant, Org, Account, Workspace, Team, Member. Note which exist.
- Auth surfaces: search for
sign-in, signin, login, signup, register, authenticate, auth/, /api/auth. Note routes/handlers.
- Notification channels: search for email senders (
sendgrid, ses, resend, mailer), push (fcm, apns, expo), in-app (notifications table/service), SMS (twilio).
- Async infrastructure: queues (
bullmq, celery, sidekiq), background jobs, event buses.
- Failure infrastructure: retry libs, dead-letter queues, error reporters (
sentry, bugsnag).
Anchor every candidate in symbols you found. A candidate that mentions infrastructure the repo doesn't have is a defect — either reframe the candidate as "build new X" or drop it.
3. Generate candidates
A candidate flow is a record:
{
"id": "kebab-case-id",
"title": "Short human-readable name",
"one_line_diff": "What makes this candidate different from the others",
"full_context": {
"entity": "...",
"trigger": { "event": "...", "timing": "before|after|on", "preconditions": ["..."] },
"action": { "verb": "...", "channel": "...", "recipient": "..." },
"flow": { "services": ["..."], "mode": "sync|async", "transport": "..." | null },
"failure_handling": { "strategy": "retry|silent|block|escalate", "details": "..." },
"out_of_scope": ["..."]
}
}
Rules:
- Differentiate at the top level: candidates must differ in entity, trigger, or recipient. If two candidates differ only in retry strategy, merge them into one with the more conservative default.
- Cap at 4 candidates: if more than 4 are plausible, group similar ones or pick the top 4 by repo-evidence strength (which entities/services actually exist).
- Order by evidence strength: the most-supported candidate is
id: 1. If a single candidate dominates (no close runner-up), emit just that one.
- No fictional infrastructure: if a candidate would require a service the repo lacks, either reframe ("Build minimal in-app notification service, then dispatch") or drop it.
4. Decide single vs multiple
You return a list. The caller branches on length:
length === 1 → caller proceeds straight to artifact generation. Skill must be confident; do not pad to 2 candidates just to ask a question.
length >= 2 → caller invokes flow-selector.
When in doubt, return more candidates rather than fewer. The user picking between two clear options is cheap; building the wrong thing is expensive.
Examples
Example 1 — Single candidate
Task: "Add a 'Forgot password' link to the login form"
Repo: Has app/(auth)/login/page.tsx, an existing auth/forgot-password/route.ts handler, one auth surface.
Output: 1 candidate.
[
{
"id": "forgot-password-link-existing-flow",
"title": "Add Forgot password link wiring to existing /forgot-password handler",
"one_line_diff": "Single auth surface, handler already exists",
"full_context": { entity: "User", trigger: { event: "click", timing: "on", preconditions: ["on /login page"] }, … }
}
]
Example 2 — Multiple candidates
Task: "Send message on sign-in"
Repo: Has User and Tenant models, an in-app notifications table, an email sender, a sign-in route.
Output: 2 candidates.
[
{
"id": "user-post-auth-notify",
"title": "User sign-in flow — in-app message to the signing-in user",
"one_line_diff": "Recipient is the user who signed in; channel is in-app",
"full_context": { entity: "User", trigger: { event: "post-auth", timing: "after", preconditions: ["successful login"] }, action: { verb: "send", channel: "in-app", recipient: "signing-in user" }, … }
},
{
"id": "tenant-owner-audit-email",
"title": "Tenant audit flow — email to the tenant owner when a member signs in",
"one_line_diff": "Recipient is the tenant owner; channel is email",
"full_context": { entity: "Tenant", trigger: { event: "member-sign-in", timing: "after", preconditions: ["member belongs to tenant"] }, action: { verb: "send", channel: "email", recipient: "tenant.owner" }, … }
}
]
Example 3 — Repo has no infrastructure
Task: "Send SMS confirmation on order placed"
Repo: Has Orders, no SMS provider, no async queue.
Output: 1 candidate, framed as build-new.
[
{
"id": "build-sms-then-dispatch-on-order",
"title": "Build SMS infrastructure, then dispatch on order creation",
"one_line_diff": "No SMS provider in repo; candidate includes provisioning Twilio + queue",
"full_context": { entity: "Order", trigger: { event: "order.created", timing: "after", preconditions: ["payment captured"] }, … out_of_scope: ["replacing existing email confirmation"] }
}
]
Anti-Patterns
| Don't |
Do Instead |
| Emit 4 candidates that differ only in retry count |
Emit 1 candidate with a sensible retry default |
| Emit a candidate using a service that doesn't exist in the repo |
Reframe as "build new X" or drop |
| Pad to 2 candidates so the caller asks a question |
Trust the single-candidate path; it's the desired UX |
| Ask the user to fill in the entity / trigger |
Infer from the task + repo. Each candidate already includes them |
| Phrase candidates as questions ("Should we…") |
Phrase as completed proposals ("User sign-in → in-app message to user") |
Success Criteria
1---2name: flow-detector3description: Analyzes a task description plus the surrounding codebase and produces a list of candidate end-to-end flows. Activates when a developer describes a task using vague verbs (send, notify, sync, handle, process, integrate) without specifying entity, trigger, or recipient. Returns 1 candidate when the flow is unambiguous, 2+ when multiple plausible interpretations exist. Each candidate is a complete proposed execution context, never a question.4---56# Flow Detector78Detect end-to-end flows for a vague task. Output is structured candidates, not questions.910## Activation Triggers1112This skill activates when:13- The user runs `/clarify "<task>"` or invokes `@clarifier`.14- The user describes a task using vague verbs: **send, notify, sync, handle, process, integrate, support, fire, dispatch, push, alert, log**.15- The user mentions a ticket reference and the description doesn't fully pin down the implementation surface.1617## Core Principle1819**Every candidate flow is a complete answer**, not a piece of one. The user picks between whole flows, not between individual dimensions. Do not emit candidates that differ only in trivial parameters (timeout values, log level, retry count) — those are defaults inside one flow.2021## Process2223### 1. Read the task description2425Extract:26- The verb (what is being done)27- Any explicit entity mention (User, Tenant, Order, etc.)28- Any explicit trigger mention (on signup, after payment, etc.)29- Any explicit recipient mention (to the user, to the team, etc.)3031### 2. Ground in the codebase3233Before generating candidates, scan the repo for evidence:3435- **Entities**: search for likely model files — `User`, `Tenant`, `Org`, `Account`, `Workspace`, `Team`, `Member`. Note which exist.36- **Auth surfaces**: search for `sign-in`, `signin`, `login`, `signup`, `register`, `authenticate`, `auth/`, `/api/auth`. Note routes/handlers.37- **Notification channels**: search for email senders (`sendgrid`, `ses`, `resend`, `mailer`), push (`fcm`, `apns`, `expo`), in-app (`notifications` table/service), SMS (`twilio`).38- **Async infrastructure**: queues (`bullmq`, `celery`, `sidekiq`), background jobs, event buses.39- **Failure infrastructure**: retry libs, dead-letter queues, error reporters (`sentry`, `bugsnag`).4041Anchor every candidate in symbols you found. A candidate that mentions infrastructure the repo doesn't have is a defect — either reframe the candidate as "build new X" or drop it.4243### 3. Generate candidates4445A candidate flow is a record:4647```json48{49 "id": "kebab-case-id",50 "title": "Short human-readable name",51 "one_line_diff": "What makes this candidate different from the others",52 "full_context": {53 "entity": "...",54 "trigger": { "event": "...", "timing": "before|after|on", "preconditions": ["..."] },55 "action": { "verb": "...", "channel": "...", "recipient": "..." },56 "flow": { "services": ["..."], "mode": "sync|async", "transport": "..." | null },57 "failure_handling": { "strategy": "retry|silent|block|escalate", "details": "..." },58 "out_of_scope": ["..."]59 }60}61```6263Rules:6465- **Differentiate at the top level**: candidates must differ in entity, trigger, or recipient. If two candidates differ only in retry strategy, merge them into one with the more conservative default.66- **Cap at 4 candidates**: if more than 4 are plausible, group similar ones or pick the top 4 by repo-evidence strength (which entities/services actually exist).67- **Order by evidence strength**: the most-supported candidate is `id: 1`. If a single candidate dominates (no close runner-up), emit just that one.68- **No fictional infrastructure**: if a candidate would require a service the repo lacks, either reframe ("Build minimal in-app notification service, then dispatch") or drop it.6970### 4. Decide single vs multiple7172You return a list. The caller branches on `length`:7374- `length === 1` → caller proceeds straight to artifact generation. Skill must be confident; do not pad to 2 candidates just to ask a question.75- `length >= 2` → caller invokes `flow-selector`.7677When in doubt, return more candidates rather than fewer. The user picking between two clear options is cheap; building the wrong thing is expensive.7879## Examples8081### Example 1 — Single candidate8283Task: `"Add a 'Forgot password' link to the login form"`84Repo: Has `app/(auth)/login/page.tsx`, an existing `auth/forgot-password/route.ts` handler, one auth surface.8586Output: 1 candidate.87```88[89 {90 "id": "forgot-password-link-existing-flow",91 "title": "Add Forgot password link wiring to existing /forgot-password handler",92 "one_line_diff": "Single auth surface, handler already exists",93 "full_context": { entity: "User", trigger: { event: "click", timing: "on", preconditions: ["on /login page"] }, … }94 }95]96```9798### Example 2 — Multiple candidates99100Task: `"Send message on sign-in"`101Repo: Has User and Tenant models, an in-app `notifications` table, an email sender, a sign-in route.102103Output: 2 candidates.104```105[106 {107 "id": "user-post-auth-notify",108 "title": "User sign-in flow — in-app message to the signing-in user",109 "one_line_diff": "Recipient is the user who signed in; channel is in-app",110 "full_context": { entity: "User", trigger: { event: "post-auth", timing: "after", preconditions: ["successful login"] }, action: { verb: "send", channel: "in-app", recipient: "signing-in user" }, … }111 },112 {113 "id": "tenant-owner-audit-email",114 "title": "Tenant audit flow — email to the tenant owner when a member signs in",115 "one_line_diff": "Recipient is the tenant owner; channel is email",116 "full_context": { entity: "Tenant", trigger: { event: "member-sign-in", timing: "after", preconditions: ["member belongs to tenant"] }, action: { verb: "send", channel: "email", recipient: "tenant.owner" }, … }117 }118]119```120121### Example 3 — Repo has no infrastructure122123Task: `"Send SMS confirmation on order placed"`124Repo: Has Orders, no SMS provider, no async queue.125126Output: 1 candidate, framed as build-new.127```128[129 {130 "id": "build-sms-then-dispatch-on-order",131 "title": "Build SMS infrastructure, then dispatch on order creation",132 "one_line_diff": "No SMS provider in repo; candidate includes provisioning Twilio + queue",133 "full_context": { entity: "Order", trigger: { event: "order.created", timing: "after", preconditions: ["payment captured"] }, … out_of_scope: ["replacing existing email confirmation"] }134 }135]136```137138## Anti-Patterns139140| Don't | Do Instead |141|-------|------------|142| Emit 4 candidates that differ only in retry count | Emit 1 candidate with a sensible retry default |143| Emit a candidate using a service that doesn't exist in the repo | Reframe as "build new X" or drop |144| Pad to 2 candidates so the caller asks a question | Trust the single-candidate path; it's the desired UX |145| Ask the user to fill in the entity / trigger | Infer from the task + repo. Each candidate already includes them |146| Phrase candidates as questions ("Should we…") | Phrase as completed proposals ("User sign-in → in-app message to user") |147148## Success Criteria149150- [ ] Output is an ordered list of 1–4 candidates.151- [ ] Every candidate has a unique top-level differentiator (entity, trigger, or recipient).152- [ ] Every candidate is grounded in symbols actually found in the repo.153- [ ] No candidate is phrased as a question.154- [ ] When the task is unambiguous, exactly one candidate is returned.