flow
<tool_restrictions>
MANDATORY Tool Restrictions
BANNED TOOLS — calling these is a skill violation:
EnterPlanMode— BANNED. Do NOT call this tool. This skill manages its own workflow.ExitPlanMode— BANNED. You are never in plan mode. There is nothing to exit.
REQUIRED TOOLS:
AskUserQuestion— ALWAYS use this for questions. Never ask questions as plain text. Every question — confirming dev server, auth status, handling stale flows — MUST useAskUserQuestion. Keep context before the question to 2-3 sentences max. </tool_restrictions>
User Flow Discovery & Verification
Discover user flows from your codebase, store them as walkable artifacts, and execute them in Chrome to verify they work.
Announce at start: "I'm using the flow skill to [discover/walk/check] user flows."
Mode Dispatch
Parse the first argument to determine mode:
| Argument | Mode | Description |
|---|---|---|
discover |
Discover | Scan codebase, generate flow artifacts |
walk |
Walk | Execute stored flows in browser |
check |
Check | Detect drift via file checksums |
| (none) | Smart routing | Assess state, ask what to do |
Mode: Smart Routing (no arguments)
When invoked without a mode argument, assess the current state and route intelligently.
Step 1: Check State
Check if docs/arc/flows/ exists and read any .md files. Count flows by status.
Step 2: Route Based on State
No flows exist (directory missing or empty):
"No user flows discovered yet. I'll scan your codebase for routes and generate walkable flows."
→ Proceed directly to Discover mode. No question needed — discovery is the only useful action.
Flows exist, some are stale: Show a brief status summary, then ask:
AskUserQuestion:
question: "[X] flows found ([Y] stale). What would you like to do?"
header: "User Flows"
options:
- label: "Check for drift"
description: "[Y] flows may be out of date — check which source files changed"
- label: "Walk all flows"
description: "Execute all [X] flows in Chrome to verify they work"
- label: "Re-discover"
description: "Regenerate stale flows from current code"
- label: "Discover new routes"
description: "Scan for routes not yet covered by flows"
Flows exist, none stale, some not yet walked:
AskUserQuestion:
question: "[X] flows found ([V] not yet walked). What would you like to do?"
header: "User Flows"
options:
- label: "Walk all flows"
description: "Execute flows in Chrome to verify they work"
- label: "Walk unwalked only"
description: "Just the [V] flows that haven't been tested yet"
- label: "Check for drift"
description: "See if source files have changed since discovery"
- label: "Discover new routes"
description: "Scan for routes not yet covered"
Flows exist, all current and passed:
AskUserQuestion:
question: "All [X] flows are current and passing. What would you like to do?"
header: "User Flows"
options:
- label: "Walk all flows"
description: "Re-run all flows to verify they still pass"
- label: "Check for drift"
description: "See if any source files have changed"
- label: "Discover new routes"
description: "Scan for routes not yet covered"
Flows exist, some failed:
AskUserQuestion:
question: "[X] flows found ([Z] failing). What would you like to do?"
header: "User Flows"
options:
- label: "Walk failed flows"
description: "Re-run the [Z] failing flows to see if they pass now"
- label: "Walk all flows"
description: "Execute all [X] flows"
- label: "Check for drift"
description: "See if source files have changed"
- label: "Re-discover failed"
description: "Regenerate the failing flows from current code"
After the user selects, proceed to the corresponding mode.
Mode: Discover
Step 1: Detect Framework
Check package.json for framework:
| Check | Framework | Route glob pattern |
|---|---|---|
"next" in dependencies |
Next.js App Router | app/**/page.{tsx,jsx,ts,js} (exclude app/api/**) |
"next" + pages/ dir exists |
Next.js Pages Router | pages/**/*.{tsx,jsx,ts,js} (exclude pages/api/**) |
"@sveltejs/kit" in dependencies |
SvelteKit | src/routes/**/+page.svelte |
"@remix-run" in dependencies |
Remix | app/routes/**/*.{tsx,jsx} |
If no framework detected:
"Could not detect a supported framework. Supported: Next.js, SvelteKit, Remix.
Is this the right directory?"
Stop.
Step 2: Glob for Routes
Use the detected glob pattern to find all route files.
If zero routes found:
"No route files found using pattern [pattern]. Is this the right directory?
Detected framework: [framework]"
Stop.
Step 3: Classify Routes (Auth)
Read middleware/proxy file to determine which routes are protected:
| Framework | Auth file to check |
|---|---|
| Next.js | proxy.ts, middleware.ts, src/proxy.ts, src/middleware.ts |
| SvelteKit | src/hooks.server.ts |
| Remix | app/root.tsx or route-level loaders |
Detect auth provider by scanning package.json dependencies and middleware:
| Signal | Provider |
|---|---|
@clerk/nextjs + clerkMiddleware |
Clerk |
@workos-inc/authkit-nextjs + authkitMiddleware |
WorkOS |
| Session/JWT patterns without known provider | Self-rolled |
| No auth middleware | None |
| Both Clerk and WorkOS detected | Ask user which is primary |
Classify each route:
- Routes explicitly in public matchers →
auth: none - Routes behind middleware protection →
auth: user - Routes with role checks (admin, org) →
auth: adminorauth: org-admin - If unclear → default to
auth: user(safer to over-classify)
Step 4: Check Existing Flows
If docs/arc/flows/ already has .md files:
Read each existing flow file's route field. Build a set of already-discovered routes.
Check if --force was passed as an argument.
- Without
--force: Skip routes that already have a flow file. Report: "Found X existing flows. New routes will be added, existing flows preserved." - With
--force: Overwrite all. Report: "Found X existing flows. All will be regenerated (--force)."
Step 5: Group Routes
Group remaining routes by top-level path segment:
/→rootgroup/about,/pricing→marketinggroup (or by first segment)/dashboard/*→dashboardgroup/settings/*→settingsgroup
Max 10 routes per group. If a group exceeds 10, split it.
Step 6: Dispatch Flow Discoverer Agents
For each route group, dispatch the flow-discoverer agent via a parallel Agent tool call:
Agent flow-discoverer: "Discover user flows for these routes.
Framework: [detected framework]
Auth provider: [detected provider]
Today's date: [YYYY-MM-DD]
Routes in this group:
1. [route path] → [file path] (auth: [level])
2. [route path] → [file path] (auth: [level])
...
Read each route's component code, follow imports to find interactive elements,
and generate flow artifacts using the step DSL.
See agents/workflow/flow-discoverer.md for the full protocol."
Dispatch all groups in a single message for maximum parallelism.
If an agent errors or times out, log a warning and continue with other groups:
"Warning: Could not discover flows for [group name] routes. [X] other groups completed successfully."
Step 7: Write Flow Files
Parse each agent's output for flow artifacts (separated by --- FLOW: <name> ---).
For each flow artifact:
- Create
docs/arc/flows/<name>.mdwith the artifact content - If the file already exists and
--forcewas not passed, skip it
Step 8: Report
Discovery complete:
- Routes scanned: X
- Flows generated: Y (Z public, W authenticated)
- Skipped (already existed): V
- Auth provider: [provider]
Flows written to docs/arc/flows/
Next steps:
- /arc:flow walk --all — Execute all flows in Chrome
- /arc:flow check — Check for drift later
Mode: Walk
Step 1: Select Browser Tool
Browser tool hierarchy:
- Chrome MCP (preferred) —
mcp__claude-in-chrome__*tools - agent-browser —
/agent-browserskill as fallback outside Claude Code - Playwright — scripted browser fallback as last resort
Check for Chrome MCP availability first. If Chrome MCP tools are not available, check for agent-browser. If neither is available, note that Playwright would need to be scripted manually and ask the user how to proceed.
Step 2: Confirm Dev Server
AskUserQuestion:
question: "What's your dev server URL?"
header: "Dev server"
options:
- label: "localhost:3000"
description: "Default Next.js / Remix dev server"
- label: "localhost:5173"
description: "Default Vite / SvelteKit dev server"
- label: "localhost:4321"
description: "Default Astro dev server"
- label: "Custom URL"
description: "I'll type the URL"
Step 3: Verify Dev Server
Navigate the browser to the base URL:
mcp__claude-in-chrome__tabs_context_mcp (get current tabs)
mcp__claude-in-chrome__navigate to [base URL]
If the page fails to load or shows a connection error:
"Dev server doesn't seem to be running at [URL]. Please start it and try again."
Stop.
Step 4: Read Flow Files
Read all .md files from docs/arc/flows/.
If no flows found:
"No flows found in docs/arc/flows/. Run /arc:flow discover first."
Stop.
Filter flows:
--flow <name>: Walk only the named flow--all: Walk all flows- No filter specified: Ask which flows to walk
Sort order: Public flows first (auth: none), then authenticated flows.
Stale flow handling:
- If stale flows are selected and
--forcewas NOT passed:AskUserQuestion: question: "Some selected flows are stale (source files changed). Walk them anyway?" header: "Stale flows" options: - label: "Walk anyway" description: "Run the flows as-is — they may fail due to code changes" - label: "Skip stale" description: "Only walk current flows" - label: "Re-discover first" description: "Regenerate stale flows from current code, then walk"
Step 5: Auth Gate
If any selected flows have auth other than none:
- Detect auth provider (scan
package.jsonand middleware) - Report:
"Your app uses [provider]. [X] flows require authentication. Please log in to your app at [base URL] in Chrome, then confirm." - Ask:
AskUserQuestion: question: "Are you logged in?" header: "Authentication" options: - label: "Yes, I'm logged in" description: "Continue with all flows including authenticated ones" - label: "Skip authenticated flows" description: "Only walk public flows for now"
Step 6: Walk Each Flow
For each flow in order (public first, then authenticated):
-
Navigate to
[base URL][route]mcp__claude-in-chrome__navigate to [full URL] -
Execute each step sequentially:
- navigate:
mcp__claude-in-chrome__navigate - click:
mcp__claude-in-chrome__findto locate element, thenmcp__claude-in-chrome__computerto click - fill:
mcp__claude-in-chrome__form_inputwith selector and value - select:
mcp__claude-in-chrome__form_inputwith selector and value - wait:
mcp__claude-in-chrome__findwith retries
- navigate:
-
Check expectations after each step:
expect: url /path— read current tab URL, check it ends with/pathexpect: visible "text"—mcp__claude-in-chrome__findfor the textexpect: heading "text"—mcp__claude-in-chrome__findfor heading with textexpect: not-visible "text"—mcp__claude-in-chrome__findreturns no match
-
Resolve
$ENV_VARvalues before filling:echo $TEST_EMAILIf a variable is not set, abort with: "Missing environment variable: [VAR]. Set it before walking authenticated flows."
-
Translate
>>selectors:button >> "Create Account"→ usemcp__claude-in-chrome__findwith queryCreate Account, then verify the matched element is a button, then click it. -
On step failure (element not found, expectation failed, timeout):
- Record: flow name, failing step index, step text, error message
- Mark flow as
failed - Skip remaining steps
- Move to next flow
-
On all steps passed:
- Mark flow as
passed
- Mark flow as
Step 7: Update Flow Files
For each walked flow, update the frontmatter:
- Set
last_walked: YYYY-MM-DD - Set
status: passedorstatus: failed
Use the Edit tool to update only the frontmatter fields, preserving the rest of the file.
Step 8: Report Results
Walk complete:
Passed (X):
✓ signup-create-account
✓ home-view
✓ dashboard-view
Failed (Y):
✗ settings-profile-edit — Step 3: fill [name="phone"] → element not found
✗ checkout-submit — Step 5: expect url /confirmation → got /checkout/error
Skipped (Z):
○ admin-users-list — auth required, skipped
Mode: Check
Step 1: Read All Flows
Read all .md files from docs/arc/flows/.
If no flows found:
"No flows to check. Run /arc:flow discover first."
Stop.
Step 2: Compute Current Checksums
For each flow, read the source_files list from frontmatter.
For each source file:
sha256sum <file path> | cut -c1-8
If a source file doesn't exist:
- Record: "source file not found: [path]"
- Mark flow as needing staleness update
Step 3: Compare Checksums
For each flow:
- If all checksums match stored values → flow is current
- If any checksum differs OR any source file is missing → flow is stale
Step 4: Update Stale Flows
For each stale flow:
- Set
status: stalein frontmatter - If a source file was deleted, add to Notes: "Source file not found: [path] (detected [date])"
Step 5: Report
Drift check complete:
Current (X):
✓ home-view
✓ signup-create-account
Stale (Y):
⚠ dashboard-view — src/components/dashboard-stats.tsx changed
⚠ settings-profile-edit — src/app/settings/profile/page.tsx changed
⚠ admin-users-list — src/components/admin/user-table.tsx deleted
Step 6: Offer Next Steps
If stale flows found:
AskUserQuestion:
question: "What would you like to do with the stale flows?"
header: "Stale flows"
options:
- label: "Re-discover"
description: "Regenerate stale flows from current code"
- label: "Walk anyway"
description: "Run the stale flows to see if they still pass"
- label: "Do nothing"
description: "Just the report for now"
If "Re-discover": Run the Discover mode for only the stale routes (re-read the route files, dispatch flow-discoverer for those routes, overwrite the stale flow files).
If "Walk anyway": Run the Walk mode with --force for the stale flows.
<required_reading> Read before running:
docs/arc/specs/2026-03-13-user-flows-design.md— Full design spec with decisions and rationalereferences/authentication.md— Auth patterns for Clerk, WorkOS, self-rolled (when auth is detected)references/platform-tools.md— Browser tool mappings across platforms </required_reading>
<progress_append> After completing any mode, append to progress journal:
## YYYY-MM-DD HH:MM — /arc:flow [mode]
**Task:** [Mode] user flows
**Outcome:** [Complete / Partial]
**Details:**
- Mode: [discover/walk/check]
- Flows: [count by status]
- Auth: [detected provider]
**Next:** [suggested next step]
---
</progress_append>