- Role
- Founder
- Organisation
- Maple Spark Labs
- Timeframe
- 2026–present
- Status
- Live
- Kind
- Product
- Technology and standards
- Generative AI
- Large language models
- Anthropic Claude
- Next.js
- TypeScript
- Supabase
- PostgreSQL
- Prisma
- AI-assisted development
- Focus
- AI product design
- Safety guardrails
- Human review
- Traceability
- Cost controls
- Learning design

Problem
An LLM can produce more information than anyone can use. The harder product problem is helping someone accomplish something specific, such as preparing to negotiate a raise, without burying them in content or losing track of what they have learned.
Constraints
The product had to turn an open-ended goal into a path that fit a real day. Because every lesson is generated, it also needed controls before and after generation, a review loop for questionable output, and a cost model the product could sustain.
Inside the AI system
Because every lesson is generated, the product puts five separate checks around it.
- Classify the request
- Constrain generation
- Validate the output
- Set a public policy
- Log and review
Under the hood: how the safety layers are built
Excerpts from the Droplet Learn codebase, lightly trimmed. The repository is private.
| Layer | What runs | When | Approx. cost |
|---|---|---|---|
| Keyword pre-filter | Word-boundary blocklist | Before any model call | $0 |
| Input classifier | Lightweight model: SAFE, FLAG or BLOCK | Before generation | ~$0.001 per check |
| Prompt guardrails | Safety rules in every generation prompt; the model can decline | During generation | $0 |
| Output scanner | Lightweight model: SAFE or UNSAFE per lesson, unsafe lessons quarantined | Before storage | ~$0.002 per path |
| Logging and review | Safety events, admin review queue, user reports | Continuous | Storage |
Together the checks add roughly half a percent to the cost of generating a path. A blocked request skips the generation call entirely.
Some topics are legitimate or harmful depending on intent. Instead of blocking them, the classifier can return FLAG, which asks the learner one clarifying question.
if (classification.classification === "FLAG") {
if (clarification) {
// Dual-use topics (e.g. "penetration testing") will always FLAG on the
// topic name alone; the clarification shows good-faith intent.
// Only an explicit BLOCK stops generation after clarification.
logger.info("FLAG accepted after clarification", { traceId, reason });
} else {
return NextResponse.json(
{ flag: true, clarifying_question: classification.clarifying_question },
{ status: 200 }
);
}
}Quarantining a lesson is half the job. The other half is making sure the learner isn't left facing a locked path.
// The serving query filters out quarantined drops, so `current` must land
// on the first non-quarantined drop or the user sees an entirely locked
// path. If every Phase 1 drop is quarantined, there is no safe target:
// abort before persisting so the user can retry.
const firstCurrentIndex = dropScanResults.findIndex((r) => r.status !== "UNSAFE");
if (firstCurrentIndex === -1) {
await prisma.path.delete({ where: { id: path.id } });
return NextResponse.json(
{ error: SAFETY_MESSAGES.PATH_BLOCKED, reason: "safety_block" },
{ status: 422 }
);
}A zero-latency keyword pass catches the obvious cases before any model call. Word boundaries keep it from blocking innocent words.
// Word boundaries prevent false positives
// (e.g. "therapist" won't match "the rapist").
const BLOCKED_PATTERNS = Object.values(blocklist.categories)
.flat()
.map((term) => ({
term,
regex: new RegExp(`\\b${escapeRegex(term)}\\b`, "i"),
}));Both model-based checks wrap the untrusted text in delimiters and treat it strictly as data. The input classifier goes one step further: an attempt to override its rules is classified as BLOCK. The exact prompts and the blocklist aren't published, because they would make the checks easier to get around.
Approach
I first framed Droplet as a tool that broke a subject into bite-sized lessons. Building and testing it changed the problem. People were not trying to study a subject in the abstract. They were trying to accomplish a goal. I reoriented the product around that job, then rebuilt the curriculum, data model, and user flow around a four-phase path: Explore, Practice, Connect, and Create.
Each Drop gives the learner one focused idea, why it matters, an optional exercise, and a chance to record takeaways and reflection. The product keeps the goal visible while the path and progress make the learning manageable.
I later stopped feature work to add a five-layer safety system: a pre-classifier before generation, generation guardrails, post-generation validation, a public content policy, and safety-event logging connected to an administrative review loop. The point was not to claim perfect safety. It was to put several different checks around a system that can fail in different ways.

Current state
Droplet Learn is live. A learner can enter a goal, receive a four-phase path, complete short Drops, record takeaways, and reflect on progress. Safety effectiveness and learning outcomes are still being evaluated.

Evidence
What I learned
AI makes content abundant. Product judgment decides what should reach a user, how it supports the job they came to do, and what the system must refuse.