Skip to content

feat(skills): add vector-ranking skill - #3

Merged
Plopix merged 2 commits into
mainfrom
feat/vector-ranking-skill
Sep 15, 2026
Merged

Plopix merged 2 commits into
mainfrom
feat/vector-ranking-skill

Conversation

@papelipe

Copy link
Copy Markdown
Member
Q A
Branch? main
Bug fix? no
New feature? yes
BC breaks? no
Fixed tickets #...

Adds a vector-ranking skill covering Discovery vector ranking, and refreshes the Discovery query reference, which had drifted from the served schema.

Why its own skill

Vector ranking spans two APIs — vocabularies and item taste are authored on Core, ranking happens on Discovery — so splitting it across query and mutation would have left the flow without a single narrative. It also has a modelling dimension (designing a vocabulary) closer to taxonomy than to either, and its trigger vocabulary ("personalization", "more like this", "boost by margin", "recommendations") would never have matched query.

What's in it

File Covers
SKILL.md Capability detection, the two APIs, five concepts, the pipeline, choosing a query path, failure modes
references/vocabulary-authoring.md Vocabulary design, positional weights, upsertVocabulary, setItemTaste, key validation, publish, ignite, verification
references/ranking-signals.md The five rankBy signals, per-tenant field enums, normalization, multi-value collapse, rankScore/rankExplain, rerank window
references/personalization.md context.userTaste, magnitude, sourcing the shopper vector, blending, nearestTo

query and mutation get ## Related skills pointers, and mutation/references/core-api.md gains a Vector Ranking Mutations section so the Core calls are findable from where they live.

Verified against the API, not just the docs

Everything was checked by introspecting the live Core API and a ranking-enabled Discovery tenant. That surfaced several things the documentation omits or states loosely:

  • RankExplainEntry carries index — without it you cannot tell two fieldBoost terms apart.
  • fieldBoost.field is the TenantRankByField enum (NUMBER/DATE filterable attributes, facet fields excluded), not "any numeric field". Guessing a field name fails validation.
  • igniteDiscoApi takes stacks, and vectors are only built with stacks: opensearch. Omitting it rebuilds the index without vectors and raises no error, so it is documented as a required step and as its own row in the failure-mode table.
  • IgnitionBulkTaskResult and SetItemTasteResult both include ExperimentalFeaturesNotAvailableError, which appears in neither doc version.
  • Under rerank, cursor pagination falls back to offset, and a skip past the rerank window returns nothing.

The skill leads with capability detection: rankBy, context and nearestTo are absent from a tenant's Discovery schema until ranking is enabled, so referencing them is a GraphQL validation error rather than an unranked result. That is the failure an agent is most likely to hit, so it is the first section rather than a footnote.

Where the two public doc pages contradict each other on how sorting composes with ranking, the skill says so and declines to assert either reading.

Discovery reference refresh

query/references/discovery-api.md was missing autocomplete, options.fuzzy, sorting, filter operators, profiling, pathResolutionMethod and priceRange, and did not mention that the schema is generated per tenant. All added.

Notes for the reviewer

  • No code changes needed — both the MCP server (virtual:skills) and the docs site discover skills from the filesystem. Plugin version bumped 3.5.0 → 3.6.0 and the README list updated.
  • Markdown formatted with the repo's own oxfmt; query/ and mutation/ are back to formatted-clean as they were on main.
  • make codeclean's type-check/lint and make tests were not run locally — bun was unavailable in the authoring environment. No TypeScript is touched, so CI should be green, but that is unverified from my side.
  • Pre-existing and left alone: mass-operations/* and permissions/SKILL.md are not oxfmt-clean on main, and three relative links are broken (js-api-client ×2, query/references/core-api.md ×1). Happy to fix either in a separate PR.

🤖 Generated with Claude Code

Discovery vector ranking spans two APIs — vocabularies and item taste are
authored on Core, ranking happens on Discovery — so it gets its own skill
rather than being split across query and mutation.

New skill `vector-ranking` with three references:

- vocabulary-authoring: vocabulary design, positional weights,
  upsertVocabulary, setItemTaste, key validation, publish, ignite, verify
- ranking-signals: the five rankBy signals, the per-tenant field enums,
  normalization defaults, multi-value collapse, rankScore/rankExplain,
  rerank window
- personalization: context.userTaste, magnitude, sourcing the shopper
  vector, blending with rankBy, nearestTo

Verified against the live Core API and a ranking-enabled Discovery tenant
by introspection rather than from the docs alone. That surfaced several
things the docs omit or state loosely:

- RankExplainEntry carries `index`, needed to tell two fieldBoost terms apart
- fieldBoost.field is the TenantRankByField enum (NUMBER/DATE filterable
  attributes, facet fields excluded), not any numeric field
- igniteDiscoApi takes `stacks`; vectors are only built with
  `stacks: opensearch`, and omitting it fails silently
- IgnitionBulkTaskResult and SetItemTasteResult both include
  ExperimentalFeaturesNotAvailableError
- under rerank, cursor pagination falls back to offset and skip past the
  window returns nothing

The skill leads with capability detection: rankBy, context and nearestTo
are absent from a tenant's schema until ranking is enabled, so referencing
them is a validation error rather than an unranked result.

Also refreshes query/references/discovery-api.md, which had drifted from
the served schema — adds autocomplete, options.fuzzy, sorting, filter
operators, profiling, pathResolutionMethod, priceRange, and a note that
the schema is generated per tenant.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ples

Review feedback on #3: Core Next error members all implement the
BasicError interface, so one fragment covers every failure and errorName
identifies which one — spelling out each error type is noise.

This was already the convention elsewhere in mutation/references/core-api.md;
the vector ranking examples simply did not follow it.

setItemTaste and igniteDiscoApi now select `... on BasicError { errorName
message }`. The schema listings still name the individual union members,
since that is reference material rather than a query to copy.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Plopix
Plopix merged commit fda4760 into main Sep 15, 2026
1 check passed
@Plopix
Plopix deleted the feat/vector-ranking-skill branch September 15, 2026 19:42
vydimitrov added a commit that referenced this pull request Sep 28, 2026
* feat(skills): add bookable-resources skill

Bookable resources — products held for a window of time instead of counted
in stock — had no coverage in any skill. Two storefront builds (Tools
Universe rentals, and the rental work in Car Parts Universe) had to
discover the whole surface from the schema, and three of its failure modes
are silent.

New skill `bookable-resources`, with references for the Core setup, the
Shop API flow and modelling.

Verified on 2026-09-24 against the live Core API and Shop API on the
tools-universe tenant, including two test reservations taken and released.
Confirmed directly:

  - Policy durations are seconds. `heavy-equipment` reads back as
    advanceWindow 10368000 = 120 days, cancellationWindow 172800 = 2 days,
    pendingHoldDuration 900 = 15 minutes. `humanized` gives the same
    values as { value, unit }, which is how you catch a wrong unit.
  - `bookingPolicies` is a connection, and `stats.referencingProductCount`
    is what blocks a delete.
  - A pool is units or capacity, never both; unit `meta` carries depot,
    serial and service data, and Discovery serves it back on the item.
  - `checkBooking` requires `language` and returns { ok, reason }, not a
    union. `bookSkuItem` returns Cart | ReservationConflict | NotBookable |
    InvalidRange | InvalidUnitId — refusals are results, not errors.
  - The reservation id is on the cart LINE's meta (`reservationIds`),
    alongside a `booking.window` the API writes itself. The line's unitId
    is not returned, so the caller has to store it.
  - `cancelReservation(cartId, reservationId)` applies the cancellation
    window to a hold that was never bought: a booking for tomorrow under a
    two-day window answers CancellationWindowClosed, so a shopper cannot
    empty their own basket. Re-hydrating without the line releases it, and
    the reservation then reads null.
  - `confirmCartBooking` answers Cart | NotPlaced | NothingToConfirm |
    ReservationNoLongerHeld.

The one thing a storefront cannot get right by reading the schema is the
double confirmation: `confirmCartBooking` writes `orderId` onto the
reservations only if the cart already has one, and the cart is linked in
the background a few hundred ms after `createFromCart` returns. A single
confirm leaves { state: CONFIRMED, orderId: null } and the admin shows
"No order — not checked out" for a booking that was paid for. The skill
documents polling the cart and confirming again, as Tools Universe does.

Version to 3.7.0 in the three manifests. mcp-servers/crystallize was left
on 3.5.0 by #3 and is brought back into lockstep.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(skills): align bookable-resources with the backend

- No feature flag gates bookings; RBAC does. Replace the
  ExperimentalFeaturesNotAvailableError probe with the FORBIDDEN case.
- setBookable, reapplyBookablePolicy and clearBookable write the draft;
  each needs a publish before the Shop sees it.
- BookablePoolKindChangeError only fires capacity -> units while a
  capacity pool is published; document clear, publish, drain, set.
- bookableProducts lists published configurations only; the pool is
  shared by every language of the product.
- The first confirmCartBooking commits the slot (CONFIRMED never
  expires); mark it optional, place payment in the sequence, and show
  the /booking/admin cancel for a failed payment.
- Expired holds become EXPIRED and are re-taken on hydrate; placed
  carts refuse hydrate, cancel and rebook.
- Add the hydrate shape for a booking line (lineId, window, unit), the
  exact cancellation rule, why orderId must be polled, and that
  bookSkuItem ignores group and type.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Vasil Dimitrov <v.y.dimitrov@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants