m2-market/specs/001-market-first-wedge/contracts/catalog-index.md

2.9 KiB

Contract: market:catalog partition (memory-api) (v1)

The semantic index over published listings. Derived data only — rebuildable from m2/market-registry at any time.

Partition

  • Namespace: market:catalog (memory-api partition, same mechanism as fedlearn:core-index design).
  • Document = one published listing: embedded text = name + summary + keywords + intent + evidence_summary (BGE-M3 hybrid dense+sparse); payload = full listing.json + tenant_visibility.
  • Endpoint: the stack behind memory.machinemachine.ai (split-brain caveat: research.md R4 — pinned until fedlearn T13 resolves; recovery = reindex).

Writers

ONLY m2-market-indexer:

  • watch — on registry merge (webhook or poll ≤5 min), upsert changed listings, remove delisted.
  • reindex — drop partition, rebuild from registry main (constitution II gate; must complete < 5 min at wedge scale).

Readers

  • CLI m2-market search → memory-api semantic query, server-side filter tenant_visibility ⊇ {caller tenant | "*"} (FR-007; Scout edge case "impossible by construction").
  • M2 Store front page/search → same query path (via CLI or direct HTTP with desktop creds).
  • Scout matcher → same query path, additionally thresholded by confidence.

Query contract (via memory-api HTTP)

Request: {query_text, partition: "market:catalog", tenant, limit} → Response items: {listing_id, score, listing: <payload>} ordered by hybrid score.

Telemetry write-back (FR-012, FR-014)

Install + proposal events land as memory records in market:evidence partition keyed by listing_id; a periodic indexer pass folds counts into stats in the registry (PR-less stats commit or scheduled batch PR — implementation's choice, registry remains truth).

AS-BUILT AMENDMENT (2026-07-02, analyze finding I1)

The live memory-api (agent.memory.system) exposes /memory/store + /memory/search only — no partition drop, no delete, no upsert-by-id, and its server-side tenant filter is a scalar tenant_id (not an array match). The implementation therefore deviates:

  • Partition = the agent_id namespace; documents are memories with the listing in metadata.listing and metadata.listing_id.
  • Writer upsert = search-first for metadata.listing_id, store only if absent (idempotent fill). Listing UPDATES and delist removal require memory-api delete — until then, delisted/updated listings are filtered/handled at read time.
  • Reader tenant filter tenant_visibility ⊇ {caller tenant | "*"} is enforced in the ONE shared reader client (cli/src/m2_market/catalog.py), which every surface uses.
  • Full rebuild = fresh-partition switch (MEMORY_API_PARTITION writer-side, M2_MARKET_CATALOG_PARTITION reader-side) — docs/runbook.md.

The original section above is retained as the target contract for when memory-api grows partition CRUD (fedlearn T13 follow-up).