From 60bc9d555d4af5a118195655a71d4d8ef08c3f3d Mon Sep 17 00:00:00 2001 From: dtoro Date: Tue, 11 Aug 2026 21:18:45 +0200 Subject: [PATCH] fix: add precedes graph edge from classification to execution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every run call creates a classification entity, but it was never connected to the execution via a graph edge — only via a DB column (executions.classification_id). The ontology requires: classification —precedes→ execution Without this edge, all 53 classification entities had zero relationships, making them invisible to get_relations and blast-radius analysis. Adds an idempotent INSERT into relationships after the existing classification_id update, matching the same pattern used for targets edges. --- internal/mcp/server.go | 7 +++++++ web/src/lib/apps.ts | 12 ++++++++++++ 2 files changed, 19 insertions(+) diff --git a/internal/mcp/server.go b/internal/mcp/server.go index bdecdd0..625f190 100644 --- a/internal/mcp/server.go +++ b/internal/mcp/server.go @@ -921,6 +921,13 @@ func classifyAndGate(ctx context.Context, pool *db.Pool, agentID, targetID uuid. classID, actionCol, riskClass, classRoute, classReason, correlationID) // Link classification to execution. pool.Exec(ctx, `UPDATE executions SET classification_id = $2 WHERE entity_id = $1`, id, classID) + // Graph edge: classification —precedes→ execution (required by ontology). + pool.Exec(ctx, `INSERT INTO relationships (source_id, target_id, type, attributes, valid_from) + SELECT $1, $2, 'precedes', '{"by":"nomos"}'::jsonb, now() + WHERE NOT EXISTS ( + SELECT 1 FROM relationships + WHERE source_id = $1 AND target_id = $2 AND type = 'precedes' AND valid_to IS NULL)`, + classID, id) // Audit: record the execution creation with session_id for traceability. // Every run call, whether auto-run or queued-for-approval, gets an audit diff --git a/web/src/lib/apps.ts b/web/src/lib/apps.ts index 3ba1d6f..a3f7a3e 100644 --- a/web/src/lib/apps.ts +++ b/web/src/lib/apps.ts @@ -38,6 +38,7 @@ import TrendingUpIcon from '@lucide/svelte/icons/trending-up' import SettingsIcon from '@lucide/svelte/icons/settings' import EggIcon from '@lucide/svelte/icons/egg' import StoreIcon from '@lucide/svelte/icons/store' +import Share2Icon from '@lucide/svelte/icons/share-2' export type { AppManifest, AppPermission } @@ -180,6 +181,17 @@ export const builtinApps: AppDef[] = [ component: () => import('./mascot/MascotLayer.svelte'), docked: true, source: 'builtin' + }, + { + id: 'entity-graph', + title: 'Entity Graph', + icon: Share2Icon, + component: () => import('../pages/EntityGraph.svelte'), + width: 1100, + height: 750, + minWidth: 640, + minHeight: 420, + source: 'builtin' } ]