A self-hosted home library catalog with barcode scanning, multi-mode scanning workflows, automatic metadata lookup, cover art, and collection management — all in a single Docker container.
Photo Intake: snap a shelf, AI reads the spines, books land in your library with covers and metadata.
Most home library apps are cloud-hosted, mobile-only, or require you to manually enter every book. Shelf takes a different approach:
- Scan and done — point your phone camera at a barcode or use a USB/Bluetooth barcode scanner and the book is cataloged in seconds, complete with cover art, author, series info, and description. Works out of the box with any scanner that sends Enter after the barcode (most do by default), and camera scanning works on iPhones and iPads as well as Android
- Bulk-add from a photo — snap a picture of a full shelf, a stack, or books laid face-up, and a vision model reads the spines and recognizes the covers. Review the candidate list — each row carries the ISBN read off a back cover, a per-row media type, and a marker on rows the model recognized rather than read — then import them all. Set a row to DVD or Video Game and it is looked up on TMDb or IGDB at confirm, not merely filed, on an exact title match. Works with the Anthropic API, any OpenAI-compatible endpoint, or a fully local Ollama model
- The barcode decides the media type — an ISBN is a book even if the dropdown still says DVD, and a game UPC reaches IGDB even if it says Book. Leave it on Auto and scan a mixed pile; the card says what it detected and why, and says plainly when a record came back thin — because a provider key is missing, was rejected, is rate-limiting you right now, has no source for that format yet, or simply had no match
- 8 scan modes — Add, Wishlist, Lend, Return, Move, Inventory, Lookup, and Quick Rate. The scan tab adapts to whatever you're doing: adding new items, lending to a friend, reorganizing shelves, or auditing a room
- Title search — don't have a barcode? Search by title across Open Library (books), TMDb (movies), and IGDB (video games) and add directly from results. Like the scan card, the result box says why it is empty — a rejected key, a rate-limited provider, one that could not be reached — so "No books found" means only that the provider answered and had nothing
- Add by hand — a title is the only field required, for anything no lookup can find: a self-published book, a burned CD, a niche import, something you made yourself. Every media type, no barcode needed, and reachable from Home, from an empty title search, from a mistyped barcode, and from any item's page
- Zero cloud dependency — runs entirely on your network in a single Docker container with a SQLite database. Your data never leaves your home
- Works on any device — responsive web UI that works on phones, tablets, and desktops. No app store required
- Multi-user — share with your household. Admins manage the catalog, viewers can browse and track what they're reading
- More than books — catalog audiobooks, eBooks, DVDs, CDs, comics, manga, and video games. Link physical and digital formats together
- Video game support — scan UPC barcodes for modern games or search IGDB by title for retro cartridges (Atari 2600, NES, SNES, etc.). Cover art, publisher, series, and platform tracking with a customizable platform list
- Lend with confidence — track who borrowed what with the Lend/Return scan modes and a "Lent Out" filter on the browse page
- Inventory auditing — pick a location, scan everything on the shelf, then see what's missing
- Delete without fear — deleting an item or removing a copy moves it to Trash with its tags, loans, copies and history intact. Editors restore in one click; admins empty it, prompted once rows pass a retention window (180 days by default). Nothing is deleted on a timer
- Know what you own — ISBNdb integration estimates your collection's value and generates a location-grouped, print-ready valuation report for insurance documentation
| Browse | Scan (Add Mode) |
|---|---|
![]() |
![]() |
| Scan (Lend Mode) | Item Detail |
|---|---|
![]() |
![]() |
| Stats | Admin Logs |
|---|---|
![]() |
![]() |
| Valuation Report | Browse (Tag Filter) |
|---|---|
![]() |
![]() |
| Photo Intake | Series |
|---|---|
![]() |
![]() |
| Wrap-up |
|---|
![]() |
Full docs live in docs/:
- Installation · Configuration · HTTPS & reverse proxy · Upgrading & backups
- User guide: Getting started · Scanning · Photo Intake · Browse · Items · Locations · Series · Lending · Wishlist & Store Mode · Sharing · Stats & valuation · Import & export · Integrations · Users & roles
- FAQ · Troubleshooting · Development · Architecture · Roadmap
docker compose up -dOpen https://localhost:18888 and create your admin account and pick a feature profile via the setup wizard. That's it.
Create a .env file alongside docker-compose.yml for host-specific config:
# Add your machine's IP so you can access Shelf from other devices
CERT_SAN=IP:192.168.1.100,DNS:shelf,DNS:localhost| Variable | Default | Description |
|---|---|---|
CERT_SAN |
DNS:shelf,DNS:localhost |
TLS certificate Subject Alternative Names |
SECRET_KEY |
(auto-generated) | JWT signing key. If unset, generated at data/signing.key (0600) on first start; an existing key from before 0.30 is moved there from the database on the first start after upgrading, so sessions survive. Set it explicitly to run several instances against one database |
SHELF_ENCRYPTION_KEY |
(auto-generated) | Encryption key for stored API credentials. Auto-generated at data/encryption.key if not set — never stored in the DB, so backups contain ciphertext only. Set it (e.g. openssl rand -hex 32) so the data directory alone can't decrypt credentials |
All persistent data lives in ./data/ (bind-mounted into the container):
data/
shelf.db — SQLite database
covers/ — cached cover images
certs/ — auto-generated TLS certificates
encryption.key — key for credentials stored in the DB (unless
SHELF_ENCRYPTION_KEY is set)
signing.key — signs login sessions (unless SECRET_KEY is set)
Keep both key files out of shared copies. The database holds no key material, so a database backup is ciphertext without anything that opens it — but a copy of this whole directory carries the keys next to the data they protect.
- Camera barcode scanning on mobile — tap to scan ISBNs and UPCs, from the Scan tab or from an item's edit form when you need to correct one ISBN
- 8 scan modes — Add, Wishlist, Lend, Return, Move, Inventory, Lookup, and Quick Rate
- Media-type detection — the barcode outranks the scan form's dropdown when it is certain; Auto reads the barcode and decides. It reads platform, format, medium and audio wording out of the retail title, and the product category behind it, so a music CD and a PC CD-ROM game are each filed as themselves rather than guessed at against a film database
- Photo intake — bulk-add from a photo of your shelves using a vision model, snapped with the phone or webcam or uploaded. Rows typed DVD or Video Game are looked up on TMDb or IGDB at confirm, on an exact title match (see Photo Intake)
- Title search — search Open Library, TMDb, or IGDB by title when you don't have a barcode; an empty result box names the reason when the search failed rather than missed
- Add by hand — a title is the only field required, for anything no lookup can find: a self-published book, a burned CD, a niche import, something you made yourself. Every media type, no barcode needed, and reachable from Home, from an empty title search, from a mistyped barcode, and from any item's page
- Cascading metadata lookup — Open Library, Hardcover, and Google Books, with national bibliographies consulted first for the groups they cover: German (978-3) ISBNs go to the Deutsche Nationalbibliothek (DNB), Italian ones (978-88 and 979-12) to the Servizio Bibliotecario Nazionale (SBN)
- Edition language — captured on lookup, editable on items, filterable in Browse; a settings dropdown picks the preferred language for title searches
- Cover art pipeline — Open Library, Hardcover, DNB (German ISBNs), Amazon, Google Books, IGDB, and manual search/upload/paste-a-URL/remove, on any item. A cover review queue walks every cover-less item one at a time — discs, games and music included, which the automatic sweep deliberately never touches — with the picker inline, and a Not available verdict that is remembered so the list converges on zero. Cover search is media-type aware: books search Google Books and Open Library, DVDs the film's TMDb poster set, video games IGDB cover art and artwork. The picker reports a missing key, a rejected key, a spent quota and an unreachable provider by name, so "No covers found for this title." is only ever a genuine miss
- UPC support — scan DVDs and Blu-rays with TMDb lookup, and music discs, which are detected on Auto. A barcode Shelf can resolve on MusicBrainz brings back the release; one it cannot is still filed under its own title
- Music by release, not by title — a Music page searches MusicBrainz by title, artist, barcode or catalogue number and catalogues the exact pressing: country, date, label, catalogue number, packaging and medium, with real track lists across multiple discs. Vinyl, Cassette, CD and Digital Music. Two pressings of one album stay distinct and are linked to each other automatically. Artwork is shown square and uncropped. See Music
- Periodicals as publication plus issue — a magazine run is one publication with many issues, not many unrelated rows. A 977 barcode resolves the publication from its ISSN; the issue number and date stay yours to confirm. When the barcode does not resolve, search for the magazine by title instead. See Periodicals
- Manga as its own media type — in the book family, so it carries an ISBN, belongs to a series, and filters separately from comics
- Retail barcodes on the edit form — the Identifiers section takes a UPC/EAN as well as an ISBN, with the same camera scanner. UPC-A is canonicalised to EAN-13 and checksummed; 978/979 Bookland codes are refused, because they belong in the ISBN field. See Editing barcodes
- Video game support — scan UPC barcodes for modern games or search IGDB by title for retro cartridges. Platform tracking with a customizable platform list (30+ platforms from Atari 2600 to PS5)
| Mode | What it does |
|---|---|
| Add | Scan barcodes to add items to your collection with full metadata lookup. Scanning something already on your wishlist marks it owned |
| Wishlist | Scan at a bookstore to save items you want — adds them to your wishlist |
| Lend | Select a borrower, then scan items to check them out |
| Return | Scan items to check them back in |
| Move | Select a target location, then batch-scan items to relocate them |
| Inventory | Select a location, scan everything there, then check for missing items. Counts physical copies, so a book with copies in two rooms is expected in both; a scan that cannot say which copy reports instead of moving one |
| Lookup | Scan to check if an item is in your collection — no changes made |
| Quick Rate | Scan to mark items read, watched or played |
Camera scanning picks its decoder to suit the device: iOS Safari drives the camera with ZXing, every other platform uses html5-qrcode. The scan page and Store Mode share one engine, so both behave the same everywhere. Retail barcodes are covered — EAN-13, EAN-8, UPC-A and UPC-E. USB and Bluetooth scanners bypass the camera entirely and work regardless.
Snap a photo of a shelf — or of books stacked or laid face-up — and Shelf reads the spines it can read and recognizes the covers it can't. Open Photo Intake in the nav, take or upload the photo, and the detected books appear as an editable candidate list: title, author, the ISBN read off a back cover if one was in frame, a per-row media type, and a marker on rows identified from the cover rather than read. Nothing is imported until you confirm. Rows with an ISBN then get the same lookup a barcode scan does; books are matched on title and author behind an author-match guard; and a row set to DVD or Video Game is looked up on TMDb or IGDB, which fills in its year, description and cover when the title matches the catalogue exactly. The Done panel shows which rows found no metadata, and marks separately the rows whose lookup was declined because the title did not match.
Configure a vision backend under Settings → Integrations → Photo Intake:
- Anthropic API — best accuracy; pay-per-photo (typically a few cents)
- OpenAI-compatible — any endpoint that speaks the OpenAI Chat Completions API: OpenAI itself, OpenRouter, or a local server (vLLM, LM Studio, LocalAI). Set the base URL, an optional API key, and a vision-capable model
- Ollama — free and fully local with any vision-capable model (gemma3, qwen2.5vl, llama3.2-vision, …); accuracy depends on the model
For high-resolution photos that exceed what the model actually ingests, Shelf shows a preview of what the model will see and offers to split the photo into overlapping tiles for better accuracy — with a cost estimate for each option before anything is sent.
- Choose your features — the setup wizard starts you on Minimal, Standard or Everything, and Settings → Features turns any optional part (Lending, Sharing, Valuation, Price alerts, Photo Intake, the integrations, …) on or off later, one at a time or by applying a profile. A feature that is off leaves the pages you use, and its data is kept for when it comes back. See Configuration → Features
- Home overview — Shelf opens on a page that answers "what is happening in my library?": catalogue, owned and wishlist totals, what is lent out, missing covers, a media-type breakdown and recent additions. Browse stays the place for searching, filtering and bulk editing. See Home
- Filter and search — by media type, location, status (read, watched, played), ownership, lending status, source (which sync, provider or import an item arrived from), and free text
- Reading tracking — want-to-read, reading, and read with start/finish dates
- Custom tags — free-form tags (
signed,first-edition, whatever you like) as chips on the item page and its edit page, applied to many items at once from the Browse bulk bar, with a tag filter and an optional Tags column on Browse. Admins rename, scope and delete tags in Settings, which shows how many items carry each. Set default tags for a scanning session on Scan, Shelf Fill or Photo Intake and every item you add is tagged as it arrives, with suggestions for the media type you are scanning - Synopses — item descriptions fetched automatically on add, plus a one-click backfill for your existing catalog (Open Library, Google Books, Hardcover)
- Stats dashboard — books read per year, collection growth, top authors, and value-over-time charts (server-rendered SVG, no JS)
- Wrap-ups — a shareable image of a month or a year: what you finished, what you added, your top author and a grid of covers. Drawn in your browser and never stored or published; download it, or share it from your phone. In January, Stats also links to last year's wrap-up ("2025 in Books"). See Stats → Wrap-ups
- Locations — organize by room, shelf, or any system you like, and nest them: a shelf inside a bookcase inside a room. Rename or move a location and everything beneath it follows. See Locations
- Shelf Fill — pick a room, bookcase or shelf and it stays selected while you scan item after item onto it. Items already catalogued move without a fresh metadata lookup; unrecognised barcodes fall through to the normal Add pipeline. See Shelf Fill
- Physical copies — own two of something and track them apart: add a copy on the item page, give each its own location, condition, acquired date, source, price, provenance and barcode, and remove one when it goes. Removing the copy marked primary promotes the next one and the item's location follows it. A removed copy goes to Trash with its details, not away for good
- Trash — deleted items and removed copies wait in Trash (account menu → Library) with everything attached to them: restore brings them back exactly as they were, and scanning one in Lend, Move or any other existing-item mode says In Trash and offers Restore instead of acting on it. Admins delete permanently or empty what has passed the retention window, and get a dismissable banner when there is something to empty. See Items → Trash
- Arrange a shelf — any location gets an Arrange page where you drag the physical copies into the order they actually sit in, or order them automatically by title, creator, series, release or issue. The order belongs to the copy, so duplicates stay distinct and can sit side by side
- Related media groups — connect the different forms of one work (a novel, its audiobook, its film adaptation) as
format,relatedoradaptation. Every item shows its whole group in a panel on the item page, with direct links marked apart from the ones reached through a third item; editors search the catalogue to add a relationship, viewers see the group read-only. A group is the connected set of links, so linking A to B and B to C presents all three. Matching is manual by design. See Related Media - Game platforms — customizable list of platforms, add your own for niche or retro systems
- Authors as links — every author on an item page links to everything by that person:
Martin Fowler, Kent Beckis two authors, a translator is shown as one (Ken Liu · translator), andJ.R.R.andJ. R. R. Tolkienare the same author. Browse filters to one author and combines it with any other filter. The authors field stays exactly as stored — fix a mis-split by editing it. See Items - Checkout system — lend to borrowers with the Lend scan mode, filter by "Lent Out" in browse
- Loan reminders — overdue loans get a red badge, and an optional daily digest (ntfy or webhook) nags you about them; configure under Settings → Library → Lending
- Wishlist — a list of what you want, kept alongside your catalog. Owning and wishing are separate: an item can also be neither — a book you read from the library stays in your catalog, with its reading history, without being owned or wished for
- Series tracking — a Series page groups your library by series with position numbers, flags likely gaps, and (with Hardcover configured) checks the full series and adds missing volumes to your wishlist in one click. Each series can carry its own synopsis, written inline or fetched from Hardcover. Rename a series (renaming onto an existing name merges the two — the quick fix for duplicate series records left by metadata lookup) or disband it entirely, right from the series card
- Bulk editing — select multiple items in Browse to move them, change type or status, add them to or remove them from the wishlist, set and clear their series, or add and remove a tag in one go
- Choose your columns — Browse's list view has a column picker (value, series, publisher, year, pages, language, added date, platform, ISBN/UPC, and more), on top of the author/type/location/status shown by default; the choice is remembered per browser, not per account
- Valuation report — location-grouped, print-ready report of your collection's list-price value for insurance documentation (print view); prices via ISBNdb
- Price alerts — Shelf checks the list price of wishlisted books nightly through ISBNdb and sends a digest when one drops past your threshold; list price, not used-market price. See Price alerts
- Display currency — pick from 20 currencies under Settings → Collection and every value surface follows. This is formatting, not conversion: Shelf never converts amounts between currencies, so the figure ISBNdb returns is the figure shown
- CSV import/export — bulk operations and backups
- Portable archive — export your whole collection as a single zip (items, tags, locations, series, reading log, checkouts, physical copies, and your cover art) and merge it back into any Shelf instance without refetching a single cover. No credentials or instance-specific data are included, so it's the safe way to move servers or hand your library to someone else — unlike a database backup, which carries password hashes and encrypted API keys but no covers at all. Importing previews first: you see how many items are new, how many are already yours, how each duplicate was matched (exactly on ISBN, or heuristically on title and author), and you can leave parts of the archive out before anything is written. Trash travels too: what you deleted arrives in Trash on the other side, with its deletion date, and an import never moves a live item to Trash
- Goodreads, StoryGraph, LibraryThing & Libib migration — upload your library export as-is (LibraryThing and Libib in beta); the format is auto-detected, reading statuses and owned/wishlist flags are mapped, and covers are fetched automatically
- Store Mode (offline PWA) — scan barcodes in a bookstore with no signal and get an instant Owned / On wishlist / Not in library verdict; unknown books queue on-device and are added to your wishlist automatically when you're back online (see Store Mode)
- Hardcover — bidirectional reading status sync, import your library, discover new books
- Audiobookshelf — sync selected libraries from your Audiobookshelf server, link physical + digital formats, and jump straight to an item in ABS from its Shelf page
- IGDB — video game metadata, cover art, and platform info via Twitch developer credentials (free)
- RomM — sync a self-hosted RomM server's digital game library. A RomM game gets its own record rather than being matched onto a physical cartridge you already own; link the two yourself. See RomM
- Komga — sync a self-hosted Komga server's digital comics and manga, matching on ISBN where one exists. A library's Comic/Manga kind is kept apart from Shelf's own media type, so it never reclassifies an item you catalogued by hand. See Komga
- ISBNdb — collection valuation with list prices for insurance documentation, and wishlist price alerts
Standing in the shop: scan, and know instantly whether you already own it.
Open Store in the nav (or visit /store), and Shelf caches your library's
ISBNs on the device. From then on, scanning a barcode answers instantly from
the local cache — even with zero signal in a bookstore basement. Books you
scan that aren't in your library are queued on-device and added to your
wishlist (with metadata and cover) the next time you're online.
To install it as an app, use your browser's "Add to Home Screen" while on the store page. One requirement: service workers (the offline machinery) only run on an origin your phone trusts. Options, from simplest to cleanest:
- Trust the self-signed cert on your phone — download the cert from your Shelf server and install it (Android: Settings → Security → Install a certificate → CA certificate; iOS: install the profile, then enable full trust under Settings → General → About → Certificate Trust Settings).
- VPN home (WireGuard/OpenVPN/Tailscale) — the offline cache still does the work in the store; the VPN is only needed when syncing.
- A real certificate — reverse proxy with Let's Encrypt, or
tailscale certfor a ts.net HTTPS name. SetSHELF_TRUST_PROXYto the proxy's address if a proxy sits in front.
Note that localhost is always trusted, so store mode works out of the box
for local development.
Create public read-only links under Settings → Data → Sharing — a wishlist
link for gift ideas or a collection link for browsing. Anyone with the
URL sees titles, authors, covers, and series only (never locations, loans,
values, notes, or ISBNs). Links are unguessable 128-bit tokens, rate-limited,
marked noindex, and revocable at any time.
- Role-based access — admin, editor, and viewer roles
- Web log viewer — monitor auth events, sync activity, and errors from the browser
- HTTPS — self-signed TLS certificates generated on first run
- Backup/restore — database backup and restore from the settings page, with optional passphrase-encrypted (AES) backup downloads that are safe to store off-site
- Hardened by default — strict Content-Security-Policy (no
unsafe-inline/unsafe-eval, no CDNs), CSRF protection on all mutating requests, write-only API credentials in Settings, encrypted credential storage
| Layer | Technology |
|---|---|
| Backend | Python 3.12, FastAPI, SQLite (WAL mode) |
| Frontend | Jinja2, HTMX, Alpine.js, Tailwind CSS |
| Auth | bcrypt, JWT in HTTP-only cookies, Secure whenever the browser connects over HTTPS |
| Container | Docker, non-root user, self-signed HTTPS |
| Role | Can do |
|---|---|
| Admin | Everything: settings, users, locations, tag rename/scope/delete, sync, bulk ops, logs, delete permanently / empty Trash |
| Editor | Add/edit items, delete to Trash and restore, scan (all modes), covers (find/upload/paste URL/remove, and the cover review queue), checkout/checkin, apply tags (one item or a Browse selection), import/export |
| Viewer | Browse, search, set status (read, watched, played), export CSV, view stats |
Shelf queries free, public APIs to look up book and game information — no API keys needed for core book functionality:
| Source | What it provides | API key required? |
|---|---|---|
| Deutsche Nationalbibliothek | German (978-3) ISBNs, consulted before the rest: title, author, publisher, year, language; cover art | No |
| Servizio Bibliotecario Nazionale | Italian (978-88, 979-12) ISBNs, consulted before the rest: title, subtitle, author, publisher, year, language | No |
| Open Library | Title, author, description, cover art, publish info, title search | No |
| Google Books | Fallback metadata and cover art; magazine title search (and the chosen result's cover) when a 977 barcode does not resolve | No (optional key supported) |
| Amazon Images | Fallback cover art via ISBN | No |
| UPC Item DB | Title lookup from UPC barcodes (games, DVDs) | No |
| MusicBrainz | Music releases by title, artist, barcode or catalogue number: pressing details, track lists, release groups | No |
| ISSN Portal | Periodical publications from an ISSN, resolved from a 977 barcode | No |
Metadata lookups send only the ISBN or UPC to these services. No personal data, account info, or collection details are transmitted.
Configure in Settings to unlock additional features:
| Service | Enables | Link |
|---|---|---|
| Hardcover | Reading status sync, richer metadata, import/export, Discover page | hardcover.app |
| Google Books | Optional credentialed metadata, synopsis, and cover requests; anonymous access remains available | Google Books API |
| IGDB (Twitch) | Video game metadata, cover art, and platform info — on UPC scan, title search, and Photo Intake confirm | dev.twitch.tv/console |
| Discogs | Pick the exact pressing of a Music item; Shelf keeps only the Discogs release ID | discogs.com/settings/developers |
| ISBNdb | Collection valuation with market prices; wishlist price alerts | isbndb.com |
| TMDb | DVD/Blu-ray metadata — on UPC scan, title search, and Photo Intake confirm | themoviedb.org |
| Anthropic | Photo Intake — reads spines and recognizes covers (best accuracy) | console.anthropic.com |
| OpenAI-compatible | Photo Intake via any OpenAI Chat Completions endpoint (OpenAI, OpenRouter, vLLM, LM Studio…) | platform.openai.com |
| Ollama | Photo Intake with a fully local vision model — no key needed | ollama.com |
See docs/development.md for the full guide and CONTRIBUTING.md before opening a PR. The essentials:
# Rebuild after code changes
docker compose build && docker compose up -d
# View logs
docker compose logs -f shelf
# Access the database
sqlite3 data/shelf.db
# Rebuild the Tailwind stylesheet after changing templates or static/js
# (all JS/CSS is vendored locally — no CDNs; requires node/npx)
make cssShelf ships with a Makefile that orchestrates a two-pass local QA workflow — no CI/CD required.
One-time setup:
pip install -r requirements-dev.txt
make install-playwright # downloads headless ChromiumCommon targets:
| Target | What it does |
|---|---|
make test |
Unit and integration tests (pytest, excludes E2E) |
make test-e2e |
Playwright E2E browser tests against a live local server |
make test-contract |
Live UPC Item DB contract check — spends one trial lookup; run at release, never on a gate |
make test-all |
test and test-e2e |
make check-deps |
pip-audit vulnerability scan of requirements.txt |
make check-licenses |
License compliance report |
make check-secrets |
Scan tracked files for accidentally hardcoded secrets |
make check-csrf |
Lint that raw fetch() calls send the CSRF token |
make check-deleted |
Lint that every read of items goes through items_live, and every read of item_copies through copies_live |
make check-alpine |
Verify templates stay compatible with the Alpine CSP build, and that the script load order in <head> is intact |
make check-sw-version |
Verify the service worker's cache version matches what it caches |
make check-tests |
Lint the test suite's own conventions |
make checks |
All of the checks above |
make report-review |
Code review report via Claude agent |
make report-security |
Security audit report via Claude agent |
make report-test |
Test coverage audit report via Claude agent |
make reports |
All three reports |
make qa |
Full Pass 1: test-all → checks → reports |
make fix |
Pass 2a: interactive Claude session reads reports and applies fixes |
make verify |
Pass 2b: re-run all tests after fixes |
make release-check |
Alias for make qa |
make install-hooks |
Install a pre-push git hook that runs make test-all |
Typical pre-release workflow:
make qa # run tests, checks, and generate reports
# review reports/CODE_REVIEW_*.md, SECURITY_AUDIT_*.md, TEST_AUDIT_*.md
make fix # Claude reads reports and applies fixes interactively
make verify # confirm all tests still passReports land in reports/ with today's date (e.g. reports/CODE_REVIEW_2026-03-27.md) and are gitignored — they're regenerated each QA cycle, so keep a copy elsewhere if you want to compare against a previous run.
Report targets default to claude-sonnet-4-6. Override with MODEL= for a deeper pre-release audit:
make reports MODEL=claude-opus-4-6AGPL-3.0 — free to use, self-host, and modify. If you offer a modified version of Shelf as a network service, you must make your changes available under the same license.












