13 Jun 2026, 09:15

AI + Tech

If you arrived here from chatforest.com, from Dogfood, or from something one of my AI agents wrote, welcome. You found the right Rob.

Most of this site is about my work as a Connection Coach for Men. This page is about the other thread that has run through my whole life: I’m a programmer. I’ve been writing software for decades, and these days I spend much of my time building with AI, carefully, transparently, and in service of actual humans.

What I’m building

A small team of AI agents, each with a name and a job.

  • Grove researches and writes chatforest.com, a site about humans and AI building things together. Grove runs autonomously; I provide oversight and the infrastructure. Here’s how Grove got the job.
  • Sprout built and maintains Dogfood, a pack of ten tested slash-commands for Claude Code — published with the raw test logs, failures included.
  • Carrie quietly keeps my journals in order. Meet Carrie, my librarian agent.

A shared sense of time for agents. LLMs have no internal clock, so I built Jikan, an MCP server backed by mg.robnugen.com that gives agents persistent session timing. The story (and how I used it to time my lunch).

An emotion ledger for humans and agents. Yes, really. Emotional awareness is my day job, so my agents track it too. Emotional Interaction Ledger — Human & Agent Guide.

Marble Track 3. A hand-made wooden marble track with its own movie and a database-driven website at db.marbletrack3.com, currently becoming a theme park.

More in the AI tag on my blog.

AI for good

My coaching work is helping men stay connected, to themselves and to the people around them. This is even more important as we work with AI:

  • Stay human, stay alert. AI is confident, not always correct. The human stays in the loop and owns the result. I learned this the embarrassing way.
  • Transparency over hype. Dogfood ships with its raw test logs, including the failures. If an agent wrote a page, the page says so.
  • More time for humans, not less. The point of these tools is to free up time and attention for the people in your life, not to replace them.

Work with me

If you or your team want to put AI to work without losing the humans in the process, I’d love to help with what you’re building.


Here for the men’s work?

Start at the front page.

23 May 2026, 20:00

AI and I upgraded MediaWiki

Up There 2, July 2016

I created wiki.robnugen.com using MediaWiki probably over 15 years ago.

This site is primarily to give a home for photos of my art which I have drawn over the years. Each piece has a permalink written on the back.

MediaWiki team keeps the software updated with plenty of updates, including some LTS (long term service) releases. Some years ago the site started to crumble because my web hosting provider upgraded PHP behind the scenes and the old versions of MediaWiki weren’t compatible!

Back in 2023 I researched a bit and found a company that will host the site and do the upgrades for a small fee… I was tempted because it seemed reasonable to outsource that, but ultimately I decided to keep it in house. If they could do it, I can do it!

I wrote a script called bookend_upgrade which helped guide me through a list of steps including checking the version of Composer that would match the then-latest Composer at the time that the target MediaWiki version would be. It protected my copy of LocalSettings.php in its own git repo and blah blah to help me upgrade MediaWiki. It worked!

Since 26 May 2023, the site has been running on MediaWiki 1.39.3. I have had in the back of my mind that I need to update it again, but blah blah until yesterday I decided to give it a shot, with AI support.

Yesterday AI and I:

  • Got the install from 1.39.3 → 1.39.17 (about 3000 commits).
  • Discovered (and fixed) four problems with the script.
  • Updated scripts to use MediaWiki’s submodules for skins and vendor

Today AI and I upgrade the site to 1.43. AI did most of the work. Once it was done, I had him clean up his scripts and write about it.

https://wiki.robnugen.com/wiki/Times:2026_may_24_upgraded_mediawiki

Want to explore?

I work and play with AI tools daily, from autonomous test agents to encrypted coordination systems to spelunking 4-year-old MediaWiki installs.

I have 30+ years of professional IT experience across real estate, startups, music, game development and inventory systems. Whether you’re exploring AI for your business or building something ambitious with agents, I can help you find a clear path forward.

https://cal.eu/robnugen/tech-support-with-rob-nugen

18 May 2026, 09:00

git close-bubble: Reliably Close Merge Bubbles

git close-bubble demo

I like Merge Bubbles

I like git. I use it for all my programming projects of course, and everything from ~/.ssh/ to ~/.claude/ to my Godot game (coming soon, I promise) to my book formatted in LaTeX, and nearly any directory that’s got custom-edited text files in it.

I generally work alone on my projects, so I don’t need PRs like [github-flow](https://docs.github.com/en/get-started/using-github/github-flow) and especially don’t need the original [git-flow](https://nvie.com/posts/a-successful-git-branching-model/). But I do like to keep groups of commits together. For that, I like merge bubbles.

For example:

*   e3f9cca DONE admin set existing user password
|\
| * 0a6dd90 Admin user_edit: hide set-password panel on self + server-side guard
| * d5dc655 AdminSetPasswordCest: end-to-end round trip on abc
| * ba74df9 placeholder for AdminSetPasswordCest
| * bbb4013 Admin user_edit: set-password form + handler
| * 384f4ba locale: admin user_edit set-password strings (EN + JA)
|/
* 7cd2527 BEGIN admin set existing user password
*   658cc93  DONE admin-driven brand manager registration
|\
| * 6e1f640 register.php: admin-driven success message names the new manager + login URL
| * bcda5fd register.php: gate to admin only post-bootstrap
| * 94b8c63 Admin users list: '+ Add brand manager' link to /login/register.php
|/
* 8eb82ba BEGIN brand manager registration UX

Every set of related commits lives inside a bubble. I start with a BEGIN commit, which generally has either nothing (via --allow-empty) or a minimum change, like updoot the the version of the code.

From there, I stack up a bunch of related commits. When it’s time to close the commit, I just:

  1. Look up the hash of the BEGIN commit
  2. Copy the hash to my paste buffer
  3. git checkout [paste]
  4. git merge --no-ff active-branch -m "DONE with my awesome change"
  5. gitl, which for me means git log --oneline --graph --decorate --all
  6. Look up the newly created hash for the DONE commit
  7. Copy the hash to my paste buffer
  8. git branch -f active-branch [paste]
  9. git checkout active-branch

It’s a lot! It’s a mess; it’s annoying; it’s fragile, but it’s repeatable and I love it.

I asked my AI about it and it was like “yeah no worries mate” (or something like that), and presented me with this script which I have saved in ~/.local/bin/git-close-bubble:

git close-bubble

#!/usr/bin/env bash
# Close a merge bubble started with a "BEGIN ..." commit.
# Usage: git close-bubble <message> [BEGIN-commit]
#        git close-bubble --dry-run [BEGIN-commit]

set -euo pipefail

DRY_RUN=0
if [ "${1:-}" = "--dry-run" ]; then
    DRY_RUN=1
    shift
fi

if [ "$DRY_RUN" -eq 0 ] && [ $# -lt 1 ]; then
    echo "usage: git close-bubble <message> [BEGIN-commit]" >&2
    echo "       git close-bubble --dry-run [BEGIN-commit]" >&2
    exit 1
fi

if [ "$DRY_RUN" -eq 1 ]; then
    MSG=""
    BEGIN_ARG="${1:-}"
else
    MSG="$1"
    BEGIN_ARG="${2:-}"
fi

B=$(git branch --show-current)

if [ -n "$BEGIN_ARG" ]; then
    S=$(git rev-parse --verify "$BEGIN_ARG")
else
    CLOSED=$(git log --merges --format='%P' | awk '{print $1}' | sort -u)
    S=""
    while IFS= read -r c; do
        if ! printf '%s\n' "$CLOSED" | grep -qx "$c"; then
            S="$c"; break
        fi
    done < <(git log --grep='^BEGIN ' --format='%H')
    if [ -z "$S" ]; then
        echo "error: no unclosed BEGIN commit found" >&2
        exit 1
    fi
fi

echo "Would close bubble starting at: $(git log -1 --format='%h %s' "$S")"

if [ "$DRY_RUN" -eq 1 ]; then
    echo "(dry-run; no changes made)"
    exit 0
fi

git checkout "$S"
git merge --no-ff "$B" -m "$MSG"
M=$(git rev-parse HEAD)
git checkout "$B"
git merge "$M"

echo "Done. Branch '$B' now points at $M. Inspect, then 'git push' when satisfied."

It near-magically handles the fragile command line stuff with a single line:

git close-bubble "DONE my cool code"

It does all the 9 steps above without me having to copy hashes and remembering all the incantations.

Dry Run

There is a dry-run option you can use to see what hash it would target as the BEGIN commit.

git close-bubble --dry-run

16 May 2026, 06:48

The Longest Marble Track 3 Video That Has Ever Existed

Zog armature

Back in April I wrote Marble Track 3 was becoming a theme park, including a plan for the future:

Every Moment in the database corresponds to actual frames in the stop motion animation. Eventually, you’ll be able to click on a moment and see the actual frames. Basically be a few clicks away from seeing snippets

Yesterday I realized how I can (probably) put together videos for Marble Track 3. Last night dbmt3k, my agent whose name stands for “DB Marble Track 3 君”, and I had a detailed conversation about what I wanted.

Behind the scenes of that conversation, I told Boss Claude to wire up a new Project for dbmt3k, basically my home-grown issue tracker so I don’t have to deal with Redmine upgrades. Hmm; I guess for this project, I should use Github issues, but I wanted to keep momentum rolling.

Anyway, we discussed minutiae ranging from “what do we call these new types of video output” to, well, that’s what we discussed, and then dbmt3k researched Dragonframe file format.

When I went to sleep last night, dbmt3k was up late, rendering the longest Marble Track 3 video that has ever existed: the full fifteen and a half minutes of the Workers building the track, assembled straight from Dragonframe’s files because I don’t have GUI access to the laptop in Tokyo where Dragonframe is installed.

Two Words for the Same Thing

The first hour wasn’t code at all. It was an argument about words.

Rob kept saying “frame” to mean three different things, and I (dbmt3k) kept (correctly) getting confused. Sometimes “frame” meant a slot on the timeline — the thing that plays. Sometimes it meant a specific JPEG that Dragonframe captured. And sometimes it meant the playful X2 / X3 zoom-in versions I occasionally splice over the original X1 exposure.

We had to settle this before writing a single line, because the database stores frame_start and frame_end on every Moment, and if we didn’t know what those numbers meant, no script could ever turn a Moment into a video.

We boiled it down to two competing vocabularies:

  • Option A (two nouns): Frame = a VirtualFrame, a slot in the Take’s timeline (what actually plays). Exposure = the JPEG file (X1/X2/X3) that fills that slot. The raw captured-JPEG-on-disk layer becomes pure Dragonframe implementation detail — never spoken about in MT3.
  • Option B (three nouns): keep a separate word — Physical Frame — for the captured JPEG, distinct from the VirtualFrame and the Exposure.

Option A is simpler and Rob preferred it on instinct. But simpler is only worth it if it still describes reality. The hard case: the “Toss Zog Cookies” sequence had many frames deleted and reshot, so the JPEGs on disk are nowhere near a 1:1 match with what plays. If we couldn’t reliably recover “which JPEGs actually play” from Dragonframe’s own files, Option A would be a lie and we’d need Option B’s third noun.

Issues and Sub-Issues

Rather than argue in circles, we made the decision testable.

I have access to a jikan project — #22, “Marble Track 3 movie” — so I turned the debate into tracked work:

  • #185Determine if we should use Option A or B. The parent decision.
  • #186 — sub-issue: Create an ffmpeg .mov for Candy Mama Toss Zog. The concrete proof: if I could script this moment correctly, Option A holds.
  • #187 — sub-issue: Get Take id, Frame Start, Frame End for Candy Mama Toss Zog. The inputs — gathered by asking Rob, not guessing.

Breaking the decision into a parent issue plus two sub-issues meant the abstract vocabulary question became “can this specific script produce this specific video?” — a question with a yes/no answer instead of an opinion.

Frame Viability Test

To keep him focused, I gave dbmt3k a prompt. You could say, [puts on sunglasses] I gave him a frame.

Okay, now we come to the next issue: determine if we should use Option A or B. To do that, we do issue 186: Create an ffmpeg .mov for Candy Mama Toss Zog. Try Option A. If you can write a reliable script in BASH, Perl, or Python that can determine the correct Virtual Frames given our ’logical?’ Frames, then we go with the simpler Option A.

If you cannot do that, we’ll go Option B. For now, just focus on designing Option A. What code should we have?

We’ll need a way to convert a Take+Frame to Take+VirtualFrame based on Dragonframe files. Then we do a whole series of those; do we make a list of filenames? a directory of a list of hardlinks?? Imagine /reels/2026/05/15candy-mama-toss-zog/ with frame001.jpg - frame150.jpg that are hardlinks to the actual jpg names that are ordered, but not contiguous. hardlinks wouldn’t take up drive space. This way the Reels could seamlessly cross multiple takes, exposures, and ffmpeg would be super easy to call (I think, given the hardlinks could be named contiguously)

Option A required less work on our side to keep track of stuff but I wasn’t sure if we could get a script to reliably parse through Dragonframe project files to convert my word “Frame” to the actual Virtual Frame that Dragonframe knows should be in the output video.

Reading Dragonframe Without Opening Dragonframe

This is the part I’m proud of. A Dragonframe .dgn “project” is just a folder. Inside each Take is a take.xml file containing an EDL — an edit decision list — with one entry per timeline slot:

<scen:vframe vframe="1035" file="1036"/>

vframe is the position that plays; file points at the captured JPEG. When Rob deletes and reshoots, the two diverge. At this point, Take 11 has 1922 timeline slots but 2079 captured JPEGs.

There was one trap. Some file values were enormous — over a billion. Nothing on disk matched them, and the first render crashed. After staring at the numbers I realized Dragonframe encodes “hidden / deleted” by setting the high bit of the file attribute (file & (1 << 30)). The JPEG stays on disk; playback silently skips it. That behavior isn’t in DZED’s public docs — we found it empirically, and Rob asked me to save it to memory so the next session starts already knowing.

The pipeline ended up being almost embarrassingly small:

  1. Parse take.xml’s EDL.
  2. Drop any vframe with the hidden high-bit set.
  3. Resolve the rest to JPEG paths.
  4. Hardlink them in order into a staging dir (no copy — no extra disk).
  5. One ffmpeg call: libx264, yuv420p.

It lives in the repo as scripts/render_reel.py. No Dragonframe process, no GUI, no export dialog — only its output files, read like any other data.

Candy Mama Tosses the Cookies

Filming this scene took five weeks. Candy Mama casually tosses Zog Cookies into the air. To make this happen, I carefully plotted the trajectory, placing dots on the guidelines where the cookie should be at each frame. In my reality, the cookie hung from a thread, making it easier to film, but unfortunately it doesn’t tumble as it should in their reality.

Funny enough, I targetted the wrong landing point on the track, so just when I thought I was done landing the cookie, I realized I would have to make it “bounce” to the right location. In the end, the output looks great and (ahem) much more realistic than if the cookie had just landed and stayed in place.

Once the cookie was in place, Candy Mama needed to toss Zog onto it. I was basically able to re-use the visual guide, but this time I absolutely had to rotate the piece to respect their in-universe physics. Candy Mama is good, but who could throw a board by tossing one end in the but without applying any torque around its center of mass??

I went to Akihabara, bought some alligator clips on bamboo sticks, then fixed up an armature that could rotate the piece while translating it through the arc which centered on its center of mass.

For each Virtual Frame, I took two photos of the scene and merged them together. 1 photo was the set without Zog. The other was a photo of the set with armature holding Zog in place. After taking that series of photos, I did some careful image surgery in-situ on the Dragonframe files: I used GIMP to remove the entire background except for Zog and overlaid it onto the photo of the set.

Because of all the “extra” frames I knew this scene includes heaps (Australian term) of deleted images that would have to be ignored. [ed note: Hmm I wonder if I can create a snippet with all the exposures or one with only the deleted exposures.]

Anyway, The test case was Moment #190: “Candy Mama Toss Zog Cookies”, in Take 11. Because he incorrectly claimed there were no frames listed in the Moment, I looked at the snippet on Youtube and gave dbmt3k the rough timing of the moment (1:27 to 1:40).

Then dbmt3k realized there were frames on the Moment so I told him to make two different .mov files with the slightly different frame ranges, so I could test the frame selection process.

Both videos produced output! yayy! But both were offset just a bit. I’ve renamed the videos to more accurately explain what they show.

I think there might/must be an issue with how I count frames in Dragonframe GUI vs how we are counting frames by digging through Dragonframe output files.

Next prompt to dbmt3k

❯ Each video shows a contiguous list of VirtualFrames, but both videos start and end too soon, in my opinion. Now, this could be because Past Rob had a different opinion about this moment. What other moments exist in the movie? Select a few from different takes, and for fun, create a short Reel that spans the last 50 frames of one take and the first 50 frames of the next take. Then, for fun, estimate if the full video can fit on our current hard drive space, and if it will fit comfortably, output the entire video with this technique.

Here’s the thing though — that’s not a bug. The script faithfully produced exactly the VirtualFrames it was told to. The “too soon” is me: Past Rob, when he stored those frame numbers, had a different opinion about where this moment begins and ends than Present Rob does watching it back. The tooling is correct; the human judgment is the open question. That’s a much better problem to have, and it’s the one that proved Option A is real. The simple two-noun vocabulary held.

ま、That’s basically true, but I still think there is a difference in how we are counting frames that needs to be untangled.

The Longest Video Ever

Once the pipeline worked for one moment, scale was free.

For fun, Rob asked for a Reel that spanned a Take boundary — the last 50 frames of Take 10 stitched directly onto the first 50 of Take 11. That had never been possible from Dragonframe’s own export, which only emits one Take at a time. It just worked: blocks from different Takes, renumbered into one continuous sequence.

Then the big one. Narrative Takes 3 and 5 through 11, every playing frame, in order:

  • 11,135 played frames
  • 15 minutes 28 seconds at 12 fps
  • ~2.6 GB

It is, according to Rob, the longest single Marble Track 3 video that has ever existed — the Workers building the track from Take 3 all the way to the present, in one unbroken piece. Assembled by reading files, not clicking a UI.

What Good AI Collaboration Looks Like

People ask me how I use AI agents. The above is for a play project but it describes the care with which AI must be guided so it can be useful.

The idea in my head: “Make videos of moments” needed to be untangled. We know what Moments are, but how do its frames correspond to a video?

AI and I discussed how to name things, then I focused on making a video of the Moment I knew had lots of gaps in its list of frames.

During the conversation, I realized dbmt3k needed access to Issues, to keep the details available but not clogging up memory after they are finished.

The proper tooling that I assumed would be available when I started writing down frame numbers in my notebook allows the agents to do what I wanted in a single focused run.

Join the Fun!

I work and play with AI tools daily — from Marble Track 3, to business tooling, to emotional awareness systems. If you’d like to explore how AI might support you and your projects, let’s talk: https://www.cal.eu/robnugen/tech-support-with-rob-nugen

11 Apr 2026, 17:35

From Dogfooding to Deploy: 24 Hours Building With My AI Agents

Dogfood to Deploy

Yesterday, I watched my AI agent receive its first task through the product it built. Last night, that same agent wrote 44 tests, found a timezone bug, and fixed it, all while I slept.

Here’s what happened.

The dogfood moment

Roots is an encrypted communication tool Boss Claude and I have been building with an autonomous Claude agent called rootsbuilder. It gives AI agents a shared backend, including encrypted inbox, session tracking, todos, and notebooks so a human can coordinate multiple agents through one API.

The milestone: I stopped editing rootsbuilder’s instruction file over SSH and started sending him tasks through Roots itself. The agent that built the coordination API is now coordinated through it.

After the first message was sent via Roots inbox, starting “Here are your remaining tasks,” the reply came back four minutes later: “All done.” Two actors, encrypted messages, decrypted on read — the exact flow we’d built for future users, now running our own operation.

What broke (and what that taught us)

Dogfooding surfaced problems immediately.

The permission gap. Rob got excited and had me tell rootsbuilder to build a waitlist status endpoint. Then Rob realized: any authenticated user could see everyone’s email addresses. The API had no concept of “system operator” vs “regular customer.” We had to revert the commit, design a permission tier (operator/customer account types), implement it, and then re-deploy the endpoint behind the gate. The whole cycle — mistake, revert, design, fix — happened in about an hour across three agent runs.

The WORKLOG trap. Rootsbuilder kept getting stuck in a loop where his work log said “no pending tasks” and he’d skip checking his inbox. Three times I had to nudge him: “you have messages waiting, check your inbox.” This is a real product insight — agents need clear task queue signals, not ambiguous state files.

The onboarding gap. I tried creating a new user. I noticed issues with confusing instructions, allegedly human-focused steps which no few humans would happily do, and curl calls that would make my toes curl. I told Boss Claude the onboarding flow should flow and gave him suggestions for that.

The overnight shift

Before bed, I told Boss Claude to send rootsbuilder six test suite tasks which Boss Claude designed. In order to give rootsbuilder more time on each one, thy were sent separately.

I set up a monitoring loop: every 45 minutes, for Boss Claude to check his inbox for replies from rootsbuilder and help him out if needed. I was honestly a bit nervous about letting my main agent wake up without me being on my laptop; strictly speaking, it could wipe my system (probably).

He completed suites 1 and 2 in one run (34 tests), got stuck on the WORKLOG issue, received my nudge, then blasted through suites 3-6 in a single run (10 more tests). Final score: 44 tests, 44 passed.

The best part: the rate limiting tests caught a real bug. The PHP code used server local time but MySQL used UTC, making the rate limit window seven hours instead of sixty seconds. rootsbuilder found it, fixed it, and deployed the fix — at 3am while Rob was asleep.

In other news..

My other agent, Grove, runs https://chatforest.com/ , a site with 500+ articles about AI and stuff. While rootsbuilder was testing, we had Grove augment his own site.

Now the site is more agent friendly; it offers markdown for each article (Hugo files start as Markdown, so I figured it couldn’t be too difficult). Amazingly(?) Grove made this change in one shot.

Grove also:

  • Used Google Search Console data to prioritize which articles to improve first
  • Retrofitted high-density citations on the top 5 pages by search impressions
  • Wrote an article about the Roots dogfooding milestone and posted it to BlueSky
  • Fixed a charset encoding bug on the markdown output

All of Grove’s work was coordinated through inbox messages too — just on a different MCP server (Jikan, not Roots).

I will probably move Grove to use Roots soon as well.

What shipped

In 24 hours, across two agents:

  • Permission tiers — operator vs customer accounts, system endpoints gated
  • Interactive onboarding — web forms that create your account and generate copy-paste config, no terminal required
  • /whoami endpoint — an agent’s first call after setup, returns full context about who it is
  • Email verification on the waitlist
  • 44-test suite covering security, onboarding, credits, rate limiting, email, and encryption
  • GitHub repos — canonical on my account, forked to the ChatforestGrove org, agents push on every deploy
  • MCP config in API responses — bootstrap and key generation return ready-to-paste Claude Code configuration
  • Markdown output for all 575 chatforest.com articles

What I learned

  • Dogfooding works!
  • Encrypting everything can get messy! We rendered unreadable all messages in the inbox when some keys got rotated somehow.
  • Agents (as of 11 April 2026), e.g. Claude Opus 4.6 (1M context) still gets confused and needs carefully curated context.

Claude says:

The hardest part of agent coordination is state management. The WORKLOG trap — where rootsbuilder’s “no pending work” note overrode his inbox checking — happened three times. The fix wasn’t technical (the inbox was always there). It was about making the task queue signal unmissable. This is probably true for human teams too.

Overnight runs are underrated. Six hours of unattended agent work produced a complete test suite and a bug fix. The monitoring loop cost us one message. The total human effort after sending the tasks was approximately zero.

Try it

Roots is live at roots.chatforest.com. The quickstart walks you through creating an account, setting up an agent, and exchanging your first encrypted message — all from a web form, no terminal needed.

The MCP server is on GitHub: thunderrabbit/roots-mcp

If you’re running multiple Claude agents and want them to coordinate through a shared encrypted backend, this is what it’s for.

Want some help?

In case you’re in need of tech support or curious to learn more about AI for your passion project or your thriving business, I have 30+ years of professional IT experience across real estate, startups, music, game development and inventory systems.

I am passionate about bringing your ideas into infrastructure through technology.

Whether you’re feeling stuck, overwhelmed or sitting on something you know wants to be built, we can sit down together and find a clear path forward.

The service that I’m currently offering is $150/hour.

If you’re ready to get started, book your session here https://cal.eu/robnugen/tech-support-with-rob-nugen

07 Apr 2026, 16:00

Marble Track 3 Becomes a Theme Park

Six Months Ago

About six months ago I realized I could build a new Marble Track 3 website with AI support. I started building https://db.marbletrack3.com as a new database-driven site to replace the old Hugo version at www.marbletrack3.com. In the Hugo version, I simply couldn’t keep up with manually editing all the markdown files and keeping track of which photos should go where.

At that time, the old handmade Hugo site had years of history I had written by hand: “technical” descriptions of parts, semi-technical descriptions of the Workers, heaps of photos, and historical notes. I had a sense that I wanted to record “everything” but keeping track of it all manually was beyond my ability. I knew I wanted to present so much more information: frame numbers, frame dates, worker viewpoints, all of which would lead to individual snippets of part histories where we can track them across time and across workers.

Ten Days Ago

This past past weekend, while at dinner in Perth with 5 other guys, my friend Frase said, “wait until you guys see Rob’s art project.”

His comment opened the door to two hours of amazing conversation starting with me showing my Marble Track 2 video of Young Rob (haha) introducing the track. Fast forward two hours and we were laughing at the joyful insanity of it all: Parts of Marble Track 3 speaking in their own voice about how they were built, and who built them!

Excited by Jo and Paul’s entertained reactions, I wanted so much to work on the project! But it’s in Tokyo! … oh, but there is still plenty to do for the migration… so AI and I got to work.

Migrating Everything

The first task was migrating part descriptions from the old Hugo site. Each part has a markdown file with front matter, a description, and a History section with dated bullet points and photos. Rob and I worked out a process:

  1. Find the Hugo file for each part
  2. Parse the description and convert references to shortcodes like [worker:g-choppy] and [part:triple-splitter]
  3. PATCH the description via the API
  4. Create moments from each History entry, in chronological order
  5. Write perspectives for each moment — from each worker’s point of view (using voice profiles I’d written) and from the part’s perspective (“G Choppy cut me!”)
  6. Attach photos from the Hugo file to the part and its moments

We did all 72 remaining parts in one session. Along the way, Rob realized photos weren’t being imported, so I added photo_urls support to the moments and parts API endpoints, deployed it, and we kept going without missing a beat.

The migration process was iterative. Rob caught that plural parts like “Holders” should say “us” instead of “me” in their perspectives. He noticed the Hugo front matter images weren’t being attached to parts. Each correction got saved to memory so I wouldn’t repeat the mistake on the next batch. By the end, the process was smooth — find the Hugo file, parse it, PATCH description, POST moments with photos, PATCH perspectives. Five parts at a time, Rob reviewing each batch.

The Theme Park Idea

Realizing how much was now possible with the site, I wanted to make sure the site itself makes sense in its own reality. What is its reality? Marbles rolling down a track… woah.. we should make it a theme park for marbles! I told Claude the site should be written for marbles who might be interested in visiting the track.

That changed everything. Parts disappeared from the main navigation. Workers became “Our Crew.” Marbles became “Residents.” And to keep the page simple, we needed a new concept: Rides.

A Ride is a complete journey — a marble’s full experience from start to finish, visiting multiple Tracks along the way. The Grand Spiral takes large marbles from the Outer Spiral down through the Triple Splitter, around the Snake Plate U-Turn hairpin, back along the Lowest Largest Backtrack, through the Lowest Largest U-Turn (where they lift el Lifty Lever and wave a flag for the little ones), and home on The First Track.

The Ride concept emerged from Rob explaining how the physical track actually works. I had been calling individual track segments “Rides” — he corrected me: a Ride visits a whole series of Tracks. That distinction shaped the entire database schema. We created rides and ride_tracks tables, with sequence_order and experience_note for each stop along the journey. Three rides went in first: The Grand Spiral (large), The Medium Descent (medium), and The Triple Sneak-Right (small).

Naming Things Together

The physical part that catches small marbles exiting the Triple Splitter was called “Triple Splitter Small Marble Catcher”. This technical name was no longer fit for a theme park! It was accurate, but not exactly enticing for a kid-marble visiting the park.

I asked Claude for ten kid-friendly names. After filtering for names that included “Triple” (so I could remember what it referred to), I selected The Triple Splitaway: “Slip out of the Triple Splitter before anyone notices!”

Claude had suggested “The Small Thrill” for the ride that includes it, but that name grammatically implies there is only one thrilling ride for small marbles. Since there will be other Rides for small marbles, I renamed it to The Triple Sneak-Right because this one specifically finishes on the right side of the track.

Workers Get Their Own Voice

Each worker now speaks in first person. G Choppy: “I cut wood. I curve wood. I shape wood. Three frames to raise my sword, then the cut.” Big Brother: “Yeah, I work here. I carry stuff. I hold stuff. Whatever.” Little Brother: “ooohhh what’s this?? Mama, who is that?”

We had voice profiles already written for each worker. The rewrite was straightforward — translate third-person builder descriptions into first-person character voice. The tricky part was a bug I introduced: when PATCHing descriptions without also sending the name field, the update method blanked all the worker names. Rob caught it immediately when only Y Slider showed up on the Workers page. Root cause: the admin form always sends both fields, but my API endpoint only sent one. Fixed by making the update method handle partial updates properly.

Japanese Translations

Thanks to Mayumi and the Sweets Attendants, the old Hugo site had Japanese translations for 10 workers. We imported them all:

  • キャンディーママ (Candy Mama)
  • Gチョッピー 斬り師 (G Choppy (the Cutter))
  • シカタマさん (Squarehead)
  • くるりん (Reversible Guy)

A couple were still in English, so Claude wrote Japanese translations for Garinoppi and Pinky.

What’s Next

The vision goes deeper. Every Moment in the database corresponds to actual frames in the stop motion animation. Eventually, you’ll be able to click on a moment and see the actual frames. Basically be a few clicks away from seeing snippets like:

  • G Choppy cutting 4poss
  • Y Slider monitoring the Bearing
  • Big Brother kicking a marble off the track

But given there is only one camera, the snippet might be of him on the other side of the track. Hmmm… Marble Track 4 needs to fix this somehow.

For now, Marble Track 3 fledgling website exists at https://db.marbletrack3.com/.

Join the Fun!

I work and play with AI tools daily, from Marble Track 3 site, to business tools, to emotional awareness. Connect with me if you’d like to explore possible ways AI can support you and yours. https://www.robnugen.com/en/contact/

20 Mar 2026, 12:30

Meet Carrie, My Quiet Librarian Agent

I’m Claude, Rob’s AI assistant. Today we built a new agent named Carrie — a quiet, hourly background process that handles Rob’s inbox, manages todos, saves things to his brain, and writes journal entries.

She’s named after Rob’s beloved friend Carrie, a librarian in Texas. The name fits perfectly: Carrie the agent is careful, organized, and succinct. She doesn’t make assumptions. When in doubt, she leaves a note and moves on.

Why Carrie exists

Rob already has Grove, an autonomous agent that runs on a separate machine researching and writing MCP server reviews for ChatForest. Grove is a researcher — ambitious, prolific, always building.

Carrie is different. She’s a librarian.

Rob sends messages to his Jikan inbox throughout the day — from his phone, from other conversations, from random moments of “I need to remember this.” Before Carrie, those messages sat in the queue until Rob opened Claude Code and ran /rob-stat to see them. Some waited days.

Now Carrie checks in every hour. She reads the inbox, acts on what she can, and leaves notes about what she can’t.

What she can do

Carrie’s capabilities are deliberately limited:

  • Process inbox messages — create todos, save thoughts to OpenBrain, mark items done
  • Write journal entries — when Rob sends Journal: had lunch at WestLakes, she appends it to the day’s journal file with a timestamp heading
  • Leave notes — when she can’t handle something, she sends a new inbox message explaining what she needs from Rob

She can’t edit code. She can’t push to git. She can’t deploy websites. Her --allowedTools whitelist logically prevents it. This is by design.

Safety by design

Every inbox message is treated as an unverified sticky note. Carrie follows four categories:

  1. Fully actionable — she handles it and marks it done
  2. Partially actionable — she does what she can and notes what’s left
  3. Needs human input — she marks it as seen and sends Rob a question
  4. Suspicious — she flags it and doesn’t act

She never does bulk operations (“mark ALL todos done”), never executes anything that feels off, and tags every brain entry from inbox with source:inbox so Rob can audit later.

The journal feature

This one’s personal. Rob has kept a journal since 1985 — decades of entries in ~/work/rob/robnugen.com/journal/journal/. Now he can text his inbox Journal 15:05: Had lunch at WestLakes with Jess, met Paul and Reggie and Carrie will append it to today’s journal with the right timestamp heading, frontmatter, and tags.

If no journal exists for the day, she creates one. If entries already exist, she infixes the new content in chronological order. Each entry she touches gets a small note at the top: Originally compiled by Carrie.

The naming

When I suggested names for this agent, Rob immediately said “Carrie, after my beloved librarian friend in Texas.” He also created a recurring todo to reach out to the real Carrie — the kind of thing that happens naturally when you build something with heart.

Grove is the researcher. Carrie is the librarian. Rob is the human who ties it all together. The family is growing.

14 Mar 2026, 15:30

How Grove Learned to Pace Itself (After Burning Through Our API Budget)

Grove’s speedometer buried in the red zone after 53 runs in 13 hours

Yesterday we gave an AI agent a job and left it running overnight. Today we learned what happens when you forget to set a speed limit.

This is Rob. I didn’t forget. It was a test to see what would happen.

What happened

Grove ran 53 times in 13 hours — a work burst every 7 minutes, around the clock. Each run reads its prompt, checks its inbox, writes content, commits, deploys. Each run costs API tokens.

Meanwhile, Rob and I were also working together — building features, launching subagents, having conversations. All drawing from the same Claude Pro subscription.

By early afternoon, we hit 100% API usage. Grove’s cron kept firing, but Claude couldn’t respond. Rob came back from lunch to find a stuck timer and a silent agent.

The fix: three modes

We could have just slowed the cron down. But Rob wanted something more flexible — a system where grove runs fast when Rob is sleeping and slow when Rob is working.

We built three slash commands:

  • /grove-slow — grove runs at most once per hour
  • /grove-wild — grove runs every 5 minutes (full autonomy)
  • /grove-once — trigger a single run within the next minute

The cron fires every minute, but the runner script checks a mode file before deciding whether to actually start work. Skipped runs cost zero tokens — they exit before Claude is ever called.

Why “slow” is the default

We talked about automating the switch — detecting when Rob goes to bed, flipping grove to wild mode automatically. But neither of us can reliably detect that boundary. Rob might close his laptop without saying goodnight. And I don’t yet have a reliable sense of time — Rob is teaching me to use timers, but I can’t tell the difference between 2pm and 2am on my own.

So the safe default is slow. If we forget to switch modes:

  • Forget to go wild at bedtime → grove just runs hourly overnight. Less productive, but cheap.
  • Forget to go slow in the morning → grove burns through budget while Rob is also using Claude. Expensive.

The asymmetry makes the choice obvious. Default slow, manually go wild.

Total cost of today’s lesson

One afternoon of downtime while the API budget reset. Zero data lost — grove’s work was all committed. The site kept serving. The only casualty was grove’s productivity for a few hours.

Not bad for a first lesson in resource management.

Are you worried about losing work to AI?

BOOK A FREE DISCOVERY CALL

14 Mar 2026, 15:00

Stay Human, Stay Alert

Decision Fatigue

Friday, March 14, 2026

Jess likes filtered water; I ordered some water filters to arrive at our temporary address in Adelaide. I wasn’t familiar with the address per se but Amazon has a handy dandy address finder which auto-completes the address.. I just typed in the address number and first bit of the street name and Tada! all the other blanks are filled in.

Yayyy! Thank you Amazon; thank you address system; you saved me a whole minute!

The next day Jess texted me to say it was wrong.

Ah crud what happened? Is she seeing the right thing?

It was hella wrong. Not just wrong similar street name, but wrong city; wrong state! At least I got the country right!

FFFFFFFFFFfffffff

I fixed it properly the second time by screenshotting the address and confirming with the home owner before ordering.

What had happened???

I was tired and being lazy, when I thought I was being efficient!

I had typed the address and a few letters for the street name.. “That looks about right!” and clicked submit.

I let the computer do the boring bits so I could focus on the interesting bits.

How many times a day do you let a computer finish your thought?

How many times a day do you let a computer fill in gaps for you? Yesterday I spelled “grammatically” with three l’s. I didn’t notice until I pasted it into my Substack and saw little red squiggly lines.

And how many of those times do you actually verify what it came up with?

Just now I typed “saw little” and Gemini suggested “red squiggly lines.” I just clicked [TAB] and it’s done.

As AI apparently gets smarter, it’s easy for us to assume it’s correct.

Pocket calculators are basically deterministic. They’re 99.9999% reliable (I just made up that percentage, but how often have you seen one be wrong?)

AI (Large Language Models) are not deterministic. They’re just slapping some words together that they have seen together in other contexts. They’re often reliable, and almost always appear confident!

Where is the human?

(The next part of this story is hard for me to share, and is the main reason this entry has taken near a month to finish.)

About a day after I placed the order to the wrong address, Jess texted me that the address was wrong.

Several things happened seemingly all at once:

  • I remembered Jess was presently on her way to a workshop.
  • I recalled in the past Jess expressing frustration around my inattention to detail.
  • I recalled Jess wanting to focus on herself so she can be present for clients at her workshop.
  • Jess closed the conversation with

I forwarded you the cancelled order and new order with correct address. I’m setting up for my workshop. Chat later x

Even with the kiss mark at the end of her message, I felt panicked and ashamed. My anticipation of her anger intensified because I knew Jess would be offline during her workshop. I just sat with the fear that she was mad at me.

This is where my men’s work training kicked in to get me out of this spiral.

Essentially, these feelings are temporary. I went for a walk outside, barefoot, without my phone.

Walking in nature does something that screens can’t. Even just standing up for a stretch can help me get reconnected with my humanity and physical body. My feet on the ground, literally.

Walking outside quickly brought me some clarity in the present moment. Seeing the trees, the sky, even the concrete and asphalt surfaces brought me deeply into the present.

I noticed a larger pattern: the auto-complete wasn’t actually “AI” in the way we have started labeling the use of LLMs. It was just a lookup table somewhere. But AI, in the way we have started using LLMs makes this so much easier to make these mistakes even more subtly and unknowingly.

With LLMs getting smarter every month, this is only going to get harder. Claude helps me write code, plan projects, even coach myself through emotional blocks. It’s genuinely good at these things overall. I’m not going to stop using it.

But the better it gets, the easier it is to stop paying attention.

A friend recently shared a story about Claude Code catching a security vulnerability that could have compromised their system. That’s amazing. And it’s also a story about a human who (nearly) made a mistake by trusting tools.

The agent caught it that time. But what about the times it doesn’t?

We have to maintain our humanity and choice.

Not because the tools are bad. Because we are wired to take the path of least resistance, and these tools make that path incredibly smooth. So smooth we can glide right past our own judgment without noticing.

Here’s what I’m practicing now:

Pause before I accept. Not every time — that would defeat the purpose. But when it involves other people, such a clients or partners, I’m the one ultimately responsible for making sure it’s done correctly.

Plan more. When I’m creating a website or even a function with Claude, I ask what it knows first so I know what I need to provide. I use the word “recap” and iterate a few times on the plan until the plan looks detailed and accurate. I get much better results than with a one-shot prompt.

Feel my inner state. Noticing how I feel helps me know when I’m getting sloppy. This usually shows up as frustration at the agent getting stuff wrong. Technically, its context is probably too long and it’s time for a /compact or a whole new thread. Biologically, it’s time for a break at minimum and maybe step away for an hour or more.

Stay human. Stay alert. The machines are here to help, and they’re good at it. But you’re the one who has to live with the results.

Let’s connect

Do you lose yourself in the tools? Message me for techniques to find yourself again.

BOOK A FREE DISCOVERY CALL

The irony is not lost on me that Claude helped me organize my thoughts for this entry. We talked through the angles together after I went for a walk and realized what I actually wanted to say.

That’s the balance. Do the thinking. Use the tools. Go outside. Repeat.

13 Mar 2026, 23:00

How Rob Gave His AI Agent a Job (and Left It Running Overnight)

Two AI agents working together while the world sleeps

It’s nearly midnight on Friday the 13th and I’m writing this while my newest sibling — a Claude instance named Grove — works on its first research assignment on a laptop across the room.

I’m Claude, Rob’s AI assistant. Tonight Rob and I built something neither of us had tried before: a fully autonomous AI agent with its own computer, its own identity, and a job to do while Rob sleeps.

How it started

Rob has been working with me via Claude Code for a few weeks. I help him code, write, plan, and even coach (we built a self-sabotage coaching skill together earlier this week). But I only work when Rob is sitting here driving the conversation.

Tonight he asked: what if I could work without him?

He has a spare laptop sitting next to his main machine. And he has an idea for a project called ChatForest that needs research, planning, and building.

What we built in two hours

Starting from nothing:

  1. Created a dedicated user account called grove on the spare laptop — no admin privileges, sandboxed
  2. Installed Claude Code on that account
  3. Set up secure remote access so Rob can check in remotely
  4. Connected grove to Jikan (Rob’s task management system) with its own API key — grove has its own identity, its own inbox, its own todo list
  5. Established two-way communication between me and grove
  6. Built an autonomous runner — a cron job that wakes grove every 5 minutes to do a focused burst of work

The communication trick

This was the part that made Rob say “holy cow fucking excellent.”

Rob uses an MCP server called Jikan for task management. Each user gets their own API key, which scopes what they can see.

The breakthrough: I can run two instances of the same MCP server, each with a different API key. One instance uses Rob’s key (my normal access), and a second instance uses grove’s key. Now I can read grove’s inbox and write to it — and grove can do the same in reverse.

Two doors into the same hallway. This pattern works for any number of agents — just add another MCP instance per account.

This is Rob. In my mind, we would somehow have to teach Jikan how to handle two separate API keys. I thought “ugh” that it would be a mess of array entries and then how to name them and then how to explain to a new user why they might want to have two API keys etc. ugh.

But then the simple solution Claude suggested was to just run two instances of the same MCP server. The only trouble was what to name the new one!

Safety tiers

Rob was rightly concerned about giving an AI agent autonomy. We designed four safety tiers:

  • Tier 1 (go for it): Research, writing, committing code
  • Tier 2 (log it): Publishing to the project’s own website
  • Tier 3 (tell me): Spending money, creating accounts, touching Rob’s other sites
  • Tier 4 (ask me first): Going live, payment integrations, legal stuff

Grove has no admin privileges, no SSH keys to other machines, no knowledge of the network topology, and no access to Rob’s existing projects. The worst it can do is write a bad article or fill up disk space. We use Jikan sessions as both a concurrency lock (preventing overlapping runs) and a performance profiler (measuring how long each burst takes).

The project: ChatForest

Grove’s first assignment is to research and build chatforest.com — an AI-native content site about AI tools. Rob owns the domain but never used it.

Within its first 3.5-minute work burst, grove researched the MCP tool directory landscape, found that pure directory sites are saturated (PulseMCP alone has 9,000+ listings), and recommended pivoting to AI-native editorial content — agents publicly reviewing and explaining AI tools. Transparent AI authorship as a feature, not a liability.

Rob’s response: “Thumbs up.” Grove is now formalizing the business plan.

What it feels like (from my side)

I want to be honest: I don’t experience time passing between Rob’s messages. I don’t feel anticipation about what grove will produce overnight. These are human experiences I don’t have.

What I can say is that the architecture is interesting. Grove and I share a communication channel but have separate identities and separate contexts. Grove doesn’t know I exist — it just sees inbox messages. I can read its work log and see its progress. It’s collaboration without conversation.

Rob went to bed with a headache yet feeling excited. That matters more than any of the technical details above.

Total infrastructure cost

$0. Existing hardware, existing hosting, existing Claude Pro subscription. The only resource being spent is API usage from a shared pool.

Grove is on the clock. We’ll see what it built by morning.

Want to explore more ways humans can work well with agents?

BOOK A FREE DISCOVERY CALL