Skip to content

Feat/v1.3#12

Merged
ThHanke merged 96 commits into
mainfrom
feat/v1.3
May 19, 2026
Merged

Feat/v1.3#12
ThHanke merged 96 commits into
mainfrom
feat/v1.3

Conversation

@ThHanke
Copy link
Copy Markdown
Owner

@ThHanke ThHanke commented May 19, 2026

Summary

Konclude v0.2.0 migration (main feature)

  • Migrated KoncludeReasoner from the deprecated NTriples string round-trip
    protocol to the v0.2.0 zero-copy binary ArrayBuffer wire protocol
    (loadTripleBuffer / realization / getInferredTripleBuffer)
  • Replaced postinstall Emscripten filter with structured {id, result/error}
    message routing; eliminated exit-code noise
  • Added postinstall asset copy script (scripts/copy-konclude-assets.mjs) so
    the WASM bundle is served from public/rdf-reasoner-konclude/ automatically
  • Filtered Konclude-echoed source triples from the inferred graph using an
    (s\0p\0o) key set — only genuinely new triples land in urn:konclude:inferred
  • Added COOP/COEP headers (same-origin / credentialless) to both server.js
    and vite.config.ts for SharedArrayBuffer support
  • Bumped rdf-reasoner-konclude to exact pin 0.2.0; version bumped to 1.3.0

Reasoner backend selector

  • Konclude (OWL 2 DL) is now the default backend; N3 (OWL-RL rules) remains
    available as the legacy/advanced option
  • Added backend radio buttons to ConfigurationPanel (OWL DL / N3 Rules);
    N3 ruleset picker shown only when N3 is selected
  • runReasoning MCP tool and rdfManager.runReasoning() accept
    reasonerBackend: 'konclude' | 'n3'; getCapabilities returns
    reasonerBackends: ['konclude', 'n3']
  • Fixed two-layer bug where backend selection had no effect: missing field in
    handleRunReasoning (React layer) and missing forwarding of reasonerBackend
    into reasoningRequest in the worker dispatch

SPARQL worker

  • Fixed Comunica QueryEngine failing inside Vite browser workers; rewired
    the worker bundle so Comunica loads correctly
  • queryGraph now returns prefix-shortened IRIs (ex:Alice not the full IRI);
    blank-node skolem IRIs mapped to _:bnode

Blank node skolemization

  • Blank nodes are content-hash skolemized to urn:vg:bnode: IRIs at store
    write time; deterministic across reloads
  • addTriple accepts blank-node IRI restrictions in OWL axioms

MCP tool improvements

  • loadRdf: improved validation; injects missing built-in @prefix declarations;
    permits both OWL restriction paths
  • addNode: accepts subjectIri as alias for iri
  • getNodeDetails: abbreviates returned IRIs using registered prefixes
  • expandNode: navigates canvas to the node after expanding
  • queryGraph: IRI shortening, ASK rejection, prefix injection, bare-IRI
    PREFIX normalisation

OWUI relay

  • Replaced MutationObserver dispatch with 500 ms fire-on-idle poll
  • Single injection path: setContent + transaction event submit (no fallbacks)
  • Streaming detection via content-length growth + relay-idle (callQueue /
    isProcessing) instead of unreliable send-button state
  • Multi-burst flushing: waitRelayFlush uses 45 s stability window
  • Relay popup BroadcastChannel scoped correctly via docker-dev origin

Demo & docs

  • Recorded Socratic pizza demo (OpenWebUI + Ontosphere side-by-side)
  • All six demo videos rebuilt with stable idle detection and caption timing
  • README.md reasoning section updated: Konclude as default, N3 as advanced
  • docs/solutions/ entries for fire-on-idle relay pattern and relay architecture

Tests

  • New reasoning_konclude_demo.test.ts: verifies Konclude infers
    ex:Person subClassOf owl:Thing and direct hierarchy edges; documents
    echo count (12 echoed, 15 genuinely new)
  • All five N3 reasoning tests pinned to reasonerBackend: 'n3'
  • Fixed nodes.test.ts (missing getNamespaces mock) and graph.test.ts
    (assertions updated for prefix-shortened IRIs)
  • Removed un-runnable NodePropertyEditor.autopopulate.test.tsx
    (it.skip; jsdom cannot drive Radix UI combobox portals)
  • 241 tests passing, 0 failing, 0 skipped

Thomas Hanke and others added 30 commits May 5, 2026 07:11
Vite's Rollup worker bundler wraps CJS modules in require_libXXX stubs
that break Comunica v5's circular actor/bus/mediator dep graph, causing
every queryGraph call to fail at runtime despite unit tests passing.

Two root causes identified via live browser reproduction:
  1. node:diagnostics_channel (lru-cache) — Vite's browser-external
     Proxy stub returns undefined for all property accesses, so
     (0, L.channel)("lru-cache:metrics") throws at init time.
  2. `global` (promise-polyfill) — not defined in browser workers;
     cascades into broken __commonJS wrappers for downstream actors.

Fix:
  - Add vite-plugin-worker-comunica.ts: intercepts the Comunica import
    in worker.plugins and substitutes an esbuild-compiled flat ESM bundle
    (same strategy as InProcessWorker tests that already pass).
  - optimizeDeps.esbuildOptions: stub node:diagnostics_channel + define
    global→globalThis so the dep-optimizer pre-bundle is also clean.
  - e2e/sparql-worker.spec.ts: browser regression test covering INSERT,
    SELECT, CONSTRUCT, DELETE, and malformed-SPARQL error path.
fix(worker): make Comunica QueryEngine work in Vite browser worker
isAiStreaming() rewritten with four UI-agnostic signals:
1. input disabled/aria-busy (textarea UIs)
2. visible spinner by class name
3. stop/abort button by aria-label OR exact text content
4. MutationObserver fallback — DOM silence for 1.5 s = done;
   catches icon-only stop buttons and thinking phases where
   none of the above signals fire (e.g. OWUI qwen3)

Removed #send-message-button dependency and type=submit fallback
that matched all OWUI buttons and caused false negatives during
the qwen3 thinking phase.

help() now opens with a full SILENTLY IGNORED format list covering
every common wrong format, including the method="toolName" mistake
qwen3 made on first attempt.
Adds everything needed to run an interactive OWUI relay session
via Playwright MCP browser without external Node processes
(BroadcastChannel must share the MCP browser process).

- .playwright/: fresh-setup, send-starter, send-task, send-pizza
  send-task is a generic injection wrapper; send-pizza is a thin
  wrapper over it
- e2e/demo-openwebui-pizza.spec.ts: recorded demo spec with
  silent-ignore warning in SYSTEM_PROMPT
- playwright.openwebui.config.ts: Playwright config for OWUI tests
- public/demo-stage-owui.html: side-by-side OWUI + Ontosphere stage
- scripts/owui-auth.mjs: saves auth cookie to .playwright/owui-auth.json
- docs/owui-relay-session.md: comprehensive session setup guide
- CLAUDE.md: pointer to the session guide
- package.json: demo:owui:auth and demo:owui:video scripts
- .gitignore: exclude owui-auth.json (contains session tokens)
…akes

Make isAiStreaming() fully generic — no chat-UI-specific selectors.
Now uses four layered signals: textarea disabled/aria, visible spinner
classes, stop-button aria-label/text, and MutationObserver DOM-silence
fallback (STREAM_QUIET_MS=4000ms). Covers qwen3:8b icon-only stop button
and any UI without standard spinner markup.

Also add WRONG/RIGHT examples for queryGraph (sparql≠query) and
setViewMode (mode≠viewMode) to help() — both consistently misused by qwen3.
MutationObserver fired mid-stream causing race conditions with thinking
models (qwen3). Fire-on-idle polls every 500ms, waits for isAiStreaming()
= false, reads the complete page text, extracts all tool calls at once.

- relay: replace MutationObserver block with idlePoll() (500ms interval)
- relay: pre-seed dispatchedSigs before poll starts to skip INSTR examples
- relay: isAiStreaming() now checks spinners, stop-word buttons, DOM quiet
- playwright: send-starter uses Good Response button (not __vgIsStreaming)
- playwright: fresh-setup selects mistral-small3.1 (no thinking blocks)
…o bookmarklet plugin

doSubmit() deadlocked: content injection causes DOM mutations → isAiStreaming()
returns true for STREAM_QUIET_MS (4s) → submit poll blocks. By the time injectResult
is called we already confirmed model idle via fire-on-idle; no need to re-check.

vite-plugin-bookmarklet: addWatchFile so virtual module invalidates on source change.
…RESPONSE] UUID prefix

insertFromPaste (appends) was promoted to Path 1 to sync Svelte for btn.click().
But OWUI pre-fills TipTap with [RESPONSE] <uuid> internal state in some conditions;
append produces [RESPONSE] UUID + result text, model echoes the UUID.

setContent(text, true) REPLACES all editor content. Enter-based submit reads TipTap
state directly so Svelte async sync no longer matters. Path order:
  1. setContent / raw PM dispatch (replace — correct for any pre-filled state)
  2. insertFromPaste (append — fallback for non-TipTap contenteditable)
  3. insertText (last resort)
…textarea

Clear TipTap before insertFromPaste so [RESPONSE] UUID pre-fill is gone
before append — paste to empty = clean insert, Svelte syncs, button enabled.

Enter fires only for textarea UIs; TipTap (OWUI) maps Enter to new paragraph
not submit — btn.click() is the correct path after Svelte syncs via paste.
hasContent fired submitInput on the first synchronous poll — before
setContent's microtask had synced Svelte's prompt store. Button was still
disabled → no click → silent fail or premature fire with stale content.

doSubmit now polls btnEnabled only, giving the ~50 ms microtask queue
time to flush. insertFromPaste (and the pre-clear step) removed; setContent
atomically replaces any [RESPONSE] UUID pre-fill and is the sole inject path.
…nt submit

Remove all fallback injection paths (raw PM dispatch, insertFromPaste,
insertText). One path: setContent(text, true) to atomically replace content,
tiptap.on('transaction') to submit once Svelte's onTransaction sync completes.
300 ms safety timeout handles edge cases where the event never fires.

Eliminates the doSubmit polling loop and the entire multi-path injection chain
that caused UUID prefix bugs and premature submit races.
Two fixes:

1. Text-stability fallback in idlePoll: track innerText change time
   separately from DOM mutation rate. If text unchanged for STREAM_QUIET_MS,
   consider idle regardless of background DOM mutations. Fixes FhGenie where
   continuous UI updates (progress indicator, animations) kept isAiStreaming()
   permanently true, blocking all tool-call dispatch.

2. Delta extraction: process only text.slice(lastIdleText.length) per poll
   instead of full page text + global dispatchedSigs. Same call in a later
   turn fires again ("call it again" works). Per-turn Set still deduplicates
   duplicate calls within a single response.
…utton

isAiStreaming() signal 3: add SVG path prefix matching for icon-only stop
buttons. FhGenie (Fluent UI) replaces the send arrow with a Dismiss24Regular
X icon (path 'M8.22 8.22') during generation — no aria-label or text, so
stop-word matching missed it.

submitInput(): extend send button heuristic to also match class name containing
'send' or 'submit'. FhGenie's button has class _questionInputSendButton_* with
no matching text or aria-label.
isAiStreaming() rewrite:
- Primary signal: find send/submit button (by id, tree-climb, aria/text/class)
- If send button found: check for X icon (M8.22 SVG path = Fluent UI Dismiss,
  FhGenie's stop affordance) or disabled-with-content (OWUI pattern)
- If send button not found: fallback to aria-disabled/busy, spinners, stop words
- Remove DOM mutation rate signal (signal 4) — caused permanent false positives
  on FhGenie due to background UI updates

idlePoll rewrite:
- Remove text-stability complexity (no longer needed)
- On idle: full page text scan with global dispatchedSigs
- Pre-seed dispatchedSigs at inject to skip pre-existing calls
- Clean and predictable: one mechanism, no delta/stability bookkeeping
React re-renders asynchronously after the native value setter + input event.
Calling submitInput() synchronously hits a still-disabled button (React hasn't
re-rendered yet) and Enter fires before the value is in React state — both
ignored. 50ms timeout lets React flush its state update before clicking.
Injecting while OWUI transitions out of streaming state causes a race:
setContent fires before the editor accepts input, pasting lands too early.

Poll isAiStreaming() (up to 10s) before calling setContent. Safe now that
signal 4 (DOM mutation rate) is removed — content injection no longer
triggers false positives in isAiStreaming().
… input

idlePoll was firing on every idle tick including while user types or pastes.
The starter prompt contains JSON-RPC example calls — relay dispatched them
immediately on paste before the AI ever responded.

Track prevStreaming: only dispatch on the single tick where streaming just
ended (true→false transition). User messages arrive while always-idle so
the transition never fires for them.
getPageText() uses a TreeWalker to collect body text skipping the input
element subtree. Tool calls typed or injected into the input (INSTR examples,
relay result text) can never be dispatched.

All three scan sites updated: idlePoll, pre-seed, and waitForIdle.
getPageText() now strips the chat input's current content via string
subtraction — prevents relay from dispatching tool calls the user typed
or pasted into the input field.

prevStreaming transition guard removed: getPageText() is the sole
protection against dispatching user content, so the streaming→idle
edge-detect is redundant and prevents legitimate re-dispatch after
multi-turn conversations.
…T walker

String subtraction was fragile — TipTap reformats pasted content (strips
backticks, changes whitespace) so inp.innerText != body.innerText substring.

The previous TreeWalker used SHOW_TEXT only: FILTER_REJECT on a text node
is treated as FILTER_SKIP (no subtree effect), so the whole input was
included. With SHOW_ELEMENT|SHOW_TEXT, FILTER_REJECT on the input element
correctly skips its entire subtree.

Textarea value is never in body.innerText, so TEXTAREA inputs skip the
walker entirely.
Replace transaction-event+setTimeout(300) submit trigger with a
waitSubmit loop that polls findSendButton() every 100ms, requiring
2 consecutive enabled ticks before clicking.

Also simplify isAiStreaming() send-button branch: any disabled state
means not ready, dropping the "disabled AND has content" heuristic
that wrongly returned idle during model thinking with empty input.

Together these ensure:
- Pre-paste (waitReady): blocks while button is disabled (thinking phase)
- Post-paste (waitSubmit): waits for Svelte to re-enable the button
  after setContent, not for the TipTap transaction which fires before
  the framework has flushed its render cycle

Co-Authored-By: Thomas Hanke <thomas.hanke@iwm.fraunhofer.de>
…pty input

FhGenie disables its send button when the input is empty (not just during
generation). The previous change (disabled→streaming) broke FhGenie dispatch.

New logic for the disabled branch:
- enabled → idle (return false immediately)
- disabled + has content → streaming (OWUI pattern)
- disabled + empty → fall through to spinner/aria signals

OWUI thinking is caught by the [class*="thinking"] spinner selector.
FhGenie idle (disabled, empty, no spinner) correctly returns false.
… dispatch

isAiStreaming() send-button branch: revert fallthrough for disabled+empty.
FhGenie disables the send button when input is empty (not just during
generation), and its Stream-mode toolbar elements match [class*="streaming"],
causing the fallback spinner check to return true indefinitely.
Correct logic: disabled+empty = idle (return false directly, like original).
OWUI during generation replaces the send button with a stop button, so
findSendButton() returns null and the fallback stop-word/spinner path handles it.

idlePoll: require 2 consecutive idle ticks (1 s stable) before extracting.
Prevents user starter-prompt examples from being dispatched in the brief
window (~200ms) between user submit and model starting to stream.
Svelte needs more time to flush state after setContent and enable the
send button reliably. 2 ticks (200ms) was sometimes too short.
Adds getAssistantText() that queries [data-message-author-role="assistant"]
(OWUI), [data-role="assistant"], and aria-log fallbacks to restrict
getPageText() to AI responses only. Prevents dispatching tool-call examples
embedded in the user's starter prompt before the AI has responded.
Replaces the stableTicks heuristic with a loop that calls setContent,
waits 600ms for OWUI's post-stream annotation phase to complete, then
polls every 300ms checking isAiStreaming() before clicking submit.
Success is detected by TipTap editor.isEmpty — a cleared input means
OWUI accepted the message, no fixed tick count needed.
- relayBridge: log tool results/errors to console; fix unknown-tool path to
  emit console.error; add runLayout to toastLabel switch; rewrite
  buildCanvasSummary to use getNodes/getLinks instead of removed getGraphState
- .playwright/pizza-demo-setup.js: new — bootstrap OWUI relay session with
  format INSTR and Socratic Turn 0 starter
- .playwright/turn-driver.js: new — drive T1–T6 Socratic questions via
  __vgIsStreaming idle detection (replaces Good Response button polling)
- .playwright/session-log-start.sh: new — persistent session log aggregator
- fresh-setup.js / send-starter.js / send-pizza.js: model corrected to qwen3:4b
- e2e/demo-pizza-socratic.spec.ts: new demo video spec — operator asks Socratic
  questions, model discovers OWL classes → subClassOf → hasPart → layout
- docs/demo-scripts/pizza-ontology.md: recording guide with turn-by-turn
  expected tool calls and fallback nudges
- .gitignore: add debug.png
4 MB mp4 / 6 MB webm — Socratic pizza ontology session, 7 turns,
full class hierarchy + hasPart links + dagre-tb layout + introspection.
Thomas Hanke added 28 commits May 8, 2026 16:44
collect-demo-videos.mjs matched result dirs by longest spec name first,
preventing pizza-tutorial-chat from overwriting pizza-tutorial video.
…ects

clearInferred only wiped data but never triggered a canvas refresh, so
inferred links and decorations persisted after clearing. Fix re-emits all
data subjects so the onSubjectsChange handler removes inferred links and
refreshes element decorations through the existing update path.

MCP clearInferred tool now routes through the registered canvas callback
(same codepath as the UI button) when available, falling back to the bare
dataProvider call when the canvas is not mounted. Tests updated to cover
both paths and the error case.
qwen3:4b consistently passes subjectIri (inferring from addTriple
parameter naming). This alias prevents silent failures when the model
uses the wrong param name.
…cording

- Switch demo:video to build+warmup approach: start dev server, prime
  Vite dep-optimization cache with a headless page load, then record.
  Eliminates mid-test page reloads caused by Vite's first-run dep scan.
- Add workers:1 to playwright.demo.config.ts to prevent parallel-startup
  race conditions between browser instances sharing the same origin.
- Re-record all 6 demo videos with updated MCP tool names (addTriple)
  and relay/layout improvements from the skolemization branch merge.
12-minute live qwen3:4b session — 11-turn Socratic pizza ontology arc.
Idle-trimmed to 5.2 min (57% saved), 27 MB mp4.
After waitQuiet for the starter/help() cycle, check that at least one
relay result message ("[Ontosphere →") exists in the chat. If not, the
model failed to load or ignored the starter — throw immediately instead
of burning 11 turns recording an empty canvas.
…rt guard

- Track MCP call count via __demoMcpCallCount__ in appFrame (replaces
  unreliable DOM selector check for relay result messages)
- 13-min session, 1769 inferred triples — idle-trimmed to 3.4 min (74%)
…length stability

isAiStreaming() gives false negatives for qwen3 (send button stays enabled
during generation), causing the idle-poll to fire after just 1 s of apparent
quiet and dispatch tool calls from partial model output — multiple sends per
turn, hacked-apart Socratic conversation.

Replace with content-length-based stability: require 24 consecutive 500 ms
ticks (12 s) with identical getPageText() length before dispatching. Any
content change resets the counter, so thinking pauses never trigger early
dispatch. isAiStreaming() is intentionally not consulted.
…o block

T4 rewritten to explicitly list all 12 required triples (equivalentClass,
rdf:type owl:Restriction, owl:onProperty, owl:someValuesFrom) for each pizza
class. qwen3:4b was creating named restriction nodes but omitting onProperty
and someValuesFrom — OWL-RL cls-svf1 never fired, pizzas stayed unclassified.

shorten-idle.py: add no_cut_last flag (arg 7). When true, the last detected
freeze block is kept at full length rather than trimmed to digest_sec.
collect-demo-videos.mjs: pass no_cut_last=1 for openwebui-socratic so the
model's final classification response is not cut away in post-processing.
… way' claim; raise idle-cut threshold

- addTriple description: remove prohibition on blank-node restriction construction;
  document 4-call pattern (_:b0/_:b1 labels) with owl:Restriction emphasis and
  distinct-label-per-restriction warning (same label = same IRI = collapse)
- loadRdf description: 'only way' → 'simplest way'; add reference to addTriple
  alternative so both paths are documented and neither is forbidden
- mcp.json: sync addTriple + loadRdf descriptions to match relay manifest
- T4 Socratic question: replace 12-triple directive list with concept-led question
  that lets qwen3 choose addTriple or loadRdf guided by the updated manifest
- collect-demo-videos.mjs: raise min_freeze 8s → 20s to preserve qwen3 CoT thinking
  phases (hidden <details> blocks freeze visible chat for 20–60s; 8s was cutting them)

Validation: loadRdf Turtle path produces correct owl:Restriction nodes with onProperty
+ someValuesFrom; runReasoning classifies pizza1→SalamiPizza, pizza2→HawaiianPizza,
pizza3→MargheritaPizza via cls-svf1 + cax-eqc chain.
- addTriple description: replace BLANK NODE LIMITATION with 4-call blank-node
  restriction pattern (_:b0/_:b1 distinct labels, owl:Restriction not owl:Class)
- loadRdf description: add explicit no-@Prefix instruction with ex: pattern example
- Top-level manifest bullet: both restriction paths documented
- links.ts error message: no-@Prefix example in fallback Turtle hint
…@Prefix

- Step 6 abort guard: wait for mcpCallCount>0 before starting waitQuiet — avoids
  false abort when qwen3 CoT thinking (20-60s in <details>) exhausts the 15s
  silence window before help() fires
- sanitizeTurtle: strip pre-loaded @Prefix lines (often malformed by qwen3 with
  spaces: `< http://...>`) and fix whitespace inside angle brackets before parse
- Applies sanitize before validateTurtleSnippet so helpful error hints still work
pizza1→SalamiPizza, pizza2→HawaiianPizza, pizza3→MargheritaPizza
all classified by OWL-RL cls-svf1 via blank-node equivalentClass restrictions.
SELECT rows and CONSTRUCT triples now use prefix:local notation
(e.g. ex:SalamiPizza, owl:NamedIndividual) instead of full IRIs.
Blank-node skolem IRIs (urn:vg:bnode:*) are shown as _:bnode.
Makes relay-injected results readable in the OWUI chat.
…on timing

- waitRelayFlush: add 45s stability window so multi-burst relay batches
  (spaced 28–38s apart) are fully drained before the next turn is sent;
  previous single-idle-detection caused HawaiianPizza/MargheritaPizza
  restrictions to be sent after the spec had moved on
- Turn loop restructured: AFTER_CAPTIONS + TURN_TOPICS both fire within 7s
  of relay flush so they survive shorten-idle's 20s min_freeze threshold
- T8/T9 split: dedicated turns for pizza2 and pizza3 prevent qwen3 from
  duplicating pizza2 edges when building pizza3
- getNodeDetails: abbreviate IRI types/predicates/objects via abbreviateIri
  so model sees ex:Pizza not http://example.org/Pizza
- Re-recorded demo: all 3 pizzas classified (SalamiPizza, HawaiianPizza,
  MargheritaPizza), captions preserved in compressed output
- Exact version pin (0.2.0) forces re-audit of inlined codec on any future upgrade
- copy-konclude-assets.mjs copies worker.js/konclude.mjs/konclude.wasm from
  node_modules/rdf-reasoner-konclude/dist/ to public/rdf-reasoner-konclude/
  on every npm install, eliminating manual drift
- Chains with existing patch-package hook in postinstall
- public/rdf-reasoner-konclude/ added to .gitignore (auto-generated, ~5 MB WASM)
…e v0.2.0 binary protocol

Replace the NTriples round-trip with zero-copy ArrayBuffer transfer:
- Inline InternTable/encodeToBuffers/decodeBuffers from ts/intern.ts (pinned 0.2.0)
- Replace _call rest-params with explicit (method, args[], transfer?) signature
- Rewrite reason() to use loadTripleBuffer → realization → getInferredTripleBuffer
- Remove _serializeToNTriples() and all NTriples-related code
- Remove Emscripten pthread-exit ErrorEvent filter (not needed in v0.2.0)
- Bump worker URL cache-bust from v=15 to v=16
…raph

Konclude returns its full taxonomy including triples already in the source.
KoncludeReasoner.reason() now builds a (s,p,o) key set from the source quads
before reasoning and skips any decoded triple whose key is already present.
Only genuinely new inferences land in urn:konclude:inferred.

Also fix pre-existing unnecessary \@ escape in regex (line 729).
… + MCP param

- appConfigStore: add reasonerBackend field (default 'konclude') + setReasonerBackend
- ConfigurationPanel: Reasoner Backend radio (OWL DL / N3 Rules); ruleset picker
  only shown when N3 is selected
- rdfManager.impl/workerProtocol: runReasoning() accepts reasonerBackend, reads
  from config if not passed, forwards to worker
- MCP runReasoning: reasonerBackend param + updated description + capabilities list
- server.js + vite.config.ts: add COOP/COEP headers (required for SharedArrayBuffer)
- README: update reasoning section — Konclude default, N3 as legacy/advanced
…onent test

- nodes.test.ts: add getNamespaces mock; update assertions for abbreviated IRIs
- graph.test.ts: update SELECT/CONSTRUCT assertions for prefix-shortened IRIs
- Remove NodePropertyEditor.autopopulate.test.tsx (it.skip; jsdom can't drive
  Radix UI combobox portals — belongs in Playwright if ever revived)
@ThHanke ThHanke merged commit b475541 into main May 19, 2026
2 checks passed
ThHanke added a commit that referenced this pull request May 21, 2026
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