Skip to content

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, the redis compose 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 new postgres:18 image also moved its data mount point from /var/lib/postgresql/data to /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-extensions repository; the core repo is extension-agnostic in-tree (only the vectis.extensions namespace anchor remains)
  • VECTIS_DEV_EXTENSIONS now accepts a parent directory, expanded to its ext_<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_rustfs extension — S3-compatible file storage; local dev docker-compose now runs RustFS instead of MinIO by default (reuses the shared S3_* settings)
  • New FILE_STORAGE_PROVIDER setting 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_sdk and vectis_testkit are 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_sdk facades — 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 license and trust_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-cascade in make check); make release-check dry-runs the full publish, and scripts/bump_version.py bumps everything in lockstep
  • Event topic hardening: channel_id carried on order/cart domain events; fraud-check and lead-created topics moved to versioned .v1 names with a docs-drift gate

State Normalization (2026-07-05)

  • Shared normalize_state_code / resolve_shopper_state helpers wired into all three call sites, replacing ad-hoc state parsing
  • Per-shopper-state hide_completely restriction 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 pinned vectis==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_topics surface — extensions declare the Redpanda topics they produce/consume
  • Versioned Redpanda event producersorder.placed / order.modified, account.created / account.updated, cart.line_added emitted on versioned .v1 topics (Design A: new topics are not re-consumed by the event-bus bridge, so no double-dispatch)
  • Consumer-side derive_consumed_topics(), an event_topics_hash in extensions.lock, a production event-consumer service, and the /marketing/email/omnisend route

True Extension Platform — Dev Discovery & Lint CLI (2026-07-05)

  • $VECTIS_DEV_EXTENSIONS — out-of-tree extension discovery: the dev-dir parent is appended to vectis.extensions.__path__, scanned last with in-tree-wins dedup, so external extensions load without living in the repo
  • vectis ext lint — new CLI (backend/vectis/cli_ext.py) that reuses the check-extension-imports and ext_migrations gates to lint any extension, in-tree or out-of-tree

Admin Accounts IA (2026-07)

  • Admin Customers section renamed to Accounts with a full /accounts route 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: duplicateChannelSettings crash, updateSetting bypass 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 (supersedes AccountPrice) → 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 manifestmanifest = ExtensionManifest(...) scanned pre-activation; new install_hint + docs_url fields; the standalone catalog module is deleted (manifest + extension_index.json are the only metadata sources)
  • Boot-time permission sync — manifest permissions upserted 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 Alembicmigrations/versions/ package data, private alembic_version_ext_<name> version tables, ext_<name>_ table-prefix enforcement, adoption stamping, api-boot auto-run; worker/schedules/consumer fail fast on drift
  • vectis_sdk import surface — frozen re-export layer; shrink-only allowlist ratchet (check-extension-imports) bans new vectis.core/vectis.modules imports from extensions
  • extensions.lock — install-set source of truth; make lock-extensions + check-extension-lock gate
  • Extension UI sync v2 — four legs (admin/, admin_pages/, storefront/, storefront_pages/), .vectis-synced markers, check-extensions-sync gate, make prod-build with EXTENSIONS_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 splitmake makemigration (generate + review) vs make migrate (apply-only), with the check-model-drift guard keeping models and migrations reconciled

Checkout Save Mode (2026-07-01)

  • Admin-selectable checkout_save_mode per 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_shopify and ext_woocommerce data-migration extensions (real-DB tested)

Pluggable Search Engines (2026-06-30)

  • Search engine factory: Meilisearch storefront support, Typesense Cloud, and the ext_algolia extension

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 a PublicCatalogProjection substrate
  • 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_catalog MCP server over the public catalog
  • Saved-card unification — gateway-agnostic saved-card strategy and unified GraphQL surface, dispatch by gateway (dropped the authorize_net hardcode), 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 + payment approval phases) with rejections written to CartRejectionEvent and faults emitted on vectis.workflow.fault.v1
  • Cart-level split-tender composer with PaymentTender.source provenance column (cascade vs buyer_prepended)
  • Stale-approval pricing-drift gateCart.cart_approved_grand_total is 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_inventory status
  • 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 sweepVoidExpiringCardAuthsWorkflow voids authorized card legs that aged past their gateway expiry, including ACH-paired tenders that hadn't settled in time

Inventory, Packaging, MMOQ

  • Packaging UoM ladderpackages + product_packages tables with per-product slug/multiplier; cart and order snapshots store the chosen package; reorder respects the original slug and flags unit_changed when the ladder shifts
  • MMOQ caps on ProductVariantmax_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; MmoqViolationDetail returned on cartBulkLookup rows
  • Reservation state machine — HELD → CONFIRMED → RELEASED / EXPIRED / FULFILLED with TTL expiry via ExpireStaleReservationsWorkflow
  • External stock pushpushExternalStock API-key-scoped mutation; tracked products update reservable inventory, untracked products only set Variant.external_stock_snapshot for audit
  • Inventory state versionProduct.inventory_state_version bumps on policy change and gates place-order races
  • External fulfillment handoffacknowledgeOrderExternalHandoff(orderId, externalReference?) releases internal holds so the ERP becomes the system of record
  • Order.inventory_risk_flag auto-fires on oversold; orders(inventoryRiskOnly:) filter for admin triage
  • clearProductInventoryState(productId): ClearProductInventoryStateResult! releases reservations; clearProductBackorders(productId) unblocks order resumption
  • OrderLineItem.tracking_enabled_at_checkout captures 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 via submitRefundForApproval(orderId, input), approver decides via decideRefundApproval(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 on vectis.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_batch flag) 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_id persists across save→restore)
  • Quick Order CSV/paste with cartBulkLookup preview (MMOQ violations, packaging hints, unitChanged) and partial-success bulkAddToCart
  • AI parse-to-cart from text or photocartAiParseToLookupItems(input: CartAIParseInput!); result feeds cartBulkLookupbulkAddToCart; cart_ai_provider_priority Setting 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_upgrade hook
  • 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_engine swap — 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_chatsupport_chat reduced 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 via prometheus-fastapi-instrumentator) and /ready (parallel DB/Redis/Redpanda probes); /health is the cheap liveness check
  • Token-bucket rate limiter — burst is the ceiling, per-endpoint policies in rate_limit_policies, attributes by X-Forwarded-For
  • JSON Schema event registry under backend/vectis/events/schemas/; see vectis/docs/REDPANDA_TOPICS.md
  • DR runbook + reference backend/scripts/backup_postgres.sh + verify_backup_restore.py weekly verify

Other Platform Polish

  • Fernet-encrypted secrets with SECRETS_MASTER_KEY + SECRETS_MASTER_KEY_ROTATING_FROM rotation
  • Per-gateway settlement webhook dispatcher with multi-secret rotation window
  • Payment gateway capability introspectionpaymentMethodCapabilities query with orphaned + disabled filters
  • Money precision validationmoney.decimal_places save-time + cache invalidation
  • Automated promotion override invalidation — stale rules flag automatically
  • Phase-based promotion enginestack_group, $0 floor, 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-Slug header forwarded by the BFF (header-based; admin/storefront routes stay flat)
  • BFF X-Forwarded-For + Uvicorn --proxy-headers for correct audit/rate-limit attribution
  • Order reissuereissueOrder and reissueShipment create 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 overdraftGcExpiredOverdraftDraftsWorkflow reaps 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 check is 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 (houdini 1.5 + houdini-svelte 2.1) as the sole GraphQL client; every page's ops live in src/lib/houdini/<PageName>.ts and consume generated *Store classes from $houdini
  • Deleted the hand-rolled $lib/api.ts (admin) and server-side gql() helper in $lib/server/api.ts (storefront); the storefront's $lib/server/api.ts now exposes only an apiHeaders() builder for the BFF forwarder
  • Page loaders standardized on +page.ts + load_<QueryName> (no more +page.server.ts for data loading); mutations use new <Op>Store().mutate(...); refetches use <QueryStore>.fetch() instead of invalidateAll()
  • BFF /api/graphql is 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 B905 zip(strict=True), a real B023 bug in schedules.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 modifyOrder mutation for editing authorized orders with automatic reauthorization
  • Added adminCreateOrder mutation 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.type field (authorization, capture, charge, void, refund)
  • Added Order.fraud_status and Order.fraud_details fields
  • 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 UPDATE during 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