exploring-an-idea · git:20260804.5f4ec88 · 2026-08-04 · sha256 ce32d074e6d1a24b
exploring-an-idea git:20260804.5f4ec88A
Immutable. This exact content is served forever at /api/v1/blob/ce32d074e6d1a24b.
---
name: exploring-an-idea
description: >
Use at the very start of a new product idea — before a PRD, PCD, repo, or
name exists, and any time someone says "I have an idea for", "should we
build", "let's scope out", or asks for a PRD/PCD from scratch. Separates
what is known from what is assumed, and forces the load-bearing assumptions
to be measured before they become architecture. Works in Claude Desktop
(research only) and Claude Code (research plus probes).
---
# Exploring an Idea
A design document reads as settled fact whether or not anyone checked. That is
the whole failure mode: an unverified sentence in a PCD becomes an interface,
then a dependency, then a week of work, and nothing in the process ever asks
"how do we know that?"
This skill exists to put that question before the document instead of after it.
## The case this was written from
Needledrop (NFC tap-to-stream for record collectors) had an unusually good PRD
and PCD — real non-goals, numeric kill criteria, a named Phase 0 with exit
criteria. It still shipped four false premises into its architecture, and the
Phase 0 spike had to correct all four:
| The PCD asserted | Measurement found |
|---|---|
| "Spotify is effectively closed to us" (5-user dev cap, 250k-MAU quota wall) | Those gate **user-data** endpoints. Client credentials reach `/v1/search` and `/v1/albums` today. Spotify became the **best-covered** platform. |
| Barcode/UPC is the strongest identifier — "UPC preferred" | Discogs carries the **physical pressing's** barcode; streaming keys the **digital release** UPC. Different numbers. Spotify UPC lookups hit **0/46**, Apple **5/42**. |
| Odesli is the hedge for the Spotify gap | Odesli returned **0 Spotify links in 19 Apple-seeded** album responses. It could not be the hedge. |
| Apple Music needs a MusicKit credential | Keyless for UPC and tracklist lookup. |
None of these were sloppy. Each is a plausible reading of vendor documentation.
They were wrong anyway, and the cost was concrete:
- An entire **provider-abstraction layer** — `LinkResolver`, capabilities
`OptionSet`, chain-of-resolvers — designed around swapping away from a
dependency that turned out to be unnecessary.
- A **"blocking, business" open question** (email Odesli for commercial terms,
owner assigned, week 1) generated by a gap that did not exist.
- A long **"Option D — build our own resolver"** analysis premised on Spotify
being unreachable.
- The real finding — **tracklist matching**, which took the album shelf from
62.1% to 79.3% and Spotify from 0% to 60% — appears nowhere in the PCD,
because the document had already decided the identifier ladder.
And the name: the PRD listed "run the trademark pass" as an open question,
*after* the docs were titled Needledrop and the repo was created as
`needledrop`. The check ran later, "Needledrop is not viable" came back, and
the project renamed to Sleevetap — while the repo, its remote, and its clone
path still say needledrop today.
The lesson is not "write better documents." It is that **a design doc is the
wrong instrument for discovering whether something is true.**
## The method
### 1. Split the idea into claims, and label every one
Before writing anything designed to be read as a plan, list the claims the idea
rests on. Give each a provenance label — no exceptions, including the obvious
ones:
- **`measured`** — you ran it and saw the result. Cite the number.
- **`documented`** — a vendor doc says so. **This is not verification.** Every
false premise above was `documented`.
- **`assumed`** — someone's reasonable belief.
Then ask of each: **if this is wrong, does the design change?** A claim that is
`documented` or `assumed` *and* load-bearing is not a claim. It is a task.
For Needledrop that filter alone catches all four: "Spotify is closed" was
documented and load-bearing; "UPC is the strongest identifier" was assumed and
load-bearing.
### 2. Probe the load-bearing ones — cheaply, before the document
A probe is a script that answers one question with a number, not a prototype.
Hours, not days; throwaway, not the first commit of the product.
The Needledrop probes would have been: one `curl` with Spotify client
credentials (kills premise 1 in ten minutes); twenty barcodes from a real
collection run against three catalogues (kills premise 2 in an hour).
In **Claude Code**, write and run them. In **Claude Desktop**, you cannot — so
name each probe, say what number would settle it, and hand them over as the
first work item. A doc that ships with "unprobed" next to a load-bearing claim
is honest; one that reads as settled is not.
**Probe on real data, not a sample you constructed.** Needledrop's collection
turned out to be 28% 7-inch singles, which resolve at 0% because a single has
no album-level streaming entity. A flat rate across that collection answers the
wrong question — and only real data reveals it.
### 3. Write the document around what survived
Now write the PRD/PCD (see core "Docs Taxonomy" for where they live). Rules
that keep it honest:
- Every technical claim carries its label. `measured` claims cite the number.
- **Do not design for a risk you have not confirmed.** The provider abstraction
is the cautionary tale: a real pattern, applied to an imaginary problem, and
the cost was not the code but the architecture built around it.
- Kill criteria are numbers with a decision attached: *"auto-match below 70% on
a real collection → revisit"* is a kill criterion. "Validate feasibility" is
not.
- When a probe contradicts the document, **amend the document in place** and
say what was wrong. Needledrop did this well: the PCD was corrected rather
than left asserting things known to be false. A doc nobody trusts is worse
than no doc.
### 4. Do the irreversible-cheap things first
Some decisions cost nothing now and a rename later. Do them before the doc is
titled:
- **Name**: saturation + trademark pass, and check the term isn't already
loaded in the domain ("needle drop" is a music-licensing term of art).
- **Repo name**: follows the name check, never precedes it.
- **Domain / handles**: check availability while the name is still free.
- **Stack**: name it in the doc, as an archetype
(`lacquer init --list-stacks`), then pass it to `lacquer init --stack <name>`.
Needledrop got the order backwards and still carries the wrong repo name.
The stack one is subtler than it looks. Needledrop's PCD said "iOS app with a
web backend" and was right — but the repo was bootstrapped during the
TypeScript-only Phase 0 spike, so detection recorded `profiles = ["web"]`,
correctly, and nothing ever looked again. Swift landed the next day with no
hooks, no CI, and no CLAUDE region; 191 tests were run by nothing at any gate
for a month. Declaring the whole stack up front is a one-word difference at
`init` and it gates the halves that don't exist yet.
Two claims about the same project were held to completely different standards:
the API premises got a Phase 0 spike, and "an iOS app with a web backend" got
written down and never checked against what the repo was actually enforcing.
A claim about your own build is still a claim.
## What good looks like
You are ready to leave exploration when:
- Every load-bearing claim is `measured`, or explicitly flagged as unprobed
with the probe named.
- The kill criteria are numbers, and you would actually stop.
- One end-to-end path has been proven on real data — not a diagram of one.
- The name has survived a trademark pass.
- The stack is named as an archetype and passed to `lacquer init --stack`, so
every half of it is gated from the first commit.
## Anti-patterns
- **The confident PCD.** Fluent architecture built on `documented` premises.
Fluency is not evidence.
- **Abstraction as insurance.** A pluggable layer against a dependency risk
nobody measured. Measure the risk; then decide if the seam is worth it.
- **Phase 0 as ceremony.** A phase whose exit criteria are activities
("outreach", "investigate") rather than numbers. Needledrop's Phase 0 was
real precisely because criterion #2 demanded a measured percentage.
- **Naming last.** The one decision that gets more expensive every commit.
- **Onboarding the spike.** Running `lacquer init` inside a Phase 0 throwaway
records the spike's stack as the project's stack. Detection is right about
what is on disk and wrong about what the project is; `--stack` is how you say
which one you meant.