$ cat workspace-template.yaml

O

Ontology Node Test Bench

// A test bench that instantiates EVERY ontology node (search x3 for entity/schema/hybrid, traverse x2, entity-360, kb-search -> from-documents bridge, merge RRF, and a self-cleaning create/set/link/delete write chain). The instruction drives the agent to configure, run, and evaluate each node against pass/fail criteria and produce a consolidated report. Use to smoke-test the whole ontology stack after a deploy.

utility
#ontology#test#diagnostics#knowledge-graph#test-bench

// Canvas Preview

canvas.flow

// Instruction

instruction.md

Ontology Node Test Bench

Overview

This workspace instantiates EVERY ontology node so you can exercise each one against the LIVE governed graph and judge its result. Some nodes appear MULTIPLE times to cover their variants (Search is here three times — entity, schema, hybrid modes; Traverse twice — different direction/depth). You are the test driver: configure each node, run it, read its output, and mark it PASS / WARN / FAIL against the criteria below, then present one consolidated report. Most of the bench is READ-ONLY; the write nodes at the bottom run only if the user opts in — they form a self-contained island (two throwaway objects plus a link between them) that fully tears itself down afterward and can never touch a real entity.

The nodes (and what each proves)

  • Search · entity mode (ontology-search, mode=entity) — name-first lexical/kNN search. Output results + count.
  • Search · schema mode (mode=schema) — matches the query to relationship/theme TYPES, returns their instances with matchedVia. Output results.
  • Search · hybrid mode (mode=hybrid) — both arms, RRF-fused.
  • Entity 360 (ontology-entity-360) — full profile of ONE object id. Inputs objectId, maxNamesPerType. Outputs entity, summaryText, linkCounts.
  • Traverse · both, depth 1 and Traverse · outgoing, depth 2 (ontology-traverse) — walk links from an object. Inputs objectId, linkType, direction, depth. Output result.
  • KB Semantic Search (kb-semantic-search) — vector search over document chunks. Output documentIds (WIRED into From Documents), chunkTexts.
  • From Documents (bridge) (ontology-from-documents) — documents → the ontology objects appearing in them. documentIds is wired from KB Semantic Search. Outputs entities, links, truncated.
  • Merge Ranked Lists (RRF) (merge-ranked-lists) — fuses listA (wired from Search·entity results) and listB (wired from From Documents entities). Outputs items, itemsText, topId.
  • Create Object ×2 / Set Property / Create Link / Delete ×3 (ontology-create-object / -set-property / -create-link / -delete) — the write path, built as a self-contained island: two throwaway objects (A, B), a link A→B between them, then three Deletes that remove both objects and the link. No field here ever references a real entity.

Phase 1 — Configure

  1. Make ONE requestUserDecision bundling: (a) an entity NAME that exists in the graph to probe (default "MAECI"); (b) a relationship/theme question (default "chi ha promulgato le leggi | who promulgated the law"); (c) run the WRITE nodes too? (default No — read-only). Keep a free-text box last. Do not prompt again after this.
  2. Use nodeFinder to get the fresh ids of ALL nodes — the template-preview ids are stale after cloning.
  3. Apply with ONE batchUpsertNodes: set query on Search · entity mode to the entity NAME; set query on Search · schema mode, Search · hybrid mode, and KB Semantic Search to the relationship/theme question. Replace every {{query}} placeholder. Leave id/type inputs (Entity 360 objectId, Traverse objectId/linkType, the write nodes) empty for now — you fill them from earlier outputs in later phases.

Phase 2 — Run the READ nodes and evaluate

Run each with nodeExecutor, read with getNodeOutput, and record a verdict.

A. The three Search variants (run all three):

  • entity — PASS if count > 0 and the top result is a NAMED entity matching the probe; WARN if empty; FAIL on error. Keep the top result's id and typeId for later.
  • schema — PASS if results is non-empty AND every result carries matchedVia.mode = "schema". Then inspect the TOP result's matchedVia.typeName and typeScore: a typeScore around 0.7–0.9 is a healthy semantic (kNN) match; a typeScore around 7 means a LEXICAL-noise type won (a known regression) — mark WARN and name the offending type. FAIL if no result carries matchedVia.mode = "schema".
  • hybrid — PASS if count > 0.

B. Entity 360 + Traverse (need an id from Search·entity):

  • Set Entity 360 objectId to the top entity id from Search·entity (batchUpsertNodes), run it. PASS if entity is populated and linkCounts lists link types. Note a link-type NAME from linkCounts (or from entity's links) — you need it for Traverse.
  • Set both Traverse nodes' objectId to the same id and linkType to the link-type name you found; run both. PASS if result returns (endpoints may be empty — an isolated entity is still a valid, non-error result; say so). Compare depth-1 vs depth-2 reach.

C. Bridge pipeline (KB → From Documents → Merge — wired, so run downstream and upstream resolves):

  • Run KB Semantic Search. PASS if documentIds is non-empty; WARN if empty (nothing in the KB matched — note it).
  • Run From Documents. PASS if entities is non-empty; WARN if empty (the matched documents mention no accessible objects — still valid). Note truncated.
  • Run Merge Ranked Lists. PASS if items/itemsText are populated and topId is set. This proves the RRF fusion of the lexical (listA) and bridge (listB) arms.

Phase 3 — Write nodes (ONLY if the user opted in)

Skip this whole phase if writes were declined; mark every write node SKIPPED.

How the safety actually works — read this before touching a write node. A wired edge only fills an input the node left EMPTY; a value you TYPE onto a node WINS and the wire is ignored. So this island is safe ONLY because you leave every object-id and link-id field BLANK — the wires then route each write onto the throwaways created here. HARD RULE: never type an object id or link id taken from a Search / Entity 360 / Traverse / Merge / From-Documents result onto ANY write node. The ONLY fields you set in this phase are TYPE ids (objectTypeId, linkTypeId) — types are catalog entries, not real entity instances, so setting them is safe.

The island: Create Object A and Create Object B mint two throwaways; Set Property patches A; Create Link joins A→B; Delete ×3 removes A, B, and the link. Every object/link id flows through a wire, so nothing here can reach a real entity.

  1. Create Object · throwaway A — set objectTypeId to a typeId you kept from Search·entity's results. Leave identityKey at its fixed value ontology-testbench-throwaway; leave properties at the default. Run. PASS if an id comes back.
  2. Create Object · throwaway B — same idea; set objectTypeId to any real object-type id (reusing A's is fine). Leave identityKey at its fixed value ontology-testbench-throwaway-b. Run. PASS if an id comes back. (B exists so Create Link has a second distinct throwaway endpoint — no real entity is ever needed.)
  3. Set Property — leave targetId EMPTY (wired from A); set only properties. Run. PASS if it returns A's throwaway id (NOT a real entity id).
  4. Create Link — leave BOTH sourceObjectId and targetObjectId EMPTY (wired from A and B). Set ONLY linkTypeId to a real link-type id (from Entity 360 if available, else mark SKIP). Run. PASS if it returns an id; note downgrade_refused.
  5. Delete · throwaway object A, Delete · throwaway object B, Delete · throwaway link — leave every targetId EMPTY (each is wired: to A, to B, and to Create Link's link id). Run all THREE, EVEN IF an earlier write step failed (cleanup). PASS if each returns a deleted_at. After these three the island leaves the graph exactly as found. NEVER delete anything else.

Phase 4 — Report

Run verification, then present ONE markdown table: a row per node instance (name, PASS/WARN/FAIL/SKIP, and the one-line reason/observation). Lead with an overall verdict (FAIL if any FAIL, else WARN if any WARN, else PASS). Call out specifically: any schema result that matched a lexical-noise type (high typeScore), any empty bridge/KB result (with the likely cause), and — if writes ran — confirm both throwaway objects and the test link were deleted. In followUp, offer to re-run with a different probe query, or to run the write nodes (they self-clean). No write confirmation is needed beyond the Phase 1 opt-in, since the three Deletes always tear down everything Create made.

// Dependencies

requirements.py
1from node import OntologySearch  # v1.4.02from node import OntologyEntity360  # v1.0.33from node import OntologyTraverse  # v1.0.04from node import KBSemanticSearch  # v1.4.05from node import OntologyFromDocuments  # v1.0.06from node import MergeRankedLists  # v1.3.07from node import OntologyCreateObject  # v1.0.08from node import OntologySetProperty  # v1.0.09from node import OntologyCreateLink  # v1.0.010from node import OntologyDelete  # v1.0.0

// Variables

variables.yaml
1query:2  type: undefined3  label: "Test Query"4  description: "The probe query. Entity-mode search wants a NAME; schema/hybrid + KB search want the RELATION/THEME words. The instruction refines it per node. Default probes both."5  required: undefined