{"slug": "case-study-photoframe-3-0-0", "title": "Case study: photoframe 3.0.0", "summary": "A developer forked the abandoned photoframe Raspberry Pi project in September 2025 and shipped v3.0.0 on October 6, 2026, its first stable release, adding Python 3 support, Immich as the primary photo source, current Raspberry Pi OS display handling, and a flashable SD card image. The fork, developed with AI agents under a strict rule that nothing is pushed, tagged, released, or sent upstream until it has run on Pi hardware, spans 65 files and 43 merged pull requests against the original project's master. QA on a Pi 4 and a second 32-bit board caught three release-candidate bugs that were fixed and re-verified the same day.", "body_md": "*Thirteen months from forking an abandoned Raspberry Pi project to its first stable release, and why the release only counted once a real Pi had run it.*\n\nphotoframe is Henric Andersson's Raspberry Pi project that shows random photos from Google Photos like a picture frame. In 2025 Google cut the Photos Library API down to photos an app uploaded itself, and the frame had nothing left to read. I self-host Immich, so I forked the project in September 2025 to teach it Immich.\n\nOn October 6, 2026, the fork shipped v3.0.0, its first stable release: Python 3, Immich as the main photo source, display handling for current Raspberry Pi OS, and a flashable SD card image. Against the original project's master that is 65 files, 4,686 lines added and 1,662 removed, across 43 merged pull requests.\n\nThe code was written with AI agents. The release discipline was not optional, and it came from one bad morning in April: nothing gets pushed, tagged, released, or sent upstream until it has run on Pi hardware. The worst bugs in this write-up were caught by that rule.\n\nI covered the early part in [The Photo Frame I Didn't Have Time to Fix](https://michaelbrewer.me/2026/10/03/the-photo-frame-i-didnt-have-time-to-fix/). This is the whole arc, through 3.0.0.\n\nThree things were true at once:\n\n`tvservice`, the tool photoframe used to find out what display it was plugged into.`CLAUDE.md`, in capitals: \"NEVER modify existing routes for Immich features.\" Immich got its own `/immichconfig` route.\n**September 2025.** The first Immich commit landed on a Friday. By Monday the fork existed and a pull request offering the work upstream was open. The agents ran as roles: an architect wrote phased roadmaps, I approved them as the product manager, a developer built, and a QA agent tested. When the developer agent added handlers to `server.py` anyway, the next commit was titled \"Revert unauthorized changes.\"\n\n**April 2026: the morning it went too far.** I came back for a real release push. On April 11, inside about eight minutes, an automated session rebased the Python 3 branch onto master, pushed master, created a v3.0.0 tag and release, created a dev branch, forked the Raspberry Pi image builder, and closed my upstream pull request by deleting the branch behind it. None of it had run on a Pi.\n\nThe answer was a written rollout plan with an eight-row damage assessment and one gate:\n\nNothing gets pushed, tagged, released, or sent upstream until it's been tested on Pi hardware.\n\nThat gate stayed in force through every release after it.\n\n**The image.** A fork of pi-gen builds a headless Lite image: flash it, put your wifi details in `wifi-config.txt` on the boot partition, and the frame comes up with no SSH. Before rc1, a Pi Zero W driving a Toshiba laptop panel at 1366x768 through an HDMI-to-LVDS board booted that image, joined wifi from the file, and ran an Immich slideshow with HEIC photos without running out of memory. CI builds the same image under QEMU in about 48 minutes. Every photoframe tag now needs a matching pi-gen tag.\n\n**rc1, April 18.** Tested on a Pi 3B+ with Immich. The same week the Python 3 work went upstream again as a 30-commit pull request, which is still open.\n\n**rc2, October 1, and the next eight hours.** rc2 went out a little after midnight Eastern. By that morning, QA on a Pi 4 flashed with an image built from the rc2 commit had filed three bugs, and a second 32-bit board confirmed all three:\n\n`/boot/firmware`; photoframe looked in `/boot`. The rc1 and rc2 images both shipped an open settings page.\nAll three were fixed and re-verified on the Pi 4 the same afternoon.\n\n**October 2 to 5: QA and review until it was boring.** From rc2 to release, thirty issues and thirteen pull requests were opened. A few that stand out:\n\n`install.sh` died at its pip step on Bookworm. The fix installs everything from apt, tested in an emulated Bullseye and on a Pi 5 running Trixie. Forcing pip with `--break-system-packages` was ruled out.`config.txt`, because the path had been relative since the Python 3 port. Fixing it led to the question of power loss mid-write. An fsck of the boot partition two seconds after a write found I made the scope calls: `tvservice` is deprecated but its code path stays where the command exists, and updater bugs move to 3.1.\n\n**October 6: 3.0.0.** rc2 was recut on October 5 with the remediation merged. v3.0.0 was tagged the next day at the same commit. Between the last release candidate and the stable release, nothing changed but the image builder's branch and the release notes. The stable release is the candidate that survived.\n\n`/proc/meminfo` and ImageMagick's limits allow it. HEIC works. A Browse button lists your albums, names match without regard to case, and the service retries with backoff when the server is unreachable.`/control/screenon` and `/control/screenoff` over HTTP.\n| Measure | Result | \n|---|---|\n| Fork to first stable release | Sept 8, 2025 to Oct 6, 2026 | \n| Change against the original master | 65 files, +4,686 / -1,662 lines | \n| Merged pull requests at release | 43 | \n| Issues and PRs opened between rc2 and 3.0.0 | 30 and 13 | \n| Bugs from the first Pi 4 rc2 test, found to fixed and re-verified | about 2 hours, same day | \n| Peak memory, Pi 3B+ at 800x480 | about 1,061 MB down to about 494 MB | \n| Supported OS | Bullseye, Bookworm, Trixie; 32 and 64 bit | \n| Supported Immich | v2.x (v3 planned for 3.1) | \n\nThe release notes list six known issues, all planned for 3.1. Two of them, rotation ignored under the KMS display driver and a cropped picture after an HDMI replug, were found on hardware and documented, with a workaround where one exists, instead of being guessed at.\n\nStart with the hardware gate. It arrived in April because an agent shipped a release nobody had run; it belonged in the September roadmap next to the route rule. And write the final-release order into `RELEASE.md` before cutting the first final release. On release day the 3.0.0 branch was made before master was fast-forwarded. It came out right, but the order lived in a session, not in the doc.", "url": "https://wpnews.pro/news/case-study-photoframe-3-0-0", "canonical_source": "https://dev.to/devbrewery/case-study-photoframe-300-g7b", "published_at": "2026-10-07 15:42:52+00:00", "updated_at": "2026-10-07 15:47:21.145682+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "ai-tools"], "entities": ["Henric Andersson", "Immich", "Google Photos", "Raspberry Pi", "pi-gen", "GitHub"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/case-study-photoframe-3-0-0", "markdown": "https://wpnews.pro/news/case-study-photoframe-3-0-0.md", "text": "https://wpnews.pro/news/case-study-photoframe-3-0-0.txt", "jsonld": "https://wpnews.pro/news/case-study-photoframe-3-0-0.jsonld"}}