Commit Graph
4 Commits
Author SHA1 Message Date
twooeyandClaude Sonnet 5 dcad1b7512 Product cover images via Open Library; Gutenberg literature filter; cron root fix
Cover sync (OpenLibraryAdapter + CoverSync + sync-covers command): fetches
a real cover from Open Library's free, keyless, explicitly-licensed-for-
this-use Covers API and attaches it as a genuine Media Library attachment
(not a hotlinked <img> — WooCommerce's shop loop/gallery/structured data
all need a real _thumbnail_id). ISBN-first, title/author-search fallback
via the confirmed cover_i field, matching the pattern already established
in HardcoverAdapter.

Two real bugs found and fixed while verifying this against the actual
catalog, not assumed:
- A Range-header HEAD-equivalent probe (added to avoid double-fetching)
  caused Open Library's server to redirect with a misleading content-type,
  producing a false positive on a known-fake ISBN. Removed — fetch once,
  verify the real bytes.
- Their search endpoint has genuine transient failures under repeated
  querying (same request, same input, failed then succeeded seconds
  later) — added retry-with-backoff on network errors/5xx, matching how
  the rest of this codebase already treats transient failures as
  retriable rather than fatal.

Also found chasing what looked like a third cover-sync bug, but wasn't
one: uploads/2026/08 was owned by root, silently blocking www-data-run
wp-cli from writing new files. Root cause was a gap in the earlier
root-hardening pass (docker-compose.yml, Commands.php) — it fixed our own
deploy.sh/backup.sh/Makefile invocations but missed the cron sidecar's
own internal process, which was still running its wp-cli loop as root via
a leftover --allow-root. Fixed at the container level (`user: www-data`
on the cron service) since it's a plain shell loop with none of
php-fpm's master-process-needs-root-to-drop-privileges concern.

Gutenberg importer: filters to actual literature via LoCC (Library of
Congress Classification) — verified directly against the real catalog
that novels consistently get a P* code while government documents/
speeches/law get E/JK/KF/DA and never a P code. Removes the Declaration
of Independence, Bill of Rights, etc. from what was importing as "books."

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 14:48:40 -04:00
twooeyandClaude Sonnet 5 71b8065f20 AO3-style tag browsing: taxonomies, display, Hardcover sync (schema unverified)
Four flat taxonomies attached to product (bsc_genre, bsc_mood,
bsc_content_warning, bsc_tag) reuse WordPress's native taxonomy archive
system for the "click a tag, see everything with it" browsing AO3 is known
for — no custom archive templates or query logic needed. Verified: all
four register correctly, render as clickable chips on the product page
(content warnings get a distinct notice instead of just another chip),
and the archive page + term count both work end-to-end with real test
data.

HardcoverAdapter + `wp bookstore sync-hardcover-tags` pull genre/mood/
content-warning/freeform tags from Hardcover's API to populate these.
This part is explicitly NOT verified against a live response — Hardcover's
API is in beta with informal docs, and there was no API token available
to confirm the exact query/response shape. Flagged clearly in the adapter
itself; needs a real token + introspection query before trusting the
field-parsing logic in production. ISBN-first-then-title/author matching
handles both real future ISBNs and the current Gutenberg-synthetic
catalog's fake ones.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 13:59:30 -04:00
twooeyandClaude Sonnet 5 a782f880bc Week 2: catalog schema, Gutenberg importer, pricing engine, membership shipping
Builds bookstore-core's first real business logic (design doc §02-§05,§07),
replacing the plugin stub with:

- Schema: bsc_work/edition/isbn/cover_variant/supplier_offer/member tables
  via dbDelta, with a versioned upgrade path.
- GutenbergImporter: imports Project Gutenberg's live bulk catalog as
  synthetic staging data. Deterministic checksum-valid ISBN-13s (there are
  no real ISBNs in PG data) keep re-imports idempotent.
- SyntheticOfferGenerator: the GutenbergTestAdapter of §04 — realistic
  condition/stock/price distribution with deliberate out-of-stock/stale/
  discontinued cases for later exception-path testing.
- PricingEngine: the §05 margin formula, verified against the design
  doc's own worked example ($6.00 + $3.99 -> $14.99 exactly).
- ProductSync: one WooCommerce product per Work, never per ISBN (§03),
  idempotent (verified: re-sync of 2000 works produces 0 duplicates).
- BSC_Free_Shipping: a real WC_Shipping_Method (WooCommerce's built-in
  Free Shipping only supports one static threshold, not membership-
  conditional), verified through actual browser-session cart flows in
  both directions, not just unit calls.

Found and fixed one real data-integrity bug while scale-testing: PG's
Authors field lists every contributor for anthology works, overflowing
primary_author's column — Work::insert() was failing silently (Catalog
insert methods never checked $wpdb->insert()'s return value), while the
following Edition/Isbn inserts for that same row went ahead anyway and
attached to whichever work_id was last successful. Fixed at the root:
inserts now throw on failure, the importer takes just the first author
(matching the column's actual semantic intent), and the import loop
catches per-row failures so one bad title can't abort a bulk run.

Verified end-to-end against a real 2000-title import: exact work/edition/
isbn/product count parity, no duplicates on re-run, real HTTP cart tests
for both shipping thresholds, live storefront search.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 13:13:38 -04:00
twooeyandClaude Sonnet 5 c9d637c907 Week 1 infrastructure: Docker environments, deploy pipeline, bookstore-core scaffold
Docker Compose environments for dev/staging/production (MariaDB, Redis,
Caddy, Action Scheduler cron sidecar), an idempotent deploy script,
git-hook-based deploy pipeline, backup/restore scripts, and the
bookstore-core plugin stub with WooCommerce HPOS compatibility declared.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 09:50:18 -04:00