From e030c59c3ec95cf2786e48ecaebf40a472b29c17 Mon Sep 17 00:00:00 2001 From: Victor Soria Date: Wed, 6 May 2026 23:19:49 -0500 Subject: [PATCH 1/2] fix(model-editor): resolve relationship source/target names for MCP-created elements Elements created via MCP use their UUID as the relationship source/target reference, while AOEF-imported elements use their source_id (the original ArchiMate identifier). Index elementsById by both id and source_id so lookups work in both cases. --- cmd/archipulse/ui/src/routes/ModelEditor.svelte | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd/archipulse/ui/src/routes/ModelEditor.svelte b/cmd/archipulse/ui/src/routes/ModelEditor.svelte index 079340f..0085b16 100644 --- a/cmd/archipulse/ui/src/routes/ModelEditor.svelte +++ b/cmd/archipulse/ui/src/routes/ModelEditor.svelte @@ -70,7 +70,10 @@ let relSearch = ''; let activeRelType = ''; // '' = all - $: elementsById = Object.fromEntries(elements.map(e => [e.source_id, e])); + $: elementsById = Object.fromEntries([ + ...elements.map(e => [e.id, e]), + ...elements.filter(e => e.source_id).map(e => [e.source_id, e]), + ]); $: relTypes = [...new Set(relationships.map(r => r.type))].sort(); From 1e51166d711f385634c04c33f2bf4fbf5c00cfdc Mon Sep 17 00:00:00 2001 From: Victor Soria Date: Wed, 6 May 2026 23:25:43 -0500 Subject: [PATCH 2/2] fix(exporter): correct AOEF export for MCP-created workspaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two issues when exporting workspaces created via the MCP server: 1. Missing model identifier: workspaces created via MCP have no model_identifier in the DB. Archi requires the attribute — fall back to 'id-'. 2. Broken relationship references: MCP stores element UUIDs in source_element / target_element, while AOEF-imported workspaces store the ArchiMate source_id. Build a UUID->source_id map and translate before writing the XML so references always point to the element identifier used in the block. --- internal/exporter/loader.go | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/internal/exporter/loader.go b/internal/exporter/loader.go index 84c7aa0..bab901f 100644 --- a/internal/exporter/loader.go +++ b/internal/exporter/loader.go @@ -94,10 +94,13 @@ func LoadModel(db *sql.DB, workspaceID uuid.UUID) (*parser.Model, error) { return nil, fmt.Errorf("load diagrams: %w", err) } - // Load model identifier. + // Load model identifier — fall back to a deterministic value when absent. var modelIdentifier string _ = db.QueryRow(`SELECT COALESCE(model_identifier, '') FROM workspaces WHERE id = $1`, workspaceID).Scan(&modelIdentifier) + if modelIdentifier == "" { + modelIdentifier = "id-" + workspaceID.String() + } m := &parser.Model{Identifier: modelIdentifier, Name: ws.Name} @@ -172,6 +175,14 @@ func LoadModel(db *sql.DB, workspaceID uuid.UUID) (*parser.Model, error) { return nil, err } + // Build a UUID → source_id map for resolving relationship references created + // by the MCP server (which stores element UUIDs in source_element/target_element + // rather than the ArchiMate source_id used by AOEF-imported workspaces). + uuidToSourceID := make(map[string]string, len(elems)) + for _, e := range elems { + uuidToSourceID[e.ID.String()] = e.SourceID + } + // --- Elements --- for _, e := range elems { m.Elements = append(m.Elements, parser.Element{ @@ -217,11 +228,19 @@ func LoadModel(db *sql.DB, workspaceID uuid.UUID) (*parser.Model, error) { // --- Relationships --- for _, r := range rels { + src := r.SourceElement + if mapped, ok := uuidToSourceID[src]; ok { + src = mapped + } + tgt := r.TargetElement + if mapped, ok := uuidToSourceID[tgt]; ok { + tgt = mapped + } m.Relationships = append(m.Relationships, parser.Relationship{ ID: r.SourceID, Type: r.Type, - Source: r.SourceElement, - Target: r.TargetElement, + Source: src, + Target: tgt, Name: r.Name, Documentation: r.Documentation, AccessType: r.AccessType,