ben.stull
7f51f5242f
feat(products): app-served image route — storefront-authorized, immutable cache (SD-0002 §6.4, INV-16)
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-12 00:34:04 -07:00
ben.stull
5d5341b29b
feat(products): confirm → fetching_images + post-commit fetch scheduling + startup recovery (SD-0002 §6.5.3/§6.9)
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-12 00:30:08 -07:00
ben.stull
23267c4d4c
feat(products): image-fetch phase — SSRF guard, bounds, renditions, resume (SD-0002 §6.5.4, §6.9, INV-18)
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-12 00:26:26 -07:00
ben.stull
13e74f4c61
feat(products): image-phase repo helpers + run progress/outcomes/counts (SD-0002 §6.5.4)
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-12 00:23:09 -07:00
ben.stull
984dc98dee
feat(products): diff resolves hosted image URLs to existing records — INV-12 over images (SD-0002 §6.3)
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-12 00:19:23 -07:00
ben.stull
906bc87c96
feat(products): export hosted detail URL for fetched images; snapshot carries status (SD-0002 §6.5.5, INV-16)
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-12 00:15:47 -07:00
ben.stull
da5177c950
feat(products): hosted-image URL build/parse helpers (SD-0002 §6.3.1)
...
DB-free host-agnostic helpers: image_url() builds the canonical hosted
Image Src for a fetched image; parse_image_id() recognizes one on re-import
via path-parse so exports round-trip across localhost/PPE/rebrand (INV-12).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-12 00:13:27 -07:00
ben.stull
0fc29a34dd
feat(platform): objectstore port — local + GCS adapters, config (SD-0002 §6.2)
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-12 00:12:24 -07:00
ben.stull
f27a24353d
feat(platform): images port — decode, resolution bar (Q-3), WebP renditions (SD-0002 §6.2)
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-12 00:09:58 -07:00
ben.stull
af72299a57
fix(products): serialize Variant Position explicitly — INV-12 round-trip bug
...
ci / check (push) Has been cancelled
ci / check (pull_request) Has been cancelled
A stored variant position is a CatalogVariant attribute, not a fields{} entry,
so the serializer emitted an empty Variant Position cell — which re-imports as
'reset to file order'. A non-sequential stored position (a merchant can import
explicit positions) then round-tripped to a spurious update, violating INV-12.
Emit str(variant.position) explicitly (like _write_image). The property-test
generator now assigns non-sequential positions to lock the regression, plus a
targeted test. Also wires isExportEnabled into ProductsPage (was dead code).
Both caught by the final code review.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-11 22:22:11 -07:00
ben.stull
155f9bd147
feat(api): GET /api/products/export — streamed canonical CSV, 409 empty_catalog (§6.4, PUC-9)
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-11 22:05:55 -07:00
ben.stull
1c2a6af986
feat(products): streamed export service + TEL-3 + EmptyCatalog (PUC-9, §9.1)
2026-06-11 22:03:09 -07:00
ben.stull
5a97b9dc59
feat(products): status-filtered export_catalog snapshot query (PUC-9)
2026-06-11 22:01:11 -07:00
ben.stull
fce0b5eaed
test(products): decimal/int variant fields round-trip clean (INV-12 coverage)
2026-06-11 21:59:06 -07:00
ben.stull
b40e4d30b5
test(products): INV-12 property test — export round-trips to a no-op (SD-0002 §6.8)
2026-06-11 21:58:47 -07:00
ben.stull
7652e92cbe
feat(products): serialize multi-variant + interleaved images (§6.5.1 row grammar)
2026-06-11 21:58:22 -07:00
ben.stull
627e257d4c
feat(products): canonical serializer skeleton — header + single product row (SD-0002 §6.5.5)
2026-06-11 21:57:48 -07:00
ben.stull
4dacc2dafd
feat(products): /api/products/* BFF endpoints + sample.csv (DOC-3)
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-11 16:18:34 -07:00
ben.stull
a8538d3ecc
fix(products): cleared Variant Position resolves to file order, not NULL
...
A blank Variant Position cell previewed position->null and aborted the
confirm transaction (variant.position is NOT NULL). Resolve the clear to
the variant's file order at diff time so preview and apply stay in
lockstep; carry file_order on VariantPlan instead of an identity-keyed
map; release the read snapshot before PreviewStale/NothingToApply.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-11 16:14:14 -07:00
ben.stull
0c7865e9e1
feat(products): confirm/apply in one transaction, runs, summary — INV-10/11/14 + TEL-2/6
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-11 16:02:27 -07:00
ben.stull
fcbf1393f5
feat(products): import_validate → draft, preview records, discard + TEL-1
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-11 15:51:03 -07:00
ben.stull
138126ab17
feat(products): repo — catalog snapshot, draft/run SQL, upsert primitives
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-11 15:42:42 -07:00
ben.stull
667a462e0b
fix(products): honest position compare for matched variants in diff
...
A Variant Position column previously produced a spurious update with a
false before:None (CatalogVariant keeps position as an attribute, not in
fields{}). Compare against the attribute, like images already do.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-11 15:37:59 -07:00
ben.stull
9bc6e4dbd2
feat(products): diff engine — add/update/unchanged/error + INV-11 fingerprint
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-11 15:28:03 -07:00
ben.stull
19ee695c20
feat(products): row validation — §6.5.1 rules + INV-15 sanitization
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-11 15:17:01 -07:00
ben.stull
f64c3fddf9
feat(products): CSV codec — file-level parse + INV-18 caps (PUC-5a)
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-11 14:58:53 -07:00
ben.stull
c39bbd4728
feat(products): domain skeleton — errors, column registry, canonical row model
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-11 14:55:37 -07:00
ben.stull
0ee948b34d
feat(products): SD-0002 §6.3 schema — migration 0002 + nh3/multipart deps (SLICE-5)
...
Also updates test_migrate_from_empty_applies_0001 → test_migrate_from_empty_applies_all
to assert both migrations apply on a fresh DB (mechanical consequence of adding 0002).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-11 14:52:18 -07:00
ben.stull
22d738fc74
feat(slice-4): honest delivery failure — send-before-commit + 502 delivery_failed (INV-9; closes #7 )
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-10 23:35:01 -07:00
ben.stull
1b8c60fcb5
feat(slice-4): SmtpMailer — real mail over a configured relay, log-hygienic (INV-8, §6.6)
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-10 23:33:40 -07:00
ben.stull
9f4295b77e
feat(slice-4): backend serves the built SPA — the deployed nginx-proxies-all topology (launch-app §2)
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-10 23:32:22 -07:00
ben.stull
a1c5544694
feat(slice-4): VERSION file + versioned /healthz — the flotilla verify contract (§8.1)
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-10 23:31:20 -07:00
ben.stull
cc9d9dda5f
test(slice-3): INV-1 whole-flow bootstrap test — empty DB to admin answer (§6.8)
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-10 22:37:10 -07:00
ben.stull
306b5c1e5d
feat(slice-3): POST /api/storefronts + storefronts-fed entry routing (§6.4/§6.5)
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-10 22:36:50 -07:00
ben.stull
e11fb032b1
test(slice-3): INV-4 invariants — concurrent-create refusal + many-capable membership schema
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-10 22:34:24 -07:00
ben.stull
ba8b493a31
feat(slice-3): storefronts domain — create + membership + INV-4 guard (§6.5)
...
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
2026-06-10 22:33:57 -07:00
ben.stull
cfc28d7002
fix(slice-2): surface ecomm.* INFO logs so the one-time code reaches the dev log
...
uvicorn configures only its own loggers, so the LogMailer INFO line — PUC-10's local dev
channel (the code in the backend log) — was swallowed. Add an idempotent dedicated handler
for the 'ecomm' logger (propagate=False) in create_app so dev.sh/uvicorn runs show the code.
Caught by the SLICE-2 end-to-end walk; unit tests read the in-memory outbox and didn't.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-10 10:42:30 -07:00
ben.stull
cfd2b4ecc7
feat(slice-2): BFF /api/auth/* — request-code, verify, me, logout (§6.4)
...
Screen-shaped JSON endpoints; signed-cookie session on verify; §6.4 error envelope;
storefront hard-wired null behind the _storefront_for seam (SLICE-3 fills it). No
enumeration: uniform 204. PUC-9 logout clears the cookie.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-10 10:35:01 -07:00
ben.stull
d2eceac272
feat(slice-2): deps — mailer + session dependencies; build mailer at startup
...
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-10 10:34:13 -07:00
ben.stull
95680c9960
feat(slice-2): accounts domain — request_code/verify/get_account (INV-2/INV-3/INV-6)
...
Email-canonical get-or-create, peppered-hash one-time codes with 10-min TTL, single-use,
5-attempt cap, and 60s resend cooldown; uniform new-or-known (no enumeration, §6.6). All
identity rules in the domain layer once (INV-6); scenario + invariant tests green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-10 10:31:32 -07:00
ben.stull
2d15f1a2cb
feat(slice-2): platform/session — stdlib HMAC signed cookie tokens (§6.2)
...
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-10 10:27:35 -07:00
ben.stull
446c13211a
feat(slice-2): platform/mailer port + LogMailer (dev channel, §6.2/PUC-10)
...
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-10 10:26:58 -07:00
ben.stull
7a6b396f65
feat(slice-2): config — session secret, cookie_secure, mailer_kind (INV-8)
...
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-10 10:26:15 -07:00
ben.stull
ef385340e8
feat(slice-1): FastAPI factory + /healthz; self-migrate at startup (INV-1/§6.4)
...
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-10 08:38:42 -07:00
ben.stull
9462423642
feat(slice-1): platform/db migration runner + migration 0001 (INV-7, §6.3 schema)
...
Forward-only, fail-stop, advisory-lock-guarded psycopg runner; migration 0001 lays
the account/auth_code/storefront/membership schema. Tests prove migrate-from-empty
and idempotent re-migrate (INV-1 partial) and the INV-4 many-capable shape.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-10 08:37:09 -07:00
ben.stull
346e7f2e22
feat(slice-1): platform/config — single env-driven config surface (INV-8)
...
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-10 08:35:16 -07:00
ben.stull
4acab40653
feat(slice-1): backend skeleton, deps, and import-linter layer contract
...
Four-layer package (main > domains > platform) per SD-0001 §6.2; GraphQL api layer
deferred. main.py is a stub so the top layer exists from the start (fleshed out in
the /healthz task). lint-imports green on the skeleton.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-10 08:34:38 -07:00