Changelog¶
Platform Waves — Extension Platform, Pricing, Channels, Accounts IA (2026-06 → 2026-07, Current)¶
Recent release cycles, newest first.
Infrastructure — Valkey 9 Cache + PostgreSQL 18 (2026-07-05)¶
- The cache/session store moved from Redis 7 to Valkey 9 (the Linux Foundation's Redis-compatible fork). Same wire protocol and client library —
REDIS_URL, therediscompose hostname, and the metrics exporter are unchanged; only the container image and CLI binaries differ - PostgreSQL upgraded 16 → 18. Major-version upgrades require a dump/restore (or
pg_upgrade) — the newpostgres:18image also moved its data mount point from/var/lib/postgresql/datato/var/lib/postgresql; self-hosted deployments should follow the PostgreSQL major-upgrades procedure in the deployment guide before pulling the new compose files
Extension Graduation — All Extensions Move Out of Core (2026-07-05)¶
- All 48 extensions now live in the separate
enterprise-extensionsrepository; the core repo is extension-agnostic in-tree (only thevectis.extensionsnamespace anchor remains) VECTIS_DEV_EXTENSIONSnow accepts a parent directory, expanded to itsext_<name>/children — a single entry covers the whole external set; docker-compose mounts the sibling checkout read-only- Gate coverage follows the extensions: discovery, lock, schema, UI sync, and contract tests all validate the externally-linked set — dev and CI now check out both repos side by side
RustFS Local File Storage (2026-07-05)¶
- New
ext_rustfsextension — S3-compatible file storage; local dev docker-compose now runs RustFS instead of MinIO by default (reuses the sharedS3_*settings) - New
FILE_STORAGE_PROVIDERsetting pins the active storage backend by name — previously the last-registered strategy silently won; empty keeps legacy behavior, an unknown name falls back with a warning instead of raising - Media serving, avatar upload, media GC, and import-media all resolve storage through the selector
Commercial Core & Open SDK Licensing (2026-07-05)¶
- Licensing pivot: the core engine, cloud offering, and first-party extensions are now proprietary commercial software
vectis_sdkandvectis_testkitare Apache-2.0 and are the only artifacts published to public PyPI; their engine dependency moved to an optional[engine]extra, so the public wheels install without the non-public engine- The release workflow publishes only the open surface — never the engine
Extension Platform Hardening (2026-07-05)¶
- Per-extension event consumer groups — each extension's consumed topics run in their own consumer group (default
vectis-ext-<name>), so one slow or crashing handler no longer lags every other extension; a failing group restarts with capped backoff without tearing down its siblings - Lazy
vectis_sdkfacades — importing an SDK module no longer pulls engine modules until a symbol is actually used; the frozen SDK surface is byte-identical - Extension license metadata — manifests can declare
licenseandtrust_tier, recorded per extension; a boot advisory logs commercial extensions running without a recorded entitlement (warn-only, never blocks activation) - Version lockstep guard — publishable packages must pin the exact engine version (
check-version-cascadeinmake check);make release-checkdry-runs the full publish, andscripts/bump_version.pybumps everything in lockstep - Event topic hardening:
channel_idcarried on order/cart domain events; fraud-check and lead-created topics moved to versioned.v1names with a docs-drift gate
State Normalization (2026-07-05)¶
- Shared
normalize_state_code/resolve_shopper_statehelpers wired into all three call sites, replacing ad-hoc state parsing - Per-shopper-state
hide_completelyrestriction now enforced in the federated-search fallback path (not just the primary index) - Fixes a search-config crash triggered when a channel had no configured shopper state
True Extension Platform — Standalone Packaging (2026-07-05)¶
- In-tree standalone packaging — each extractable extension ships its own
pyproject.toml(depends on pinnedvectis==x) with per-surface include globs, so it can be built as an isolated wheel without editing core - Glob-completeness gate — CI guard that fails if an extension's packaged files drift from its include globs
- Extraction runbook documenting the path from in-tree extension to standalone repo
True Extension Platform — Event Topics & Producers (2026-07-05)¶
- Manifest
event_topicssurface — extensions declare the Redpanda topics they produce/consume - Versioned Redpanda event producers —
order.placed/order.modified,account.created/account.updated,cart.line_addedemitted on versioned.v1topics (Design A: new topics are not re-consumed by the event-bus bridge, so no double-dispatch) - Consumer-side
derive_consumed_topics(), anevent_topics_hashinextensions.lock, a productionevent-consumerservice, and the/marketing/email/omnisendroute
True Extension Platform — Dev Discovery & Lint CLI (2026-07-05)¶
$VECTIS_DEV_EXTENSIONS— out-of-tree extension discovery: the dev-dir parent is appended tovectis.extensions.__path__, scanned last with in-tree-wins dedup, so external extensions load without living in the repovectis ext lint— new CLI (backend/vectis/cli_ext.py) that reuses thecheck-extension-importsandext_migrationsgates to lint any extension, in-tree or out-of-tree
Admin Accounts IA (2026-07)¶
- Admin Customers section renamed to Accounts with a full
/accountsroute tree (/accounts,/accounts/[id],/accounts/create,/accounts/b2c); old/customers*paths 301-redirect - B2C customers surfaced at Accounts → B2C Customers
- Account configuration consolidated under Settings → Accounts (customer groups, statuses, roles & permissions, approvals, payment terms, registration); approval-rules scope disambiguated
Channels Opt-In (2026-07)¶
- Multi-channel opt-in flag — multi-channel UI and mutations are gated behind an explicit setting, with resolver-level guards (hidden UI is never the only defense)
- Single-channel admin presentation — channel framing drops out of the admin (including extension/AI enable toggles) when only one channel exists
- Audit closures:
duplicateChannelSettingscrash,updateSettingbypass of the disable guard, residual channel UI
Pricing Completeness (2026-07)¶
- Eight-level price resolution hierarchy (strict priority, fall-through per level): managed cart prices → location-owned price list (supersedes
LocationPrice) → customer-owned price list (supersedesAccountPrice) → customer category overrides → assigned price list → group category overrides → customer group pricing → default variant price - Price-list lifecycle CRUD, per-customer pricing admin UI, product price matrix
- CSV + config-bundle import/export, override restore UI, B2C customer pricing surface
True Extension Platform — Core Platform (shipped 2026-07-02)¶
The extension contract hardened end-to-end; see Building Extensions for the full developer story.
- Canonical extension ids — id = directory name minus
ext_= entry-point key; legacy names coerced with a warning, collisions refused - Module-level manifest —
manifest = ExtensionManifest(...)scanned pre-activation; newinstall_hint+docs_urlfields; the standalone catalog module is deleted (manifest +extension_index.jsonare the only metadata sources) - Boot-time permission sync — manifest
permissionsupserted additively at every api boot;ext.<name>.<perm>convention (role wiring is planned) - Registration ledger — all registrations happen in
on_activate/hot_reload, attributed via contextvar, auto-unregistered on deactivate/uninstall; cross-extension duplicates hard-fail; fraud providers renamed per-provider (ipqs/maxmind/signifyd/riskified) - Per-extension Alembic —
migrations/versions/package data, privatealembic_version_ext_<name>version tables,ext_<name>_table-prefix enforcement, adoption stamping, api-boot auto-run; worker/schedules/consumer fail fast on drift vectis_sdkimport surface — frozen re-export layer; shrink-only allowlist ratchet (check-extension-imports) bans newvectis.core/vectis.modulesimports from extensionsextensions.lock— install-set source of truth;make lock-extensions+check-extension-lockgate- Extension UI sync v2 — four legs (
admin/,admin_pages/,storefront/,storefront_pages/),.vectis-syncedmarkers,check-extensions-syncgate,make prod-buildwithEXTENSIONS_SYNC_HASH - Payment token claims — gateways declare
claims_payment_token; core infers the gateway from token shape; webhook signature secrets move to Settings (payment.<gw>.webhook_signature_key) with env fallback + boot backfill - Event-consumer extension bridge — the standalone consumer runs extension discovery and bridges consumed Redpanda envelopes onto the in-process event bus (
source="redpanda"), so extension subscriptions fire there - Migrate flow split —
make makemigration(generate + review) vsmake migrate(apply-only), with thecheck-model-driftguard keeping models and migrations reconciled
Checkout Save Mode (2026-07-01)¶
- Admin-selectable
checkout_save_modeper gateway config (off,order_and_save,charge_and_save,save_first), charge-first semantics on NMI (atomic customer-vault add) and Authorize.Net (create-profile-from-transaction), soft fallback when a gateway doesn't support the configured mode
Import Extensions (2026-07-01)¶
- Pluggable import provider surface + orchestration + admin import wizard
ext_shopifyandext_woocommercedata-migration extensions (real-DB tested)
Pluggable Search Engines (2026-06-30)¶
- Search engine factory: Meilisearch storefront support, Typesense Cloud, and the
ext_algoliaextension
SEO, AI & Ops Wave (2026-06)¶
- SEO — per-entity SEO fields, dynamic
sitemap.xml/robots.txt(chunked at the 50k-URL protocol cap), storefront head tags + JSON-LD via a server-side head-payload endpoint, backed by aPublicCatalogProjectionsubstrate - AI SEO — provider-priority meta generation, content analysis, AI vision alt-text, admin SEO assistant panel
- Product feeds — Google Merchant XML + CSV, opt-in with reporting
- AI product access — safe public product API +
llms.txt;ext_mcp_catalogMCP server over the public catalog - Saved-card unification — gateway-agnostic saved-card strategy and unified GraphQL surface, dispatch by gateway (dropped the
authorize_nethardcode), account-scoping IDOR closure - Loyalty redemption — spend points at checkout (pre- and post-tax paths), scheduled points expiration, admin redemption-rules UI
- Observability — structured logging + Sentry
B2B Workflow, Inventory Lifecycle, Refund Approvals (2026-05)¶
A 236-commit wave covering the B2B four-phase approval workflow, packaging + MMOQ, the inventory state machine + external-fulfillment handoff, refund approvals with durable Temporal execution, product labels with predicates, AI parse-to-cart, and the extension self-registration completeness story.
B2B Cart / Order Approval¶
- Multi-phase workflow (
cart+paymentapproval phases) with rejections written toCartRejectionEventand faults emitted onvectis.workflow.fault.v1 - Cart-level split-tender composer with
PaymentTender.sourceprovenance column (cascadevsbuyer_prepended) - Stale-approval pricing-drift gate —
Cart.cart_approved_grand_totalis compared against the live grand total at place-order; mismatch rejects placement - Inventory revalidation gate at placeOrder — re-checks inventory under a serializable transaction; on drift the cart transitions to
cart_approved_blocked_inventorystatus recurateCascade(orderId, newCascade: JSON!, reason?)lets an approver re-curate the tender cascade per-order after a fault- Multi-approver archival quorum preservation so removing one approver doesn't disable an in-flight approval
- Refund cap invariant + recurring auto-pause
- Card-auth expiry void daily sweep —
VoidExpiringCardAuthsWorkflowvoids authorized card legs that aged past their gateway expiry, including ACH-paired tenders that hadn't settled in time
Inventory, Packaging, MMOQ¶
- Packaging UoM ladder —
packages+product_packagestables with per-product slug/multiplier; cart and order snapshots store the chosen package; reorder respects the original slug and flagsunit_changedwhen the ladder shifts - MMOQ caps on
ProductVariant—max_per_customer_per_order,max_per_customer_30d,max_backorder_qty,mmoq_display_unit_id; cart-aggregate enforcement; channel-timezone 30-day window; serializable re-validation at finalize;MmoqViolationDetailreturned oncartBulkLookuprows - Reservation state machine — HELD → CONFIRMED → RELEASED / EXPIRED / FULFILLED with TTL expiry via
ExpireStaleReservationsWorkflow - External stock push —
pushExternalStockAPI-key-scoped mutation; tracked products update reservable inventory, untracked products only setVariant.external_stock_snapshotfor audit - Inventory state version —
Product.inventory_state_versionbumps on policy change and gates place-order races - External fulfillment handoff —
acknowledgeOrderExternalHandoff(orderId, externalReference?)releases internal holds so the ERP becomes the system of record Order.inventory_risk_flagauto-fires on oversold;orders(inventoryRiskOnly:)filter for admin triageclearProductInventoryState(productId): ClearProductInventoryStateResult!releases reservations;clearProductBackorders(productId)unblocks order resumptionOrderLineItem.tracking_enabled_at_checkoutcaptures the tracking flag at place-order time so fulfillment isn't surprised by a live toggle
Refund Approval¶
- Refund Approvals Inbox in admin (
/refund-approvals) — staff submits viasubmitRefundForApproval(orderId, input), approver decides viadecideRefundApproval(refundRequestId, approved, notes?) - Self-approval blocked at the API; per-tender progress tracking in the inbox
- Refund execution is a durable Temporal
RefundExecutionWorkflow(backend/vectis/modules/refund_approval/workflows.py); faults emit onvectis.workflow.fault.v1;retryRefundExecution(refundRequestId)is idempotent against empty-transaction states
Product Labels¶
- Predicate-driven labels rendered on PDP, related products, recently-viewed, search results, and catalog cards
- Batch evaluation (
evaluate_many+supports_batchflag) so hundreds of labels evaluate against thousands of products in one pass - Stale reference GC via
clearProductLabelStaleReferences(extensionName: String!): Int!mutation — scoped per extension; predicate namespacing keeps cross-extension predicates from colliding
Cart Bulk Ops + AI Parse-to-Cart¶
- Bulk select + delete + save-for-later with
bulkRemoveCartLines(lineIds),bulkSaveCartLinesForLater(lineIds),restoreSavedCartLine(unit_package_idpersists across save→restore) - Quick Order CSV/paste with
cartBulkLookuppreview (MMOQ violations, packaging hints,unitChanged) and partial-successbulkAddToCart - AI parse-to-cart from text or photo —
cartAiParseToLookupItems(input: CartAIParseInput!); result feedscartBulkLookup→bulkAddToCart;cart_ai_provider_prioritySetting controls provider order
Extensions Plug-In Completeness¶
- Self-registration for goshippo, omnisend, shipstation; new slots for CarrierSeed, SavedPayment, AI providers, notification templates, Cmd-K Quick Actions, federated search, page tabs, nav, models, GraphQL queries / mutations
- ExtensionRegistry._activate consults
InstallStateService— install-state persistence, version detection,on_upgradehook - Zero-extension-imports invariant enforced via AST guard — core never imports
ext_* - Worker readiness gate — refuses to start if an enabled payment method has no registered strategy (strict mode default)
ext_excise_engineswap — decommissioned the old excise extension with a one-click config migration- TaxJar real integration — calculation, filing workflow, order sync (replaces stub)
- J'AI →
ext_jai_chat—support_chatreduced to strategy ABCs only; the extension contributes models, GraphQL, admin tab, and a session-cleanup schedule - Cmd-K Quick Actions + federated search — extensions contribute palette entries and async search indexes; merged into the global picker
Observability + DR¶
/metrics(Prometheus viaprometheus-fastapi-instrumentator) and/ready(parallel DB/Redis/Redpanda probes);/healthis the cheap liveness check- Token-bucket rate limiter — burst is the ceiling, per-endpoint policies in
rate_limit_policies, attributes byX-Forwarded-For - JSON Schema event registry under
backend/vectis/events/schemas/; seevectis/docs/REDPANDA_TOPICS.md - DR runbook + reference
backend/scripts/backup_postgres.sh+verify_backup_restore.pyweekly verify
Other Platform Polish¶
- Fernet-encrypted secrets with
SECRETS_MASTER_KEY+SECRETS_MASTER_KEY_ROTATING_FROMrotation - Per-gateway settlement webhook dispatcher with multi-secret rotation window
- Payment gateway capability introspection —
paymentMethodCapabilitiesquery with orphaned + disabled filters - Money precision validation —
money.decimal_placessave-time + cache invalidation - Automated promotion override invalidation — stale rules flag automatically
- Phase-based promotion engine —
stack_group,$0floor, order-discount distribution; enforcement-vs-observability usage writes - Stage-sorted tax engine with order-subtotal redistribution
- CMS block templates + ProductTranslation overlay with cache contract
- Split CSP for CMS embeds
- Multi-factor affiliate fraud guard + suspicious review
- Location archive + 3-way employee reassignment; storefront active-location switcher; channel context via
X-Channel-Slugheader forwarded by the BFF (header-based; admin/storefront routes stay flat) - BFF
X-Forwarded-For+ Uvicorn--proxy-headersfor correct audit/rate-limit attribution - Order reissue —
reissueOrderandreissueShipmentcreate new linked orders so ERP pollers pick them up; original order untouched (Order columns:is_reissue,reissued_from_order_id,reissue_reason) - Background shipping rate precompute with explicit cache-hash composition, cubing dedup, untracked-item filter, asyncio yield
- Allow store-credit overdraft —
GcExpiredOverdraftDraftsWorkflowreaps unredeemed drafts - Cookie consent rewrite — close 7 audit gaps + always-show granular banner
- A11y warning sweep — 6 critical errors fixed, 50 warnings cleared
- Tests at 1,670 across backend + storefront/admin Playwright;
make checkis the local gate (regression guard + schema-drift guard) - CI currently disabled in favor of local
make check(2026-05-18); push directly to main when the local gate passes
Platform — Houdini Frontend Migration & CI Pipeline¶
- Migrated both admin and storefront apps to Houdini (
houdini1.5 +houdini-svelte2.1) as the sole GraphQL client; every page's ops live insrc/lib/houdini/<PageName>.tsand consume generated*Storeclasses from$houdini - Deleted the hand-rolled
$lib/api.ts(admin) and server-sidegql()helper in$lib/server/api.ts(storefront); the storefront's$lib/server/api.tsnow exposes only anapiHeaders()builder for the BFF forwarder - Page loaders standardized on
+page.ts+load_<QueryName>(no more+page.server.tsfor data loading); mutations usenew <Op>Store().mutate(...); refetches use<QueryStore>.fetch()instead ofinvalidateAll() - BFF
/api/graphqlis a CSRF-guarded forwarder that attaches the JWT from the session cookie server-side — JWT never reaches the browser - Added GitHub Actions CI (
.github/workflows/check.yml) with five jobs —backend-lint(ruff),backend-resolver-check(AST guard against duplicate Strawberry resolvers),backend-tests(pytest against real PG 16 + Redis),admin-check(houdini generate+svelte-check, 0 errors),storefront-check(same) - Backend cleanup landed during the ruff sweep: 10 F821 fixes, 3 B904
raise ... from, 2 B905zip(strict=True), a real B023 bug inschedules.py(closure captured last iteration), and 44 E741 renames
Payment Lifecycle Rework¶
- Reworked payment processing to support configurable capture modes (authorize-only vs auth+capture)
- Added full payment lifecycle: authorize → capture → void → refund with per-transaction status and type tracking
- Automatic void/refund on order cancellation and refund transitions via state machine side effects
- Authorize.net FDS fraud filter detection: orders flagged as
HeldForReview, admin fraud hold release - Added
modifyOrdermutation for editing authorized orders with automatic reauthorization - Added
adminCreateOrdermutation and admin UI for staff to create orders with saved card charging - Saved payment methods now support B2B location scoping (
location_id) - Admin payments page now includes capture mode configuration (Authorize Only / Authorize + Capture)
- Added
PaymentTransaction.typefield (authorization,capture,charge,void,refund) - Added
Order.fraud_statusandOrder.fraud_detailsfields - Updated order detail page with fraud hold banner, transaction actions (capture, void), and release button
- Added admin Create Order page with account/channel selection, line items, saved cards, and capture mode
Core Geocoding Service¶
- Added geographic data model (GeoUnion, Country, Region, County, City) with ISO standards
- Implemented IP geolocation, address geocoding, and address validation strategies
- Enriched RequestContext with IP and geo data
- Added admin UI for managing geographic data and geocoding settings
Tax Architecture Rework¶
- Rebuilt tax calculation engine using geographic data models
- Added support for granular sales tax, excise tax, and compound tax
- Tax jurisdiction lookup by ISO region, county, and city
- Added admin Tax management page
Documentation System¶
- Added MkDocs Material documentation site with developer, usage, and operations guides
- Added in-admin contextual help system (HelpDrawer + HelpTooltip components)
- Added structured, translatable help JSON files for all admin routes
- Added Cursor rule to enforce documentation updates alongside code changes
- Added GraphQL schema introspection script for auto-generated API reference
Multi-Currency & Checkout Pipeline¶
- Implemented multi-currency support: channel-level supported currencies, exchange rates, currency-aware price resolution
- Added dual-amount orders (transacted + base currency)
- Wired end-to-end checkout pipeline: promotions → shipping → tax → payment → order creation
- Integrated promotion engine with cart-level coupon application and per-line discount allocation
- Added cart pessimistic locking via
SELECT FOR UPDATEduring checkout - Added Redpanda event publishing for
order.created,order.status_changed,inventory.adjusted - Added dedicated event consumer service in Docker Compose
Internationalization & Channel Commerce Mode¶
- Implemented channel-based commerce modes: B2B, B2C, hybrid
- Added multi-language support with JSONB translations on products, categories, brands, and CMS pages
- Added locale resolution middleware (X-Locale header, cookie, Accept-Language)
- Added LanguageSwitcher and CurrencySwitcher storefront components
- Implemented channel-scoped extension activation via ChannelExtension model
Payment & Shipping Extensions¶
- Built Authorize.Net CIM extension (PaymentProcessStrategy)
- Built gift card extension with virtual/physical cards, partial redemption, bulk generation
- Added UPS shipping rate extension
- Replaced inline SVG/emoji icons with Lucide icons across storefront and admin
- Added shipping carrier icon components (UPS, USPS, FedEx, OnTrac, freight)
- Added payment card brand icons (Visa, Mastercard, Amex, Discover)
Testing & Production Prep¶
- Set up pytest with real PostgreSQL test database
- Added test fixtures: event_bus, strategy_resolver, request contexts
- Wrote test suites for strategy resolver, pricing, cart/order flow, GraphQL API, RBAC, events, extensions
- Created production-ready multi-stage Dockerfiles
- Added CMS policy pages: contact, shipping, return, privacy, terms
- Fixed accessibility warnings in storefront and admin layouts
Admin Panel & Dashboard¶
- Built 20+ admin routes: orders, products, customers, pricing, promotions, inventory, shipping, tax, CMS, settings
- Implemented command palette (Cmd+K) with fuzzy search
- Added Customer 360 view
- Built collapsible sidebar navigation with Lucide icons
Core Commerce Modules¶
- Implemented all 14 strategy ABCs with default implementations
- Built promotion engine with condition/action pattern and CartIndex
- Built fulfillment module with by_order and by_box modes
- Built RMA module with line-level approval
- Built store credit, gift card, and net terms modules
- Built reporting and audit modules
Pricing & Inventory¶
- Implemented five-level pricing hierarchy
- Built price list management with scheduling and volume tiers
- Built inventory module with warehouses, stock levels, and reservations
- Implemented backorder support
Orders & Cart¶
- Built configurable order state machine
- Implemented cart with per-location support and session-based guest carts
- Built checkout mutation with initial pipeline
- Fixed SQLAlchemy identity map caching issues
Auth & Accounts¶
- Implemented BFF authentication pattern with httpOnly session cookies
- Built B2B account hierarchy: Account → Location → Employee → Address
- Implemented RBAC with role-based permissions
- Built customer group system
Products & Catalog¶
- Built product module with variants, categories, and brands
- Implemented Meilisearch integration for full-text search
- Built catalog visibility rules engine
Foundation¶
- Scaffolded monorepo: backend, admin, storefront, migrator
- Set up Docker Compose with PostgreSQL, Redis, Redpanda, Temporal, Meilisearch, MinIO
- Created FastAPI application factory with Strawberry GraphQL
- Created SvelteKit admin and storefront shells
- Set up Alembic migrations with initial schema
- Established Forge methodology artifacts: constitution, conventions, reconciliation