← back to Cli Printing Press
fix(skills): goal-driven sniff strategy replaces page-crawl approach (#109)
125188d19069df3c0ad70c89313e763da6174848 · 2026-04-01 20:45:48 -0700 · Matt Van Horn
The sniff gate previously built a list of 10-15 URLs to load. This
fundamentally fails on SPAs where API calls fire on interaction, not
page load. The Domino's run proved this: loading pages caught 3
GraphQL ops; clicking through the order flow caught 25.
Changes:
- Add Step 0 (Identify the User Goal) before sniff capture starts.
Reads the research brief's Top Workflows and states the primary
goal explicitly ("Order a pizza for delivery").
- Replace "Build the page list" with "Build the user flow plan" --
a sequence of interactive steps (click, fill, submit) that walk
through the user's goal, not a list of URLs to load.
- Update agent-browser Step 2b explore loop to follow the same
flow-driven approach instead of generic element prioritization.
- Add Step 2c thin-results safety check: if < 5 endpoints found,
diagnose (did I interact?), re-sniff with interaction, compare
against community wrappers as a floor.
- Add User Goal Flow section to sniff discovery report template,
tracking which steps completed and what each triggered.
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Files touched
M skills/printing-press/SKILL.mdM skills/printing-press/references/sniff-capture.md
Diff
commit 125188d19069df3c0ad70c89313e763da6174848
Author: Matt Van Horn <mvanhorn@users.noreply.github.com>
Date: Wed Apr 1 20:45:48 2026 -0700
fix(skills): goal-driven sniff strategy replaces page-crawl approach (#109)
The sniff gate previously built a list of 10-15 URLs to load. This
fundamentally fails on SPAs where API calls fire on interaction, not
page load. The Domino's run proved this: loading pages caught 3
GraphQL ops; clicking through the order flow caught 25.
Changes:
- Add Step 0 (Identify the User Goal) before sniff capture starts.
Reads the research brief's Top Workflows and states the primary
goal explicitly ("Order a pizza for delivery").
- Replace "Build the page list" with "Build the user flow plan" --
a sequence of interactive steps (click, fill, submit) that walk
through the user's goal, not a list of URLs to load.
- Update agent-browser Step 2b explore loop to follow the same
flow-driven approach instead of generic element prioritization.
- Add Step 2c thin-results safety check: if < 5 endpoints found,
diagnose (did I interact?), re-sniff with interaction, compare
against community wrappers as a floor.
- Add User Goal Flow section to sniff discovery report template,
tracking which steps completed and what each triggered.
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---
skills/printing-press/SKILL.md | 19 ++++-
skills/printing-press/references/sniff-capture.md | 90 +++++++++++++++++------
2 files changed, 85 insertions(+), 24 deletions(-)
diff --git a/skills/printing-press/SKILL.md b/skills/printing-press/SKILL.md
index 3a755c82..9562357c 100644
--- a/skills/printing-press/SKILL.md
+++ b/skills/printing-press/SKILL.md
@@ -601,7 +601,24 @@ When `AUTH_SESSION_AVAILABLE=false`, show only options 2-4 (the existing 3-optio
### If user approves sniff
-Read and follow [references/sniff-capture.md](references/sniff-capture.md) for the complete
+#### Step 0: Identify the User Goal
+
+Before building the capture plan, answer one question: **What does the end user of this CLI actually want to do?**
+
+Read the research brief's Top Workflows. The #1 workflow IS the primary sniff goal. State it in one sentence:
+- Domino's: "Order a pizza for delivery"
+- Linear: "Create an issue and assign it to a sprint"
+- Stripe: "Create a payment intent and confirm it"
+- ESPN: "Check today's scores and standings"
+- Notion: "Create a page and organize it in a database"
+
+If the API is read-only (news, weather, data feeds), the primary goal is "fetch and filter data" and the flow is search/filter/paginate rather than a multi-step transaction.
+
+The sniff will walk through this goal as an interactive user flow. Secondary workflows become secondary sniff passes if time permits.
+
+State the goal explicitly before proceeding: "Primary sniff goal: [goal]. I will walk through this as a user flow."
+
+Then read and follow [references/sniff-capture.md](references/sniff-capture.md) for the complete
sniff implementation: tool detection, installation, session transfer, browser-use/agent-browser
capture, HAR analysis, and discovery report writing.
diff --git a/skills/printing-press/references/sniff-capture.md b/skills/printing-press/references/sniff-capture.md
index 5f31f782..66b19850 100644
--- a/skills/printing-press/references/sniff-capture.md
+++ b/skills/printing-press/references/sniff-capture.md
@@ -277,15 +277,36 @@ Claude drives browser-use directly via CLI commands — no LLM key needed, no Py
**IMPORTANT: Run the page collection loop in foreground, not background.** The loop takes ~60-90 seconds for 10-15 pages. Background execution has unreliable output capture for shell functions that call browser-use. Always run this inline.
-**Step 2a.1: Build the page list**
+**Step 2a.1: Build the user flow plan**
-From Phase 1 research, identify 10-15 target pages that exercise different parts of the API. Include:
-- Homepage
-- Scoreboard/listing pages for each major resource (scores, standings, teams)
-- Detail pages (individual team, player, event)
-- Search results
-- Stats/leaders pages
-- News pages
+From the primary sniff goal (Step 0 in the SKILL.md), derive the interactive steps a real user would take to accomplish that goal. This is NOT a list of pages to load -- it is a sequence of actions.
+
+Example for "Order a pizza for delivery" (Domino's):
+1. Click "Delivery" on homepage
+2. Enter a delivery address, click "Continue"
+3. Confirm a store from the results
+4. Browse the menu (click a category like "Specialty Pizzas")
+5. Add an item to cart (click "Add to Order")
+6. View cart (click cart icon)
+7. Proceed toward checkout (STOP before entering payment)
+
+Example for "Create an issue" (Linear):
+1. Click "New Issue" from the sidebar
+2. Fill in title and description
+3. Assign to a team/project
+4. Set priority and labels
+5. Submit (or preview if dry-run)
+
+Example for "Check today's scores" (ESPN):
+1. Load the homepage (scores are front-page content for read-heavy sites)
+2. Click a sport (NFL, NBA, etc.)
+3. Click a specific game for the box score / play-by-play
+4. Click standings
+5. Click a team for the team page
+
+Each step triggers API calls that page loads alone would miss. After the primary flow, add 1-2 secondary flows from the research brief's other top workflows (e.g., "Check rewards," "Track an order").
+
+**SPA interaction rule:** On each page/state, take a snapshot first. Look for interactive elements (buttons, forms, dropdowns, tabs). Click through them. SPAs fire API calls on interaction, not on page load. If you load a page and see no XHR activity, that means you need to interact with the page, not that there is nothing to find.
**Step 2a.2: Collect API URLs**
@@ -356,15 +377,17 @@ If browser-use is not available, use agent-browser with Claude driving the explo
agent-browser network har start
```
-2. **Explore the site** using the snapshot-reason-act loop:
- - `agent-browser snapshot -i` to see the page
- - Identify interactive elements: search boxes, filters, buttons, dropdowns, pagination
- - Prioritize: search forms > filters > action buttons > dropdowns > pagination
+2. **Walk the user flow** using the snapshot-reason-act loop:
+ - Use the user flow plan from Step 2a.1 (same flow applies to both backends)
+ - For each step in the flow:
+ - `agent-browser snapshot -i` to see the current page state
+ - Find the interactive element for this step (button, form, link, dropdown)
+ - Click/fill/submit it
+ - `agent-browser wait --network-idle` after each interaction
+ - Apply sniff pacing between interactions (1s initial, adaptive per Sniff Pacing rules)
+ - After completing the primary flow, run 1-2 secondary flows
- Skip: navigation links, footer links, social media buttons, cookie/consent banners
- - Fill forms with realistic sample data based on the domain
- - `agent-browser wait --network-idle` after each interaction
- - Repeat for up to 5 rounds or until no new API endpoints appear for 2 consecutive rounds
- - Apply sniff pacing between interactions (1s initial, adaptive per Sniff Pacing rules)
+ - Fill forms with realistic sample data based on the domain (real-looking addresses, names, etc.)
3. **Capture response bodies** (agent-browser HAR omits them):
```bash
@@ -383,6 +406,20 @@ If browser-use is not available, use agent-browser with Claude driving the explo
agent-browser network har stop "$DISCOVERY_DIR/sniff-capture.har"
```
+#### Step 2c: Thin-results safety check
+
+After completing the primary user flow capture (browser-use or agent-browser), count unique API endpoints discovered. If fewer than 5 unique endpoints:
+
+1. **Diagnose, don't accept.** Thin results from an SPA almost always mean the agent loaded pages without interacting. Ask yourself: did I click buttons? Did I fill forms? Did I submit anything? Did I scroll to trigger lazy loads? If the answer is "I mostly just navigated to URLs," that is the problem.
+
+2. **Re-sniff with interaction.** Go back to the page where results were thinnest. Take a snapshot. Find interactive elements. Click the most prominent one. Wait for network activity. Repeat for at least 3 interactions before accepting thin results.
+
+3. **Compare against known endpoints.** If Phase 1 research found community wrappers documenting N endpoints but the sniff found fewer than N/2, the sniff missed something. Community wrappers are a floor, not a ceiling -- they represent what someone else already reverse-engineered, often years ago. The real API surface is almost certainly larger.
+
+4. **Report the gap honestly.** If re-sniffing with interaction still produces thin results, report: "Sniff captured X endpoints but community wrappers document Y. The site may use WebSocket, protobuf, server-side rendering, or other techniques that resist browser capture." Do NOT conclude "the API has few endpoints" when the real answer may be "I didn't interact enough to trigger them."
+
+If the thin-results check triggers a re-sniff that discovers additional endpoints, merge the new captures with the originals before proceeding to Step 3.
+
#### Step 3: Analyze capture
Run websniff on the captured traffic:
@@ -409,19 +446,26 @@ Write a structured sniff provenance report to `$DISCOVERY_DIR/sniff-report.md`.
The report must contain these sections:
-1. **Pages Visited** — List every URL browsed during the sniff, in order. Include the page purpose (e.g., "Homepage", "Search results for 'stripe'", "Team detail page").
+1. **User Goal Flow** — The primary sniff goal and each step attempted.
+ - Goal: [e.g., "Order a pizza for delivery"]
+ - Steps completed: [numbered list of steps taken, with which API operations each step triggered]
+ - Steps skipped: [any steps that couldn't be completed, with reason]
+ - Secondary flows attempted: [any additional workflows beyond the primary goal]
+ - Coverage: [X of Y planned steps completed]
+
+2. **Pages & Interactions** — List every URL browsed and interaction performed during the sniff, in order. Include the page purpose and what was clicked/filled/submitted (e.g., "Homepage -- clicked 'Delivery' button", "Address modal -- entered '350 5th Ave', clicked 'Continue'").
-2. **Sniff Configuration** — Backend used (browser-use, agent-browser, or manual HAR), pacing settings (initial delay, final effective rate), and proxy pattern detection result (proxy-envelope detected / not detected, with the proxy URL if applicable).
+3. **Sniff Configuration** — Backend used (browser-use, agent-browser, or manual HAR), pacing settings (initial delay, final effective rate), and proxy pattern detection result (proxy-envelope detected / not detected, with the proxy URL if applicable).
-3. **Endpoints Discovered** — A markdown table with columns: Method, Path, Status Code, Content-Type. One row per unique endpoint observed.
+4. **Endpoints Discovered** — A markdown table with columns: Method, Path, Status Code, Content-Type. One row per unique endpoint observed.
-4. **Coverage Analysis** — What resource types were exercised (e.g., "collections, workspaces, teams, categories") and what was likely missed. Compare against the Phase 1 research brief to identify gaps (e.g., "Brief mentions 'flows' but no flow endpoints were discovered during sniff").
+5. **Coverage Analysis** — What resource types were exercised (e.g., "collections, workspaces, teams, categories") and what was likely missed. Compare against the Phase 1 research brief to identify gaps (e.g., "Brief mentions 'flows' but no flow endpoints were discovered during sniff").
-5. **Response Samples** — For each unique response shape (keyed by status code + content-type category), include a truncated sample:
+6. **Response Samples** — For each unique response shape (keyed by status code + content-type category), include a truncated sample:
- JSON/text responses: first 2KB or 100 lines, whichever is smaller
- Binary responses (images, protobuf, etc.): skip content, include a metadata note: `Binary response: <content-type>, <size> bytes`
- Aim for one sample per unique shape, not one per endpoint
-6. **Rate Limiting Events** — Any 429 responses encountered, delays applied, and effective sniff rate achieved (e.g., "Sniffed 7 endpoints at ~1.5 req/s effective rate, one 429 at request #4").
+7. **Rate Limiting Events** — Any 429 responses encountered, delays applied, and effective sniff rate achieved (e.g., "Sniffed 7 endpoints at ~1.5 req/s effective rate, one 429 at request #4").
-7. **Authentication Context** — Whether the sniff used an authenticated session. If yes: transfer method used (auto-connect / profile / headed login / HAR), which endpoints were only reachable with auth (e.g., "order history, saved addresses, rewards required login"), and confirmation that session state was excluded from manuscript archiving. If no: "No authenticated session used."
+8. **Authentication Context** — Whether the sniff used an authenticated session. If yes: transfer method used (auto-connect / profile / headed login / HAR), which endpoints were only reachable with auth (e.g., "order history, saved addresses, rewards required login"), and confirmation that session state was excluded from manuscript archiving. If no: "No authenticated session used."
← 998908b5 refactor(skills): extract conditional sections to reference
·
back to Cli Printing Press
·
fix(cli): README scorer alias, template placeholder, verify 9ab8aa12 →