Skip to content
Agentmart
Submit a tool

spike

Gather EVIDENCE from the live Flow surface — DOM, network, HAR — before claiming a feature is broken, missing, or impossible.

Skill by ffroliva

IndexedMIT★ 332
Write a review

Install

Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.

npx agentmarket install skill/ffroliva-spike

Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

Gather EVIDENCE from the live Flow surface — DOM, network, HAR — before claiming a feature is broken, missing, or impossible. The first layer of investigation for any "X does not work on Y" question, and the only thing that can settle one.

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

Permissions

What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.

Tools
No tools found
Secrets
None asked for
Install scripts
None found

Network hosts it talks to 1

  • codeload.github.com · found in install command

Files it touches

  • ~/.claude/skills/spike

What's inside

Skill name
spike
Description
Gather EVIDENCE from the live Flow surface — DOM, network, HAR — before claiming a feature is broken, missing, or impossible. The first layer of investigation for any "X does not work on Y" question, and the only thing that can settle one.
License
MIT
Instructions
174 lines in SKILL.md · view on GitHub

Agent Skills spec issues

  • files: referenced but missing: scripts/dev/har-spike/extract_har_summary.py

Files (1)

  • SKILL.md

SKILL.md (first lines)

# `spike` — measure the blackbox before you describe it

gflow-cli drives a product it does not own. Every statement about what Flow does is
either **measured** or **guessed**, and a guess written into code or docs becomes a
fact nobody re-checks.

**Load this skill before you write, say, or encode any of these:**

- "X is not supported / not available / not rendered on this host"
- "this surface is labs-only" · "the migrated host cannot do X"
- "the selector is gone" · "Flow removed it"
- a `raise_if_migrated`-style guard, a capability table, or a KNOWN_ISSUES entry
  asserting an absence
- an issue reply telling a user a feature cannot work

## The rule this exists to enforce

```
A SELECTOR THAT DOES NOT MATCH IS EVIDENCE ABOUT THE SELECTOR.
IT IS NEVER EVIDENCE ABOUT THE FEATURE.
```

A wait that times out tells you your anchor missed. It tells you nothing about whether
the thing exists. To claim absence you need a **positive observation of absence** — a
DOM inventory that lists what IS there, a network log that shows what was and was not
called. "It timed out" is not that.

> **Written from a two-line failure that cost a day.** On 2026-09-06 `gflow character
> create` was believed impossible on `flow.google.com`. The entire chain came from one
> 20 s readiness timeout: selector missed → gate timed out → "no prompt textbox" →
> "labs-only surface" → "renders no prompt textbox for it, **ever**", shipped in a code
> comment, the CHANGELOG, a release ledger and a test class NAME. #701 then added a
> guard that aborted *before* probing the DOM — which made the claim **unfalsifiable**,
> because no run could ever look. Reality: the editor was fully present, on the same
> backend; labs renders React + Slate, the migrated host renders Angular + ProseMirror.
> Seven selectors changed and it worked. Thirty minutes of DOM reading would have
> prevented all of it.

**Never put a guard in front of a probe.** A fail-fast that runs before the evidence is
collected deletes the evidence that would correct it. If you must fail fast, fail
*after* looking, and say what you looked at.

## The ladder — cheapest rung that answers the question

1. **Read an existing capture.** `scripts/dev/_spike_out/` and
   `docs/superpowers/spikes/` may already hold the answer. Free.
2. **In-process probe** — `scripts/dev/spike_*.py`. Playwright driving gflow's own
   transport, so you see exactly what gflow sees. Use when you can already reach the
   surface, or want to observe the production path's own traffic. `$0` unless you
   submit.
3. **HAR + DOM harness** — [`scripts/dev/har-spike/`](../../scripts/dev/har-spike/README.md).
   CDP-attached **real Chrome**; a **human** drives the failing action by hand and you
   get the complete HAR. Use when the driver cannot get far enough to observe anything,
   or when an in-process capture is ambiguous. This is the tiebreaker.
4. **Only then** form a conclusion.

Start at 1. Escalate only when the rung below cannot answer it.

## What a spike must capture

Write a new `scripts/dev/spike_<question>.py` when none fits. It should record:

- **Structure, not labels.** Ligature text, ARIA roles, custom-element tag names,
  `href`s. Never anchor on display text — see the locale-invariance rule in AGENTS.md.
  Custom elements (`<flow-slot-chip-button>`) are the best anchors available: they are
  component boundaries, not layout accidents.
- **The carrier.** labs renders ligatures in `<i class="google-symbols">`, the migrated
  host in `<mat-icon>`. Same ligature, different tag — a mismatch here looks exactly
  like a missing feature.
- **Both sides of a transition.** Snapshot before AND after the click. A signal present
  in both proves nothing; one that appears only after is a real settle signal.
- **The network.** Which hosts, which routes, which `batchexecute` rpcids. This is how
  "the backend is shared, only the frontend was rebuilt" gets established instead of
  assumed.
- **Occlusion.** An element can exist, be visible, and still not be clickable. Record
  what `elementFromPoint` returns over it.
- **A control.** If you are testing a fix, run the same probe with the fix stashed. A
  result with no control is a coincidence with formatting.
- **A stand-in that holds what the real job holds.** A fake job proves only what the
  fake holds. The #1097 shutdown spike detached a sleeping coroutine and measured stdio
  shutdown as safe. A real job holds a Playwright browser, and at stdin EOF
  `asyncio.run` cancels Playwright's connection with everything else: 40 s or more of
  close timeouts, found only by the live e2e
  ([spike](../../docs/superpowers/spikes/2026-10-25-mcp-detached-task-shutdown.md)).
  If the question is about teardown, locks or connections, the stand-in must open the
  real resource.

## Cost discipline

Navigation, DOM reads, `flow.createEntity`, `batchDeleteAssets` and a reCAPTCHA mint are
all **free**. Image generation costs daily **quota**, zero credits. Video costs
**credits**. Say which in the spike's docstring, and delete anything the spike created.

## Profile etiquette — never kill a browser on a profile you do not hold

**`ProfileLockedError` is the lease working, not a stale lock.** It means another
process owns that profile right now, and the error carries the holder's evidence. Read
it. Then wait, or spike on a different profile. Both are cheap; neither can corrupt
anything.

**Never kill Chrome processes to clear the way.** Two Chrome instances on one
`user_data_dir` is the corruption the lease exists to prevent, and a process list cannot
tell you which browser belongs to whom — so "these look like my orphans" is a guess made
against a fact the lease already gave you.

> **Written from the incident it prevents.** On 2026-09-07 two sessions worked this repo
> at once. One ran the e2e suite on `denon82` and held its lease. The other hit
> `ProfileLockedError`, read the resulting Chrome processes as orphans of its own spike,
> and killed eighteen of them in two batches; nine belonged to the running suite. It then
> diagnosed the cause as "spike scripts do not take the lease" — but its own spike went
> through `FlowApiClient`, which acquires at `api/client.py` before Chrome starts, so it
> *had* held the lease. **The tool was correct and was overruled by a process list.**
> A real defect did surface underneath — three scripts launched Chrome outside any lease,
> fixed with a guard test in #717 — but it was not what caused the incident, and fixing it
> would not have prevented it. This rule would have.

If you write a spike that launches Chrome itself rather than through `FlowApiClient`,
wrap it: `async with ProfileLease(profile_dir), async_playwright() as pw:`. Chrome must
never start on a profile this process does not own.

From ffroliva/gflow-cli, MIT.

More from ffroliva/gflow-cli

Signals

Stars
332
Forks
95
Open issues
30
Contributors
22
Last commit (this folder)
Oct 15, 2026 (13 days ago)
Last release
Oct 24, 2026 (4 days ago)

From GitHub (fetched Oct 24, 2026).

Details

License
MIT
First seen
Oct 24, 2026
Last checked
Oct 24, 2026

More from ffroliva

Cite this page

spike skill. Agent Mart. https://agentmart.dev/skills/ffroliva-spike (last updated 2026-10-24)