The grade-school assignment was always the same: write about what you did over the summer. Mine this year was a cruise up the Alaskan Inside Passage, and like every vacation, it generated more code than rest. The boat needed tracking, the long sea days needed reading material, and the reading material needed to fit on an e-reader. Each of those turned into a project.

This is the lighter companion to the other half of my summer, the A2A stack build log, which covers the agent work. This post is about the boat, the books, and a few odds and ends.

boatymcboatface: tracking the Grand Princess

The premise: family back home wanted to follow the ship, and passengers on board wanted a map that did not eat the satellite Wi-Fi allowance. boatymcboatface (private) does both. It tracks the Grand Princess (MMSI 310327000) live, at grand-princess-alaska.web.app.

The architecture splits in two. A Go backend on Cloud Run holds exactly one WebSocket to the global satellite AIS network (aisstream.io), caches the latest position in memory under a mutex, and logs a permanent waypoint to Firestore once every ten minutes. That cadence is deliberate: 144 writes a day stays inside the free tier, and the container rehydrates its cache from the last known waypoint on cold start, so the ship never jumps on a page load.

The frontend is a Lit web-component SPA with Leaflet inside the shadow DOM. It adapts to where you are. On board, it queries the ship's local gateway; online, it polls the Cloud Run /position endpoint every three minutes, which cut satellite bandwidth by over ninety percent against a live WebSocket.

Two fixes landed during and after the voyage. The first improved location accuracy with trail backfill, dead reckoning between fixes, and onboard detection. The second made the AISSTREAM_API_KEY optional, so the site keeps serving the recorded track as a read-only map now that the cruise is over.

pdf2epub: reading arXiv on a Kobo, and a budget that crept

Long sea days are good for catching up on papers, and a Kobo is a better reading device than a laptop on a moving ship. The problem is that academic PDFs are multi-column and math-heavy, and naive converters scramble them. pdf2epub rebuilds the document structure with multimodal Gemini on Vertex AI, transcribes equations into LaTeX, compiles them to native MathML through Pandoc, and packages the result as a Kobo-optimized KEPUB.

I budgeted this as a cheap weekend utility. It started under two dollars in API spend. By the time I had it producing clean output on real papers, it was closer to thirteen. The overage came from iterating on the transcription prompts and re-running full documents to check the math, which is the kind of cost that hides in a per-page pipeline until you total it.

Two pieces of the work paid for themselves. I wrote a concurrent Go port of the Python CLI to run page transcription in parallel, which made the re-runs bearable. And I added Kobo diagnostics that read the device's on-disk SQLite database to confirm a book actually registered. The first version broke on a firmware update because the schema column had moved; the fix selects the right column across firmware versions instead of assuming one. There is also local Gemma 4 support for the offline path.

magnifica-epub: an encyclical as a digital book

Off the boat, a different conversion problem. Pope Leo XIV's encyclical Magnifica Humanitas, an address on safeguarding the human person in the age of artificial intelligence, was published as server-side-rendered web pages. I wanted it as a proper e-book. magnifica-epub is a Go pipeline that parses the SSR pages, strips menus and navigation, segments the text into per-chapter spine items, and compiles a compliant EPUB 3 and KEPUB.

The interesting work was fidelity. Footnotes reconstruct as bidirectional EPUB 3 pop-overs, so a reader can open a note in an overlay without losing their place. The build assembles a vector SVG cover, embeds the Vatican insignia on the title page, and applies a typographic stylesheet with Vatican crimson and warm gold accents, classical serif faces, drop caps, and Maltese cross section dividers. An evaluation pass compares the output against a generic CLI converter to confirm the structure held.

The pipeline is written for this one document, but the bones generalize to any structured HTML or Markdown source. It is the sibling utility to pdf2epub: one starts from PDF, the other from HTML, and both aim at the same clean EPUB target.

Elvish odds and ends: linguistics, the primer, and the site

The vacation also gave me time to push on my Tolkien-language work, which feeds the Mithlond ecosystem.

eldamo-linguistics (private) does computational linguistics quantitative analysis of Tolkien's constructed languages, with the downstream goal of exploring creating Gemma 4 fine-tuning data and post-training recipes. This round promoted the phonetic and phonological scratch scripts into a formal eldamo_linguistics library with a pytest suite, fixed XML traversal and parent resolution in the extraction pipeline, and added a rigor pass: a stress oracle, primer-drift validation, a methodology-limits document, and a research journal. The point of the rigor work is to keep the linguistic claims honest before they shape any training set.

The Linguistic Primer is the public face of that analysis, adding on to the interactive phonology and other pages in the Mithlond web app. It now carries an interactive IPA phoneme chart, a sound-law explainer that animates lenition, spirantization, and Germanic umlaut on real Elvish examples, and Needleman-Wunsch alignment grids for comparing word forms, all under a dark theme with a unified sidebar across the site.

And this site itself, ghc.wtf, got attention. The fieldnotes rich editor gained a dockable, sliding layout with swappable metadata and AI-assistant panes, a combined Ruth Starkman and stop slo scoring tool that helps me catch my own AI writing tics got more robust matching, and the Go backend went through a code-health pass. The editorial skill I used to revise the A2A post came out of that same stop-slop line of work.

riptide: the one I updated out of pure curiosity

Not every project came from a problem the trip handed me. Gemini 3.5 Flash shipped with a built-in computer-use tool while I was away, and I wanted to understand what an agent harness actually has to do once the model can see a screen and ask to click on it. So I built one (ok, updated one I had started previously). riptide is a Go harness that connects Gemini 3.5 Flash's computer use to a real headless Chrome through chromedp, running an Observe-Reason-Act loop over the screenshot and accessibility tree.

Two pieces of that work got their own writeups. Migrating to Gemini 3.5 Flash covers the upgrade that broke every action on turn one and the strategy that fixed it. Sandboxing browser agents covers the line in every Go browser-agent codebase that disables Chrome's sandbox, and four ways to put the isolation back.

The thread

A vacation is supposed to be a break, and this one was, mostly. The pattern I notice looking back is that most of the projects solved a problem the trip handed me in real time: the family wanted to see the boat, I wanted to read papers on the boat, and the rest filled the quiet hours. Riptide was the exception, the one I chased because a new model capability landed and I could not leave it alone. The heavier agent work in the companion post was the deliberate project. The summer asked for the others.

Oh, and I did a blasé job of "defending" AI to the Z's, who are haters.