Commit Graph

24 Commits

Author SHA1 Message Date
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 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 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 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 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