Project contents
← Automating Garage Dreams
Updates
Automating Garage Dreams
Every update on this project, newest first.
Updates are bullet-point summaries generated with AI from my commits and build notes, then checked by me. Project articles and learning notes are written by me.
-
MAX_REVIEW_REVISIONSdid nothing: the verdict function still compared against a literal 1. It now takes the budget as a parameter, with tests at 0, 1 and 2 rounds.- Qwen3.6 35B-A3B timed out after 241 s because thinking is on by default. Switched the size experiment to Gemma 4, and noted that its
<|think|>token isn’t stripped by the<think>filter. - Descriptions were cut off at the first “z”: JavaScript has no
\Zanchor, so with theiflag it matched a literal z. Fixed the regex, with a test fixture using car names that contain z. - Read the six drafts the reviewers split on. Codex was right every time, so the plan to loosen it was dropped, along with publishing on a single pass.
- Across three batches (62 runs) the publish count moved 2, 3, 1, which is noise. Lint failures by rule are the useful metric; description failures went from 7 to 0 after the parser fix.
- Gemma 4 26B-A4B published 20 of 20, but shared 41% of its five-word sequences with the source guide. The next step is a deterministic cap on verbatim overlap.
- See What the review gate taught me.
-
- Dry runs didn’t consume a section and didn’t write a run log, so twenty test runs measured one section and kept nothing. Dry runs now write to a gitignored harvest folder, and a driver walks the real selection order.
- First baseline over 22 runs: 2 published, 5 died before review. Claude passed 47% and Codex 18%, and neither ever rejected outright. Most objections were claims the source doesn’t support.
- Raised the minimum source section from 200 to 400 words. Eligible sections fell from 1,413 to 438, and the runway from about 3.9 years to 1.2.
- See The 400-word source floor.
-
- The NZ spelling rule
/colou?red/matched the correct “coloured” and told the revision loop to change it to itself. Split the patterns and added a test that every suggestion passes the linter, which found five more broken rules. - A run that published nothing left its log uncommitted, and the dirty-tree check would then have blocked every later run. Every outcome now writes and commits its log, except the dirty-tree abort itself.
- The revision prompt asked for the smallest possible edit, so a 718-word draft came back unchanged. The prompt now branches: too long means delete sentences or a section, too short means expand from the source only.
- The NZ spelling rule
-
- The plan said “split each guide on
##”. 71 of the 85 guides contained no##at all, and had looked perfect in a browser since the day they were converted: Word’s heading styles had been flattened to bold paragraphs, and four guides from the old WordPress site carried raw<h2>HTML that Markdown passes straight through. Fixed the corpus rather than teaching the pipeline to recognise bold-as-heading. - 4,100 bold pseudo-headings promoted on a four-condition heuristic (entirely bold, standalone, no terminal punctuation, 14 words or fewer), each condition tuned against counts from the actual corpus. The no-terminal-punctuation rule alone excluded 630 emphasised sentences. The script asserts the stripped-tag prose is byte-identical before and after, and caught nothing on the real run, which is the point.
- I had made the same mistake in the opposite direction an hour earlier: grepped for Markdown pipe tables, found none, and reported that the conversion had lost the spec tables. 75 of 85 files had them as raw HTML. Grepping source syntax tells you nothing about the rendered page.
- Adding a generated cover failed the build on missing alt text, which revealed that a fallback written minutes earlier, to synthesise alt text for exactly that case, could never run. The schema already forbade the state it existed to handle. Deleted it: a fallback that cannot fire is worse than none, because it reads as a guarantee.
- Two of the 85 “guides” were not guides. One was a classified ad for a Volvo 940 that came with two rims and some seat material. Everything in the source folder had been converted indiscriminately.
- Found a guide titled as an EP3 Civic Type R whose body is entirely about the EK9, down to the part numbers. Flagged, not fixed, but it matters, because the pipeline derives articles from these, so a wrong guide becomes wrong articles. Nobody has ever proofread the 1.01 million words.
- The plan said “split each guide on
-
- Threw the first version away. The single-model scaffold became
legacy/, and the rebuild starts from a working site with a two-tier content model: long buyer’s guides written by two frontier CLIs that review each other, and short nightly articles drafted by a local model and reviewed by both. Subscription auth throughout rather than per-token billing. - The GitHub repo I was about to push to already held the old site’s history, with no shared commit to merge against. Renamed it to
garagedreams-legacyrather than force-pushing over it. The local.githad been lost once already and that is not a thing to do twice. - The logo was in the DOM and invisible. The SVG carries live
<text>in a font the browser never loads, because an SVG in an<img>renders in an isolated context with no access to the page’s fonts. Inlined it and loaded the face; the real fix is re-exporting with the text outlined. - Inlining both logos then mixed their colours: white letters in the orange wordmark, an orange R in the white one. SVG
<style>blocks are not scoped, and both files had been exported with the same generic.cls-1,.cls-2class names. Namespaced each at inline time.
- Threw the first version away. The single-model scaffold became
-
- Wrote the remaining-implementation plan for a less capable model to execute: complete code for every file, exact commands, nothing inferred. It ran steps 1 to 3 and the code matched the plan, but it left the generation script uncommitted, so the work existed only on disk.
- The generation script hardcoded Ollama’s port. Swapped both the URL and the model name to environment variables, because the development machine runs llama.cpp on 8899 and the Pi will run something else.
localhostin that variable resolves to the dev container, not the host serving the model.host.docker.internalis the fix, and it is the kind of thing that reads as “the model is down”..devcontainer/devcontainer.jsonwas listed as created in the previous session’s notes and did not exist. Described, never written. The first instance of a note recording an intention as a fact.
-
- First conversion run over the 101 guides: 84 converted, 16 skipped, 1 error. The skips are folders holding more than one
.docxand therefore colliding on slug, and the first file wins. The error isaccord-euro-r.docx, whose zip structure is broken and which no reader can recover; that guide is simply absent. - Image extraction produced nothing, and it took a re-run to work out why: the photos sit beside the
.docxin each folder rather than embedded inside it, so the converter’s image handler had nothing to find. - Decided the guides carry their real press photos while generated articles get a placeholder. The text is original either way; the split is honest about which pages had a human behind them.
astro devwithout--hostbinds only to127.0.0.1inside the dev container, which the editor’s port forwarding cannot reliably reach. Minutes lost to a page that looked like it was building slowly.
- First conversion run over the 101 guides: 84 converted, 16 skipped, 1 error. The skips are folders holding more than one
-
- Surveyed the source material: 101
.docxbuyer’s guides, each in its own folder with photos beside it. Section headings are bold paragraphs rather than Word heading styles, and the spec data is in real Word table elements, so it should survive conversion as markdown tables. - Decided the generator writes from one section rather than a whole guide: roughly 1,500 distinct chunks across the corpus, picked one a night. The guides themselves get published too, so the source is also the archive.
- The brief’s cron line was
node scripts/generate-article.js, which cannot run a TypeScript file. Corrected totsxbefore it reached a Pi.
- Surveyed the source material: 101