Level 2: Persona Services

Windows & Curation

A window is the practical Level 2 unit of persistent curation: a user-scoped collection that stores inspiration, product context, and the latest personalized retrieval results together.

What A Window Is

If you are building on Prism and Mosaic, think of a window as the saved container around a curation workflow. It can be seeded from captures, product handles, targeted retailer products, or cluster-derived recommendations. The same object can then support browsing, re-curation, sharing, and search.

  • User-created windows start from explicit inputs like captures or selected products
  • Auto windows can be generated from cluster or preference signals derived by Mosaic
  • Focused windows are domain-specific windows built from retailer-targeted product seeds

Core Flow

  1. Collect a seed set from captures, products, or a Mosaic-derived recommendation set
  2. Create window metadata in your own application layer
  3. Run Prism curation against the seed set
  4. Store entries, curation results, and provenance metadata so the window can be refreshed or searched later

TypeScript Example

A practical pattern is to let Mosaic choose retailer-specific seeds, then use Prism to produce the personalized product set you store inside your window.

type FocusedProduct = {
  product_variant_handle: string;
  name?: string;
  image_url?: string | null;
  cover_image_url?: string | null;
  window_title?: string | null;
};

type WindowRecord = {
  id: string;
  userId: string;
  title: string;
  domain: string;
  previewImageUrl?: string;
  source: {
    name: "mosaic-focused";
    domain: string;
    product_variant_handle: string;
    generatedAt: string;
  };
  entries: Array<{ product_variant_handle: string }>;
  results: unknown[];
};

async function getTargetedProducts(domain: string, token: string): Promise<FocusedProduct[]> {
  const response = await fetch(`${process.env.MOSAIC_URL}/domains/${domain}/targeted-products`, {
    headers: { Authorization: `Bearer ${token}` }
  });

  if (!response.ok) {
    throw new Error(`Mosaic targeted-products failed: ${response.status}`);
  }

  const data = await response.json();
  return data.products as FocusedProduct[];
}

async function curateWindow(
  userId: string,
  domain: string,
  productHandles: string[],
  prismToken: string
) {
  const response = await fetch(`${process.env.PRISM_URL}/search/unified`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${prismToken}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      queries: productHandles.map((handle) => ({ product_variant_handle: handle })),
      user_id: userId,
      domains: [domain],
      n_products: 48,
      n_pool: 300,
      noise: 0.0
    })
  });

  if (!response.ok) {
    throw new Error(`Prism curation failed: ${response.status}`);
  }

  const data = await response.json();
  return data.results;
}

async function createFocusedWindow(
  userId: string,
  domain: string,
  prismToken: string,
  mosaicToken: string
): Promise<WindowRecord[]> {
  const targetedProducts = await getTargetedProducts(domain, mosaicToken);

  const deduped = Array.from(
    new Map(
      targetedProducts.map((product) => [product.product_variant_handle, product])
    ).values()
  );

  return Promise.all(
    deduped.map(async (product) => {
      const results = await curateWindow(
        userId,
        domain,
        [product.product_variant_handle],
        prismToken
      );

      return {
        id: crypto.randomUUID(),
        userId,
        title: product.window_title ?? product.name ?? `Focused: ${domain}`,
        domain,
        previewImageUrl: product.cover_image_url ?? product.image_url ?? undefined,
        source: {
          name: "mosaic-focused",
          domain,
          product_variant_handle: product.product_variant_handle,
          generatedAt: new Date().toISOString()
        },
        entries: [{ product_variant_handle: product.product_variant_handle }],
        results
      };
    })
  );
}

Refresh Pattern

Keep the window metadata stable and rerun Prism against the saved seed set when the user asks for a refresh.

async function refreshWindow(window: WindowRecord, prismToken: string) {
  const productHandles = window.entries.map((entry) => entry.product_variant_handle);

  const results = await curateWindow(
    window.userId,
    window.domain,
    productHandles,
    prismToken
  );

  return {
    ...window,
    results
  };
}

Implementation Notes

  • Source metadata matters because you will want to distinguish user-created, auto-generated, and retailer-focused windows
  • Viewer vs owner behavior should usually differ: owners can refresh and mutate, while public viewers mainly get cached reads
  • Approval or moderation state is useful when you generate windows automatically and do not want to surface all of them immediately
  • Window search should be treated as its own retrieval mode, not just a UI grouping abstraction

When To Use It

Use windows when you need durable curation state rather than a one-off search response. They are the right abstraction for saved discovery flows, personalized landing surfaces, retailer-specific collections, and any product experience that needs to evolve as user taste changes.