When I reached step 6 (Scheduling) of bluesky-pipeline-tutorial-v4.md, I chose Option A.  Instead of keeping a Terminal or iTerm window open, or using pm2,  I decided to use a Docker container running in OrbStack.  But instead of prompting Claude to generate a tutorial and prompting it to regenerate when I hit roadblocks, I started a prompt and gave it feedback as I progressed.  Here’s the initial prompt:

I want to execute a binary from an npm package as a long-lived process in an OrbStack container.  As a software engineering instructor, guide me through that process step-by-step, prompting me after each step with “Let me know when you’ve finished this step—or if you need help troubleshooting—and I’ll share the next step” until I let you know that my binary is running successfully.

This approach is a deliberate “human-in-the-loop” approach versus the loop engineering you may have read about or may already be executing with AI agents.  But I’m trying to knock off the rust of not having shipped production code since the pandemic—not build a software engineering team of agents (not yet anyway).

Claude started by giving me Step 1: Confirm OrbStack is running and its Docker CLI is active.  Once I finished following those instructions and told Claude I was done, it generated Step 2: Pick the npm package/binary and decide how it’ll live in the image.  But instead of walking through those individual steps, I prompted Claude by attaching bluesky-pipeline-tutorial-v4.md with the following prompt:

I’m trying to run some custom code I’ve written as a long-lived process, specifically the schedule.ts file described in step 6 of the attached file.

In response, Claude generated Step 3: Write the Dockerfile.  The step included a .dockerignore to add so the build context would stay clean.  Once I finished this step, Claude generated Step 4: Build the image.  With that step completed, Claude generated Step 5: Run it as a long-lived, persistent container.  In addition to the docker run command, the step included a bulleted list which explained each flag in the command.  The flag walkthrough pointed out that the openDb() call in my runPipeline function defaulted to a location that would cause the database to be overwritten the next time I rebuilt the container.  I prompted Claude with the following to confirm I had the right idea regarding the change I needed to make:

Do I need to change line 5 in run.ts to set dbPath = “/app/data/bluesky.db” and rebuild the container?

Claude’s response confirmed I was correct, included the full line change to implement, and explained the steps to rebuild the container, stop and remove the existing one, and start the new one.

After I completed Step 5, Claude generated Step 6: Verify it’s actually working, not just “running”.  Step 6 included a lot of diagnostic steps I wouldn’t have thought of, though Claude’s suggested code for querying the database from the command line didn’t work after a couple attempts.  Claude then suggested adding a script to the container solely for counting rows in the database’s ‘posts’ table and rebuilding it.  That check worked.  When I prompted Claude with the actual count (3333), it generated a couple of checks to run to see how long the container had been running and to check the row count in the database against my Bluesky post count.  After I prompted Claude that the count was valid (my profile had 10.8K posts at the time I ran the container), it responded with a summary of the work we’d done and one last check for the container restart count.

A slightly different experience than prompting for a tutorial and asking for an updated one that addressed issues and roadblocks I encountered with the previous version, but the same end results: working code, and a slightly better understanding of the language and tools I used to build it.