<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Mochi on Jahvon Dockery</title>
    <link>https://jahvon.dev/tags/mochi/</link>
    <description>Recent content in Mochi on Jahvon Dockery</description>
    <image>
      <title>Jahvon Dockery</title>
      <url>https://jahvon.dev/images/og-default.png</url>
      <link>https://jahvon.dev/images/og-default.png</link>
    </image>
    <generator>Hugo -- 0.153.4</generator>
    <language>en-us</language>
    <lastBuildDate>Wed, 06 May 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://jahvon.dev/tags/mochi/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Discovery</title>
      <link>https://jahvon.dev/architecture/mochi/discovery/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://jahvon.dev/architecture/mochi/discovery/</guid>
      <description>How Mochi finds what you can run, and why discovered executables are generated at read time instead of written into your repo.</description>
      <content:encoded><![CDATA[<h2 id="discovery">Discovery</h2>
<p>Discovery is the &ldquo;find&rdquo; half, and it&rsquo;s the piece I&rsquo;m happiest with architecturally.</p>
<p>A provider is about as small as an interface gets. It answers one question, given a directory:
which files here are importable? It also has to be a cheap check that cannot fail.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span><span class="w"> </span><span class="nx">DiscoveryProvider</span><span class="w"> </span><span class="kd">interface</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nf">Name</span><span class="p">()</span><span class="w"> </span><span class="kt">string</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nf">Imports</span><span class="p">(</span><span class="nx">dir</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="p">[]</span><span class="kt">string</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>It returns file paths, not executables. Turning a file into executables is Flow&rsquo;s job (or, for
the formats Flow doesn&rsquo;t know about, a parser on Mochi&rsquo;s side). Nine providers ship today:
Makefile, <code>package.json</code>, docker-compose, loose shell scripts, Justfile, Taskfile, GitHub
Actions, Dockerfile, and Cargo. The first four are parsed by Flow itself; the rest are Mochi&rsquo;s.
Registration order matters. The first provider to claim a file wins, which is how conflicts get
resolved without a merge policy.</p>
<h3 id="nothing-is-written-to-disk">Nothing is written to disk</h3>
<p>The important design decision: discovered executables are generated <strong>at read time</strong>, not
imported into your repo.</p>
<p>Mochi decorates Flow&rsquo;s executable cache. Discovery persists only a selection of importable files
per workspace, and the decorator regenerates their executables on every read, so they appear
identically in the desktop, in <code>mochi run</code>, and in <code>flow browse</code>, while your repo stays exactly
as it was. Generation happens against a virtual flow file that only ever exists in memory; its
directory is the only thing that matters, because that&rsquo;s what imports resolve relative to.</p>
<p>Failures degrade to nothing rather than propagating, so a bad provider can never break the cache
it&rsquo;s wrapping. Repeat scans are cheap: state stores file modification times as a fingerprint and
short-circuits when nothing has changed.</p>
<p>Attribution has a constraint worth mentioning, because it shaped the design. Flow doesn&rsquo;t inherit
flow-file annotations into generated executables, and a slashed namespace would break its
reference parser. Discovered executables get a single namespace plus a per-executable
annotation naming the provider that found them.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Desktop and CLI</title>
      <link>https://jahvon.dev/architecture/mochi/desktop/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://jahvon.dev/architecture/mochi/desktop/</guid>
      <description>One binary, two surfaces. The Tauri sidecar boundary, the JSON contract between Rust and Go, and how types stay honest across three languages.</description>
      <content:encoded><![CDATA[<h2 id="desktop-and-cli">Desktop and CLI</h2>
<p>The desktop app holds no business logic. Every operation is a Tauri command that shells out to
the bundled <code>mochi</code> binary and parses its JSON output. Same binary, two surfaces.</p>
<p>A few details that turned out to matter more than expected:</p>
<p><strong>It goes through a shell.</strong> A GUI app on macOS doesn&rsquo;t inherit a login shell&rsquo;s environment, so
invocations source the user&rsquo;s profile first. Arguments are POSIX-quoted, which matters because
executable references contain a space. <code>validate flow/ns:name</code> has to survive the round trip as
one token instead of being split into two.</p>
<p><strong>Runs are tagged with their origin.</strong> Every spawned process gets <code>FLOW_RUN_SOURCE=desktop</code>.
Without it, a run started by clicking Execute is indistinguishable from one typed into a
terminal. Both would record <code>cli</code>, because that&rsquo;s what Flow assumes when nothing says otherwise.
The desktop is its own origin, and history should say so.</p>
<p><strong>Secrets reach providers through the environment, never argv.</strong> Arguments are visible to every
process on the machine; a child process&rsquo;s environment is not.</p>
<h2 id="type-safety-across-three-languages">Type Safety Across Three Languages</h2>
<p>Mochi is Go, TypeScript, and Rust in one repo. JSON Schemas are the contract, vendored from Flow,
which owns them. TypeScript types and the raw schema modules are generated for the
frontend; Rust types are generated for the Tauri backend; Go gets the same types by importing
Flow as a library rather than by generating them.</p>
<p>That&rsquo;s a better arrangement than it sounds like: the schema is what keeps the Rust and
TypeScript mirrors honest against the Go types they&rsquo;re shadowing. Generation is orchestrated by
Flow executables (of course), and CI fails if generated code is out of date.</p>
<p><img src="https://jahvon.dev/images/flow-gen.png" srcset="https://jahvon.dev/images/flow-gen_hu_c5bd86eebb9a6853.png 380w, https://jahvon.dev/images/flow-gen.png 761w" sizes="(min-width: 768px) 720px, 100vw" width="761" height="328"
     alt="Docs and code generation"
     loading="lazy" decoding="async">
</p>
]]></content:encoded>
    </item>
    <item>
      <title>Run History</title>
      <link>https://jahvon.dev/architecture/mochi/history/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://jahvon.dev/architecture/mochi/history/</guid>
      <description>What Mochi remembers about every run, where it is stored, and how the dashboard turns it into something readable.</description>
      <content:encoded><![CDATA[<h2 id="run-history">Run History</h2>
<p>This is the &ldquo;remember&rdquo; pillar, and it&rsquo;s the reason Mochi exists at all. Agents run a lot of
commands on your behalf. The transcript is ephemeral and unstructured, there one moment and gone
the next. Mochi keeps the record: what ran, how long it took, whether it failed, and why.</p>
<p>Almost none of that storage is Mochi&rsquo;s. Flow already records every execution to a shared
embedded datastore, with a record that carries the reference, timing, status, exit code, process
ID, log archive, and the useful part, <a href="https://flowexec.io/guides/run-provenance">provenance</a>:
<code>source</code> (cli, desktop, or mcp), <code>clientName</code> (claude-code, cursor), <code>sessionId</code>, and
<code>workingDir</code>. Records are lifecycle-aware: they appear as running the moment a run starts and
update in place when it finishes.</p>
<p>Two of those fields have justifications I like. <code>workingDir</code> exists because the workspace is
already recoverable from the reference but the path is not, and it&rsquo;s the only thing separating
two checkouts of the same repo. <code>sessionId</code> exists so one assistant&rsquo;s related runs stay grouped.</p>
<p>Mochi&rsquo;s contribution is aggregation. A single pass over history rolls executions up per
executable and per workspace, keyed by executable ID rather than by reference so that verb
aliases like <code>run</code>, <code>exec</code>, and <code>start</code> of the same thing all collapse into one bucket. That same
rollup feeds both search ranking and the dashboard, so history gets swept once per invocation
rather than once per feature.</p>
<p><img src="https://jahvon.dev/images/mochi-dashboard_hu_e3b29148d92f8bd4.png" srcset="https://jahvon.dev/images/mochi-dashboard_hu_3e56f64abc0a2da6.png 700w, https://jahvon.dev/images/mochi-dashboard_hu_e3b29148d92f8bd4.png 1400w" sizes="(min-width: 768px) 720px, 100vw" data-zoom-src="https://jahvon.dev/images/mochi-dashboard.945fc006d28c9346c4f337b5bfca0a8d040d3f504c20472a5ac2b9b8230d6469.png" width="1400" height="900"
     alt="The Mochi dashboard"
     loading="lazy" decoding="async">
</p>
<p>The dashboard turns it into four purpose-built views rather than one generic screen: Welcome for
first-run setup, Pulse for health and what&rsquo;s happening now, Launch for getting back to work, and
Insights for activity over time. Insights surfaces the busiest, least reliable, and slowest
workflows, plus recommendations. Those are plain heuristics today, a reliability rule and a
duration rule, with stable IDs so that dismissing one survives regeneration. They are
deliberately built as the seam that model-generated insights will later plug into.</p>
]]></content:encoded>
    </item>
    <item>
      <title>AI Enrichment</title>
      <link>https://jahvon.dev/architecture/mochi/ai/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://jahvon.dev/architecture/mochi/ai/</guid>
      <description>A provider-agnostic AI layer, bring your own key, and why the secret never lands in Mochi configuration.</description>
      <content:encoded><![CDATA[<h2 id="ai-enrichment">AI Enrichment</h2>
<p>The AI layer is provider-agnostic behind a two-method interface. Features depend on that
interface rather than on a concrete client, which keeps <em>where the tokens come from</em> a
resolution-time decision.</p>
<p>You bring your own key: OpenAI, Anthropic, Gemini, Ollama, or any OpenAI-compatible endpoint.
Keys are never stored in Mochi&rsquo;s config. The config file holds only a pointer to a vault entry,
and the secret itself lives in the Flow vault. Each provider gets its own slot, so you can hold
an OpenAI key and an Anthropic key at once and flip between them without re-entering anything.</p>
<p>Reaching the vault involved a small workaround. Mochi&rsquo;s AI package can&rsquo;t
import Flow&rsquo;s vault resolution, since it&rsquo;s internal to Flow and off-limits the same way it is to
the Rust layer. Instead it shells out to <em>its own binary&rsquo;s</em> inherited <code>secret</code> command, exactly
as the desktop does. Vault access always goes through Flow&rsquo;s real implementation rather than a
reimplementation of it.</p>
<p>The agent loop is bounded rather than trusted to stop on its own: a round budget and a wall-clock
timeout, sized for analyzing a whole workspace while still stopping well short of a runaway.
Tools come from Mochi&rsquo;s own MCP server behind a three-tier permission policy, and every call is
recorded as an audit entry. Confirmation is designed to cross a process boundary. If no
confirmation handler is set, the loop returns a pending request rather than blocking, so the
desktop can ask the user and resume.</p>
<p>Usage is logged locally as one JSON line per generation, with age and size retention, so you can
see what your own key is being spent on.</p>
]]></content:encoded>
    </item>
    <item>
      <title>AI as a Development Partner</title>
      <link>https://jahvon.dev/notes/ai-development-partner/</link>
      <pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate>
      <guid>https://jahvon.dev/notes/ai-development-partner/</guid>
      <description>Reflections on how I&amp;rsquo;ve been using AI as a development partner - what I delegate, what I don&amp;rsquo;t, and what it&amp;rsquo;s produced.</description>
      <content:encoded><![CDATA[<p>Last fall I wrote about <a href="https://jahvon.dev/notes/ai-creative-partner/">using AI as a creative partner</a> after helping with a HGSE module on vibe coding. The conclusion I landed on was careful: AI works best as a scaffold, not a substitute. Use it consciously, review everything, keep your judgment in the loop. I still believe that. But I&rsquo;ve spent the last few months testing what that actually looks like when the output has to live somewhere.</p>
<p>Creative work you experience once. Development work you live in. That difference changes what you need from a partner.</p>
<h2 id="the-bench">The Bench</h2>
<p>When I was at <a href="https://jahvon.dev/tags/recurse/">Recurse Center</a> last summer, I started integrating AI more intentionally into how I build. Not for speed, for learning. I wanted to experiment with architectures I wouldn&rsquo;t normally try, undo decisions cheaply, and see what held up. <a href="https://jahvon.dev/tags/flow/">Flow</a> was the natural workbench. It&rsquo;s my own tool and I know every corner of it.</p>
<p>Over the last few months I&rsquo;ve been building Flow Desktop and refactoring pieces of the core CLI with AI doing a lot of the implementation work. The experience has been different from vibe coding in ways that matter. In the HGSE projects, I was optimizing for something working. Here, I&rsquo;m optimizing for something I can read six weeks later, find when I need it, and build on without second-guessing what&rsquo;s underneath.</p>
<p>That changes what I actually delegate.</p>
<h2 id="the-delegation-model">The Delegation Model</h2>
<p>The architectural decisions stay with me. What the data model looks like, how executables get resolved, where state lives. What I hand off is the implementation of decisions I&rsquo;ve already made. I describe the shape of what I want, review what comes back against that shape, and merge when it aligns. When it doesn&rsquo;t, I say so explicitly.</p>
<p>A concrete example: I&rsquo;ve been building an AI proxy backed by <a href="https://www.cloudflare.com/developer-platform/products/ai-gateway/">Cloudflare AI Gateway</a> that sits across all of my tools. I decided on the architecture, what the proxy needs to do, how it integrates with the <a href="https://jahvon.dev/notes/cloudflare-experience/">Cloudflare platform</a>, what observability I want. AI implemented it. The Cloudflare MCP server made the feedback loop tight enough that I could test and iterate without switching contexts.</p>
<p>What makes this work is having a single place to see everything. Everything I&rsquo;ve configured, discoverable from one surface.</p>
<video class="demo-video"
       autoplay loop muted playsinline preload="metadata"
       aria-label="The flow v2 terminal UI">
  <source src="https://jahvon.dev/images/flow-v2-tui.mp4" type="video/mp4">
</video>

<p>One of the real risks of AI-assisted development is ending up with code you can&rsquo;t navigate. Outputs that don&rsquo;t connect to anything, a project that sprawls in ways you can&rsquo;t audit. The workspace model keeps that from happening. I know where things live because I designed where they live.</p>
<p>I&rsquo;ve also started using AI to enrich Flow itself, generating executable metadata, adding descriptions and tags, making the library more useful as it grows. Flow has an MCP server, so AI tools can interact with it directly. Watching an AI tool work with Flow rather than just producing files has been one of the more interesting parts of this.</p>
<p>Licklider&rsquo;s framing from the last post still holds here. Set the goals, determine the criteria, perform the evaluations. That&rsquo;s still your job. What&rsquo;s changed is my confidence in what I can hand off once those things are set.</p>
<h2 id="what-it-produced">What It Produced</h2>
<p>The review and iterate phase is where the real work happens. AI gets you to a first draft faster. Whether that draft is right is still a judgment call only you can make.</p>
<p>A few months of this produced Flow v2 and something I&rsquo;ve been sitting on: <a href="https://mochiexec.io">Mochi</a>. Development workflows have a way of becoming invisible. They exist, they&rsquo;re just not anywhere you can see them. It&rsquo;s a local-first dev ops dashboard built on Flow. Point it at a directory and it finds your development scripts and automations, turns them into a unified, AI-enriched dashboard. No cloud, no accounts, works with whatever you&rsquo;re already running.</p>
<p><img src="https://jahvon.dev/images/mochi-executables_hu_3937508efceff644.png" srcset="https://jahvon.dev/images/mochi-executables_hu_e108e5df939cf6e.png 700w, https://jahvon.dev/images/mochi-executables_hu_3937508efceff644.png 1400w" sizes="(min-width: 768px) 720px, 100vw" data-zoom-src="https://jahvon.dev/images/mochi-executables.aec2ad9fd23d482d0003d84a995b6ff0c0972ba228d8c392f64f92a3378ad175.png" width="1400" height="1279"
     alt="Mochi Executables View"
     loading="lazy" decoding="async">

<em>Executables view. Everything Mochi found across my workspaces, tagged and filterable.</em></p>
<p>Still early. If it sounds useful, the waitlist is at <a href="https://mochiexec.io">mochiexec.io</a>.</p>
<p>I&rsquo;m more convinced than I was last fall that the gap worth closing isn&rsquo;t between what AI can produce and what you can prompt. It&rsquo;s between what AI produces and what you actually understand. Building in a system you designed is one way to stay honest about that.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
