Pivot ecomm from a generic multi-tenant Shopify-alternative storefront to the
commitment-commerce + cross-maker verified referral NETWORK direction
(ecomm-content/rfcs/network_strategy.md; OHM identity/relationality super-drafts).
Phase A of the reset — clear the decks:
- Strip the storefront surfaces with no carry-forward: the storefronts domain,
the products import/export/image HTTP spine (service/imagefetch/repo + endpoints),
the SPA frontend, and the Playwright e2e suite.
- Keep the reusable core: /healthz + /api/auth/* (accounts); the
catalog-normalization library (codec/dialect/models/serialize/validate/diff/hosted —
importable, no HTTP yet); platform/* infra. Migrations untouched (append-only).
- Reduce check.sh to backend-only (import-linter + pytest); trim dev.sh and the unused
GCS overlay env. Repoint app.json/README/CLAUDE.md; bump VERSION 0.8.0 -> 0.9.0.
check.sh green: import-linter (main > domains > platform) KEPT, pytest passing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addresses adversarial-review findings: a canonical file carrying a stray
Shopify-signature name (e.g. SEO Title) no longer misdetects as Shopify and
silently drops canonical Type / corrupts the weight unit (kg->g). A
canonical-distinctive column (Description/Variant Cost/Variant Weight/Google
Product Category/Variant Volume/Tax ID/Position) now vetoes detection, so
detection leans conservative (under-detection warns; over-detection corrupts).
Also closes the dual-named-column shadow (Body (HTML)+Description) and the
stale-weight-unit-on-clear case. Tests cover all three.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>
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>
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>
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>
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>
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>