Refresh the ontology vector index when the editor creates or relabels a class or property, before alignment runs by WaylandYang · Pull Request #956 · deeplethe/utopia · GitHub
Skip to content

Refresh the ontology vector index when the editor creates or relabels a class or property, before alignment runs - #956

Merged
WaylandYang merged 2 commits into
devfrom
claude/festive-hamilton-599c0e
Sep 26, 2026
Merged

WaylandYang merged 2 commits into
devfrom
claude/festive-hamilton-599c0e

Conversation

@WaylandYang

Copy link
Copy Markdown
Contributor

Problem

The four editor handlers in ontology_routes.rs (create_entity_type, update_entity_type, create_relation_type, update_relation_type) write the element and enqueue align_types or align_phrases, but nothing refreshes the ontology vector index.

phrase_alignment::shortlist picks candidates by vector whenever a signature has more than ten structural candidates, and type_alignment::candidates_for retrieves candidate classes by vector. An element without a vector never reaches the model, so the 0053 fingerprint of the shapes it could bind does not change and they are never re-decided. Only OWL import, bootstrap, type resolution, build_proposals and, since 0061 cut 1.1, the agent's adoption refreshed the index. The bordered_by case seen on a real base was this gap on the adoption path; the editor had the same one.

Change

  • One shared step for the four handlers (reindex_then_align): queue embed_ontology with a two second debounce, then the alignment job with its existing five second one. The embed job is deduplicated against queued copies only (enqueue_unless_queued_after), not running ones: a refresh already running has read its stale set and cannot see this edit, so a second copy must follow it. Queuing rather than embedding in place keeps a batch of edits on the ontology page to one embedding call.
  • A changed label or description needs no extra bookkeeping. types_needing_embedding judges staleness by comparing the stored embedded_text with label plus description, so an update whose text changed is re-embedded by the same job, and an update that only touched domain or range is a no-op for it.
  • Both aligners wait while embed_ontology is queued or running for the base, the way the phrase aligner already waits for the kind-word round: they re-enqueue themselves thirty seconds later, at most one queued copy. The debounce alone would not order the jobs. The order is now vectors, then kind words, then phrases. OWL import queues the same job, so a large import is covered too.
  • Docs: half a paragraph in docs/design/ontology.md; ADR 0061's status line notes the editor path.

Verification

Run against a local Postgres 16 with pgvector via UTOPIA_DATABASE_URL.

  • New test a_property_created_in_the_editor_has_its_vector_before_phrases_are_aligned in phrase_alignment_tests.rs, scripted chat and embedding endpoints, calling the real handlers:
    1. after create_relation_type, the base's queue is embed_ontology then align_phrases, and the row has no vector;
    2. running the aligner while the refresh is queued makes no model call and re-queues the alignment behind it;
    3. running the embed job body puts the property in the vector table with label\ndescription as the embedded text; the aligner then asks the model and the new property is among the candidates;
    4. update_relation_type with a new description queues the refresh again, and it re-embeds exactly that row.
  • New test kind_word_alignment_waits_while_the_ontology_index_is_refreshing in type_alignment_tests.rs, mirroring the existing kind-word wait test.
  • cargo fmt, cargo clippy --all-targets -- -D warnings clean; cargo test -p utopia-server: 457 passed.

🤖 Generated with Claude Code

WaylandYang and others added 2 commits September 26, 2026 22:38
… a class or property, before alignment runs

The four editor handlers (create and update for classes and for
properties) wrote the element and enqueued `align_types` or
`align_phrases`, but nothing refreshed the ontology vector index. The
phrase aligner picks its shortlist by vector whenever a signature has
more than ten structural candidates, and the kind-word aligner retrieves
candidate classes by vector, so an element without a vector never
reached the model; the 0053 fingerprint of the shapes it could bind did
not change and they were never re-decided. Only OWL import, bootstrap,
type resolution, build_proposals and (since 0061 cut 1.1) the agent's
adoption refreshed the index; the `bordered_by` case on a real base was
this gap on the adoption path, and the editor had the same one.

The handlers now share one step: queue `embed_ontology` with a two
second debounce, then the alignment job with its existing five second
one. The embed job is deduplicated against queued copies only
(`enqueue_unless_queued_after`), not running ones: a refresh already
running has read its stale set and cannot see this edit, so a second
copy must follow it. A changed label or description is covered without
any extra bookkeeping, because the index judges staleness by comparing
the stored text with label plus description.

The debounce alone does not order the two jobs, so both aligners now
wait while an `embed_ontology` job for the base is queued or running,
the way the phrase aligner already waits for the kind-word round: they
re-enqueue themselves thirty seconds later, at most one queued copy. The
order is therefore vectors, then kind words, then phrases. This also
covers OWL import, which queues the same job.

Tests, scripted model and real Postgres: a property created through
`create_relation_type` leaves the embed job queued ahead of the
alignment, the alignment defers without a model call while it is
pending, the property is in the vector table once the job body has run
and the model is then asked with it among the candidates; an update that
changes the description queues the refresh again and re-embeds exactly
that row. The kind-word aligner has the matching wait test.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Wayland Yang <wayland0916@gmail.com>
…ps both cut 1.1 and cut 3 clauses)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.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.

1 participant