Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions use-crystallize/skills/mass-operations/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,11 @@ A schema violation or unparseable JSON marks the task `error` before any operati
`bulkTask.info.error` reads only `Invalid Operation File` — the per-field issues go to the worker's own
logs, not to you. Validate locally to see them.

**Seeding a tenant that Discovery will serve:** run `igniteDiscoApi` after the operations that create
the shapes and content have completed with clean `operationLogs`, never before or alongside them. Poll
the ignition task like any other bulk task, treat `error` as blocking, and probe Discovery with
`{ __typename }` before reporting success. See [[vector-ranking]] for the full ignition steps.

## Rules that prevent most failures

- **An item `upsert` with neither `itemId` nor `resourceIdentifier` always CREATES** — on every run.
Expand Down
4 changes: 3 additions & 1 deletion use-crystallize/skills/mutation/references/core-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -763,7 +763,9 @@ is not proof that the items are published yet — index right after it and the i
versions.

**Re-run `igniteDiscoApi` after every change to vocabularies or taste entries** — an unindexed change
has no effect and raises no error. Omitting `stacks: opensearch` likewise fails silently: the index
has no effect and raises no error. Run it **last**, once shapes and published content exist. A task
that ends in `error` is blocking: `bulkTask(id:) { ... on BulkTask { status info } }` carries the reason
in `info.traces`. Probe Discovery with `{ __typename }` before reporting success. Omitting `stacks: opensearch` likewise fails silently: the index
rebuilds, but without vectors. Full guidance, including vocabulary design, positional weights and
key validation, is in the [[vector-ranking]] skill.

Expand Down
4 changes: 3 additions & 1 deletion use-crystallize/skills/query/references/discovery-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ the tenant is re-indexed:

**Introspect, do not assume.** An un-ignited tenant answers
`{"success": false, "message": "There is no ignited Tenant for <tenant>."}` rather than serving a
schema at all.
schema at all. A tenant with no shapes has no `browse` field. A tenant whose schema cannot be built
answers HTTP 500 with `extensions.code: "TENANT_SCHEMA_UNAVAILABLE"`. Retrying will not help, so report
it with the tenant identifier. `{ __typename }` is the cheapest check that a tenant is serving.

> **Note**: The Discovery API uses **lowercase** type names in inline fragments (`... on product`,
> `... on category`) because types are derived from your shape identifiers. Interface fragments keep
Expand Down
5 changes: 4 additions & 1 deletion use-crystallize/skills/vector-ranking/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ Two properties fall out of this and drive every design decision:
Core upsertVocabulary once per vocabulary — FULL REPLACE, not a patch
Core setItemTaste once per item, per vocabulary — writes the DRAFT
Core publishItem per item and language — the step that is easy to miss
Core igniteDiscoApi(stacks: opensearch) poll bulkTask until "complete", then let it propagate
Core igniteDiscoApi(stacks: opensearch) LAST; poll bulkTask until "complete" ("error" is blocking)
Discovery { __typename } probe before reporting success
Discovery search(rankBy:) your rules: margin, stock, velocity, recency
Discovery search(context:) ranked for this shopper
Discovery search(rankBy: + context:) your rules, plus this shopper
Expand Down Expand Up @@ -173,6 +174,8 @@ Four of these produce **no error at all** — they are the reason this skill exi
| Order unrelated to taste, no errors | Taste is on the draft only | `publishItem`, index again, retest |
| Order unchanged after editing taste | No index run since the change | `igniteDiscoApi(stacks: opensearch)`, wait for `complete` |
| Index rebuilt, still no vectors | `igniteDiscoApi` run without `stacks: opensearch` | Re-run with `stacks: opensearch` |
| Ignition task ends in `error` | e.g. ignited before any shape existed | Read `bulkTask.info.traces`, fix what it names, then ignite again. Never re-run it unchanged |
| `{ __typename }` fails after `complete` | Discovery cannot build the tenant's schema | Do not re-ignite in a loop. Report it with the tenant identifier and the response body |
| Rankings subtly wrong, no errors | `magnitude` miscomputed client-side | Assert `sqrt(Σ w²)` against a known case |
| `rankScore` comes back `null` | No rerank ran — no `rankBy`, or every term had `weight: 0` | Give at least one term a non-zero weight |
| Vocabulary "does not exist in enum" | No index run since it was created | `igniteDiscoApi(stacks: opensearch)`, wait for `complete` |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,10 @@ it just has no relation to taste. The check in step 6 is the only thing that cat
`igniteDiscoApi` rebuilds the tenant's Discovery index and materializes the vectors. Run it after **any**
change to vocabularies or taste entries, not only the first time.

**Ignite last.** Run it once the shapes exist, each with at least one component, and the content is
published, not as an early setup step. A tenant with no shapes that Discovery can index is refused: the
task ends in `error`.

**Pass `stacks: opensearch`.** The argument is optional in the schema but it selects which index stack
is built, and vector ranking is only served from the `opensearch` stack — an ignition without it
produces a working Discovery index with no vectors in it, and no error.
Expand Down Expand Up @@ -284,12 +288,24 @@ query Task($id: ID!) {
bulkTask(id: $id) {
... on BulkTask {
id
status
} # pending, started, complete, error
status # pending, started, complete, error
info # on error, info.traces says why
}
}
}
```

**`error` is blocking.** Read `info.traces`, fix what it names and only then ignite again. Re-running an
ignition that failed, unchanged, fails the same way. Before reporting success, probe Discovery itself:

```graphql
{
__typename
}
```

Anything but `{"data":{"__typename":"Query"}}` means the tenant is not serving, whatever the task said.

From then on the tenant's Discovery schema includes `context`, `rankBy` and `nearestTo`, and every
vocabulary you created becomes a value of the `TenantVocabularyIdentifier` enum. **A vocabulary is only
a valid enum value after the next index run** — until then, queries referencing it fail schema
Expand Down
Loading