SLICE-6: export & the round-trip lock — canonical serializer + streamed export (SD-0002 §7.2) #29
Reference in New Issue
Block a user
Delete Branch "worktree-slice-6-export-roundtrip"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
SLICE-6 — Export & the round-trip lock (SD-0002 §7.2)
Completes PUC-9 (export the catalog) and PUC-10 (the round-trip is visibly lossless) of SD-0002. Anchor: #13.
What landed
serialize.py) — one codec, two directions: theCatalogProductsnapshot → canonical CSV, the inverse of codec/validate, in the §6.5.1 row grammar (product fields + option names on row 0, one variant per row, images interleaved, image-only rows, noOption*Valuefor no-option products).GET /api/products/export?status=all|active|draft|archived— streamedtext/csv(StreamingResponse), storefront-scoped, read-only;409 empty_catalog(raised eagerly, no partial body);422on a bad status.repo.export_catalog(status-filtered, handle-sorted snapshot) +service.export_catalog(stream + TEL-3catalog_exported).<details>disclosure; download links per status); disabled-with-note when the catalog is empty.exportUrl/EXPORT_STATUSES/isExportEnabledhelpers.DoD
diff(catalog, import(export(catalog))) = ∅over 200 generated text-field catalogs + decimal/non-sequential-position coverage.e2e_export_download+e2e_roundtrip_noopgreen (6/6 total).Note
The final code review caught a real INV-12 bug the green tests missed:
Variant Position(aCatalogVariantattribute, not afields{}entry) was emitted as an empty cell, re-importing as "reset to file order" — so a non-sequential stored position round-tripped to a spurious update. Fixed (emit it explicitly, like images); property generator strengthened to non-sequential positions + a targeted regression test.Gates:
check.shgreen (lint-imports, 166 backend pytest, frontend build, 14 vitest);e2e.sh6/6 green.🤖 Generated with Claude Code
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>