Marvin
Projects

Project / 2026

FunnelLoops

An AI-native funnel platform where a sandboxed agent and a visual builder edit the same validated funnel spec, with versioned publishing, CRM sync, and real-time analytics.

System / funnelloops2026
SURFACEScanvas · agent · visitorsAGENT RUNTIMEmicro-VM · jq · skillsVALIDATORvalidate · reject · never repairDOCUMENTone funnel spec · convex truthAI funnel platform
01Skills-driven agent runtime02Validate-and-reject spec boundary03Real-time decision analytics

The thesis

I stopped asking the model for perfect output and gave it a file to edit instead.

FunnelLoops treats a marketing funnel as a typed document with strict semantics: steps, fields, routing, and actions in one flat spec. The agent works the way an engineer does. It reads the document, loads the skill the task needs, makes a surgical edit, and submits the result to a validator that rejects anything structurally broken. Everything else in the product, the canvas builder, versioned publishing, CRM integrations, and analytics, is built around that single document and the events it produces.

Stage
Pre-launch
Scope
Product architecture · Full-stack engineering · Agent runtime design

System map

One validated document, three kinds of editors.

01 / Surfaces

Three editors, one spec

A visual canvas builder for humans, a sandboxed agent for language, and a published runtime for visitors all read and write the same funnel document.

02 / Agent runtime

A VM, a file, and skills

A coding agent runs inside a micro-VM where the funnel is a JSON file it edits with jq, guided by markdown skill files loaded on demand.

03 / Data authority

Convex owns the truth

Funnels, immutable versions, teams, submissions, and integration state live in one reactive backend; interfaces subscribe instead of parsing streams.

04 / Pipelines

Contracts at the edges

Canonical CRM entities sync through a change-data-capture outbox, and a Tinybird event pipeline turns funnel traffic into queryable decisions.

01 / Product model

A funnel is a typed document, not a pile of pages

Decision modelOne document, four routing shapes
EDITOR STATEnever enters the specLANDINGrouting inlineFORMtyped fieldsBRANCHconditions + fallbackENDterminal stepACTIONStop-level recordstrigger: after:<stepId>FUNNEL.JSONone flat typed spec18 field types · 4 routing shapesaction · eventtrigger · function

Routing is one discriminated union inline on each step: linear · conditional · split-test · multi-path.

Every surface of the product agrees on what the funnel does, so revisions stop breaking campaigns.

One flat spec with inline routing keeps the builder, runtime, and analytics from drifting apart.

A discriminated routing union and top-level trigger-referenced actions make the document patchable with one query language.

The visible promise is simple: describe a campaign and get a working funnel with landing pages, forms, routing, and follow-up actions. The difficult part is that a funnel is control flow. Conditional branches route a visitor by their answers, split tests weight traffic across variants, and actions fire when specific steps complete. Represent that as a loose collection of page configurations and every feature ends up fighting the data model.

So the spec is a single flat JSON document with strict semantics. Each step carries its routing inline as a discriminated union: linear with one next step, conditional with grouped conditions and a required fallback, split-test with weighted variants, and multi-path choices. Actions are top-level records that reference the step that triggers them. Eighteen typed field kinds cover capture. Editor concerns like generation status stay out of the spec entirely; the document describes the funnel, not the editing session that produced it.

Flatness was a deliberate decision about who edits this document. A deeply nested schema is pleasant to render and miserable to modify surgically. A flat document with inline routing can be navigated and patched with a single query language, which is exactly how the agent works on it. And because the published runtime executes the same routing the builder draws and the analytics pipeline records, the three surfaces cannot drift into different opinions about what the funnel does.

  • Inline discriminated-union routing
  • Actions reference their trigger steps
  • Eighteen typed field kinds
  • No editor state inside the spec
Foundational decisionDesign the artifact for the hands that will edit it, human or model.

02 / Agent runtime

Give the agent a filesystem, not a flowchart

Decision modelA VM, a file, and seven skills
PI AGENTshort prompt · reads the indexSKILLS ×7SKILLS.md · one per taskJQsurgical patchesFUNNEL.JSONthe document as a fileGATEWAYmodel routing · host callsCREDENTIALSdo not exist inside the VMMICRO-VMsandboxed virtual filesystemeffects exit via host commandsaction · eventtrigger · function

Hydration mounts the skills and funnel.json before the agent wakes; credentials and the business network do not exist inside the VM.

The AI improves by writing better playbooks, not by rebuilding the product.

A sandboxed VM bounds what the agent can touch, and gateway routing keeps model choice a config change.

Skills are markdown with jq recipes; the agent loads one per task and edits /funnel.json surgically.

Most AI builders hardcode a chain: prompt in, structured output out, merge and hope. Every product change means re-engineering the chain. FunnelLoops runs a coding agent inside a sandboxed micro-VM instead. The funnel spec is mounted at /funnel.json, and the agent edits it with bash and jq: surgical patches to the fields that changed, never a rewrite of the whole file.

Domain knowledge lives in seven markdown skill files behind an index. The agent reads the index first, then loads only the skill the task needs: planning a funnel from scratch, editing fields, routing, field types, action intents, layout, or publishing. Skills contain literal jq recipes for common operations. Changing how the product builds funnels means editing prose, not redeploying orchestration code.

The system prompt is a few sentences that point at the index and set the working rules. Model choice is a gateway configuration, not an architecture decision, so models can be swapped and benchmarked without touching the runtime. Inside the VM, permission prompts are auto-approved because the sandbox is the boundary that matters: the VM holds no credentials and no network access to the business. Every effect leaves through explicit host commands.

  • Sandboxed micro-VM with a virtual filesystem
  • Seven markdown skills behind an index
  • Surgical jq edits over full rewrites
  • Model routing through one gateway config
Foundational decisionShip knowledge as documents the agent loads, not chains you recompile.

03 / Correctness boundary

Validate and reject beats generate and repair

Decision modelAccept valid funnels, reject the rest
AGENT EDITjq patch + syncVALIDATORzod schema · graph checksCONVEXpersists only valid funnelsviolation → error the agent acts onSILENT REPAIRnever patches model outputaction · eventtrigger · function

Six properties every funnel must hold: a terminal step exists · every target resolves · no cycles · no unreachable steps · split weights sum to 100 · conditionals declare a fallback.

Customers never receive a funnel that routes visitors into a dead end.

Correctness lives in a versioned, testable validator instead of scattered repair code.

Schema plus graph checks reject cycles, orphans, bad weights, and missing fallbacks with errors the agent can act on.

The common failure mode of AI builders is accepting whatever the model returns and patching it afterwards with repair code that grows forever and still misses cases. FunnelLoops inverts that: the platform never repairs agent output. Every sync runs the document through the schema and then through a structural validator that understands what a funnel must be.

Structure means real graph properties. At least one step must terminate the funnel. Every routing target must exist. A depth-first search rejects cycles, and a breadth-first walk from the entry step rejects orphans a visitor could never reach. Split-test weights must sum to one hundred. Conditional routing must declare a fallback. A violation rejects the sync with an error message written for the agent to act on, and the agent retries with a fix.

This moves the correctness contract into the platform, where it is testable and versioned, and it makes the agent allowed to be wrong cheaply. A rejected edit costs one round trip. A silently repaired edit costs trust, because the funnel that ships is no longer the funnel anyone asked for. The same validator guards the human path from the builder, so there is exactly one definition of a well-formed funnel.

Foundational decisionReject broken output with an explanation; never silently repair it.

04 / State and streaming

The database is the product; the stream is presentation

Decision modelTruth and presentation, separated
AGENT VM/funnel.json after each turnSYNCdebounce · hash dedupeauthenticated gatewayCONVEXthe database is the productBUILDERrenders from subscriptionsAGENT EVENTStext · tool callsCHAT UIpresentation onlynever parsed for stateaction · eventtrigger · function

Four host commands are the only exits from the VM: sync · publish · analytics · load.

What users see on the canvas is always what was actually saved.

The database is the single source of truth; the chat stream carries no state.

Debounced, hash-deduped auto-sync persists through an authenticated gateway; the builder re-renders from Convex subscriptions.

Streaming generated JSON to the browser and reconstructing state there is a race condition dressed up as UX: partial data on disconnect, duplicate application on retry, and a client that has to understand the model's output format. FunnelLoops persists artifact-first. The agent's toolkit exposes four commands, sync, publish, analytics, and load, and each executes on the host through an authenticated gateway into Convex. The VM never sees a credential.

After every turn, an auto-sync pass reads the file, debounces rapid edits, deduplicates with a content hash so identical states never write twice, validates, and only then persists. The builder does not parse the chat stream for state. It subscribes to Convex and re-renders when the document actually changed, which is the same path it uses when a human edits on the canvas.

The chat still streams, because watching the work happen is part of the product. An adapter translates agent events into the AI SDK wire protocol so a standard chat client renders tokens and tool calls, plus one custom event that tells the canvas the funnel changed. The presentation channel and the truth channel stay separate, and only one of them can create facts.

  • Four host commands behind an authenticated gateway
  • Debounced, hash-deduped auto-sync
  • Convex subscriptions drive the canvas
  • Wire-protocol adapter for the chat UI only
Foundational decisionLet the stream animate the work; let the database own the outcome.

05 / Integrations

One canonical contact, many CRMs

Decision modelCanonical entities, contract edges
CANONICAL RECORDcontact · lead · company · dealper-provider external refsENTITY CHANGEcreate · update · deleteCDC OUTBOXhash dedupe · bounded retriesADAPTERgenerated from configHUBSPOT CRMproduction-ready · upsertWEBHOOKraw payload storedSIGNATURE + DEDUPEfail-closed · duplicates skippedaction · eventtrigger · function

Contract tests hold every connector to one suite; the next connector is declarative config plus thin overrides.

Leads reach the CRM the team already uses, reliably, without duplicate records.

Connectors are declarative configs held to one contract-test suite, so the catalog can grow safely.

Canonical entities with external refs sync through a CDC outbox with hash dedupe; webhooks are signature-verified and idempotent.

A funnel that captures leads is only useful if the leads land where the team works, which means CRM integrations, which is where most platforms accumulate bespoke connector code. FunnelLoops has an integration SDK where a connector is a declarative configuration: auth, field maps, object capabilities, and webhook handling. Adapters, manifests, and tools are generated from that config, and contract tests hold every connector to the same behavioral suite.

Sync is built on canonical entities. Contacts, leads, companies, and deals have typed core fields plus provider references, so one record can be linked to several external systems with per-provider sync state. Outbound changes flow through a change-data-capture outbox with payload-hash deduplication and bounded retries. Inbound webhooks are signature-verified, made idempotent by event ID, and resolved back to the canonical record.

HubSpot is the production-ready reference: OAuth, upsert-by-email semantics, deletes that treat an already-missing record as success, and cursor pagination. The point of the SDK is that the second connector is configuration plus a thin override file, not a fork of the first one.

Foundational decisionMeet external systems with contracts and idempotency, not bespoke glue.

06 / Analytics

Every visitor decision becomes a queryable event

Decision modelFrom visitor decision to query
PUBLISHED FUNNELvisitor decisionsDECISION EVENTS18-type taxonomyBATCHING SDKdevice · geo · attributionTINYBIRDraw + materialized viewsDASHBOARDSfunnel · breakdown · sankeyAGENTread-only analytics commandaction · eventtrigger · function

Routing decisions are recorded events: the runtime logs which branch fired and why. Ground truth, not inference.

Teams see where visitors leak out of the funnel and which variant actually wins.

A real-time pipeline serves both dashboards and the agent from the same measured events.

Eighteen event types, denormalized rows, and materialized views make routing decisions queryable ground truth.

A funnel is an experiment, and an experiment without measurement is decoration. Published funnels emit an eighteen-type event taxonomy through a batching SDK: views, starts, submits, completes, and abandons, field-level focus and error events, routing decisions, and automation and integration outcomes. Each row is denormalized with device, geography, and attribution so questions do not require joins at query time.

Tinybird ingests those events into raw datasources and materialized views, and a set of pipes serves the product surfaces: the conversion funnel, per-step breakdowns, time series, and a journey view that renders the paths visitors actually took as a Sankey diagram.

Recording routing decisions as events is the detail that matters. Because the runtime logs which branch fired and why, the journey view is ground truth rather than inference, and a split test can be read directly from the data. The same pipeline feeds back into the agent through a read-only analytics command: ask where the funnel leaks, and it answers from measurements instead of guessing.

Foundational decisionInstrument decisions, not just pageviews; that is what makes analytics actionable.

07 / Verification

Benchmarks that speak the user's language

Decision modelMeasured the way users ask
PROMPT"make email optional"AGENTfinds the skill itselfFUNNEL.JSONjq edit · no mechanism hintsVERIFIERprogrammatic · reads the file back11/11 PASSeleven operations · four categories64 unit tests · skills under testaction · eventtrigger · function

Verifiers are programmatic, not model-graded; models swap via the gateway for comparison runs.

The AI is measured on the sentences customers actually type, not on developer demos.

Programmatic verifiers give a pass rate that can gate releases and compare models.

Natural-language prompts exercise skill discovery end to end, and the skill markdown itself is under test.

The failure mode of agent demos is testing the happy path with developer-shaped prompts. The FunnelLoops benchmark harness drives the agent with natural language only, the way a customer would type it: make the email field in the contact form optional. The agent has to read the skill index, load the right skill, derive the jq edit, and execute it. Nothing in the harness hints at the mechanism.

Eleven operations across four categories, field editing, routing changes, step operations, and full funnel creation, each end with a programmatic verifier that reads the resulting document back and asserts the change actually happened. The current suite passes eleven of eleven. Underneath, sixty-four unit tests cover the schema, the structural validator, migration, the stream adapter, and auto-sync.

The tests I care most about are the ones over the skill files themselves. When product knowledge is prose, prose becomes an interface, and an interface needs tests: frontmatter shape, index consistency, and referenced commands. A skill edit that would confuse the agent fails in CI before it ever reaches one.

Foundational decisionIf users will ask in natural language, benchmark in natural language.

What this demonstrates

The real product is a document three kinds of editors can trust

FunnelLoops looks like an AI funnel builder because that is the job users hire it for. Underneath, it is a document system with strict semantics and several editors: a canvas for humans, a sandboxed agent for language, and a runtime for visitors, all converging on one validated spec in one reactive database, with contracts at every edge that touches the outside world.

The sequence was deliberate. The spec, the validator, and the persistence gateway came first; only then did the agent get its filesystem, its jq, and its skills. That ordering is what makes the system safe to grow. New capability is a new markdown skill, and it arrives without weakening anything, because validation and the gateway do not move.

This is the kind of work I want to keep doing: finding the representation that makes a hard product tractable, then building the boundaries that let humans, runtimes, and models edit it without stepping on each other.

Discuss an AI-native product