archgraph-argo 0.16.0 → 0.16.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -740,24 +740,66 @@ function syncGraphToQea(graph, qeaPath, opts) {
740
740
  stages.members = nowMs();
741
741
 
742
742
  // --- deletion reconcile (opt-in) ---------------------------------------
743
+ // Elements are anchored by t_object.Alias; relationships have NO Alias column — their
744
+ // canonical id lives in t_connectortag(schema_id). Using row.Alias for relationships made
745
+ // the candidate check always false, so deleted relationships were never projected out.
743
746
  const keepAliases = new Set();
744
747
  for (const e of graph.elements || []) { if (e && e.id !== undefined) { keepAliases.add(String(e.id)); } }
745
748
  for (const rel of graph.relationships || []) { if (rel && rel.id !== undefined) { keepAliases.add(String(rel.id)); } }
749
+ // connector id -> canonical relationship id (schema_id tag)
750
+ const relSchemaById = new Map();
751
+ try {
752
+ const relTagRows = db.prepare("SELECT ElementID, VALUE FROM t_connectortag WHERE Property='schema_id'").all();
753
+ for (const t of relTagRows) { relSchemaById.set(Number(t.ElementID), String(t.VALUE)); }
754
+ } catch { /* table may be absent on a hand-drawn model */ }
746
755
  const candidates = [];
747
756
  for (const row of existingElems) {
748
757
  if (row.Alias && !keepAliases.has(String(row.Alias))) { candidates.push({ type: 'element', id: Number(row.Object_ID), alias: row.Alias }); }
749
758
  }
750
759
  for (const row of existingRels) {
751
- if (row.Alias && !keepAliases.has(String(row.Alias))) { candidates.push({ type: 'relationship', id: Number(row.Connector_ID), alias: row.Alias }); }
760
+ const relId = relSchemaById.get(Number(row.Connector_ID));
761
+ if (relId !== undefined && !keepAliases.has(relId)) { candidates.push({ type: 'relationship', id: Number(row.Connector_ID), alias: relId }); }
762
+ }
763
+ // views: a projected diagram is owned by the projector. Identify its canonical view id via
764
+ // the schema_view_id StyleEx token, or (when EA rewrote StyleEx and dropped the token) by
765
+ // matching the deterministic diagram ea_guid `diag:<viewId>` against the view-id catalog
766
+ // that kg_sync_meta retains. A view id no longer in canonical -> delete the diagram.
767
+ const canonicalViewIds = new Set();
768
+ for (const v of graph.views || []) { if (v && v.view_id !== undefined && v.view_id !== null) { canonicalViewIds.add(String(v.view_id)); } }
769
+ const guidToView = new Map();
770
+ try {
771
+ const viewMetaRows = db.prepare("SELECT key FROM kg_sync_meta WHERE kind='view'").all();
772
+ for (const r of viewMetaRows) { guidToView.set(deterministicGuid('diag:' + String(r.key)), String(r.key)); }
773
+ } catch { /* meta table may be absent */ }
774
+ for (const d of existingDiags) {
775
+ let vid = parseStyleToken(d.StyleEx, 'schema_view_id');
776
+ if (!vid && d.ea_guid) { vid = guidToView.get(String(d.ea_guid)) || ''; }
777
+ if (vid && !canonicalViewIds.has(String(vid))) { candidates.push({ type: 'diagram', id: Number(d.Diagram_ID), alias: String(vid) }); }
752
778
  }
753
779
  stats.deleteCandidates = candidates.length;
754
780
  if (candidates.length > 0 && o.allowDelete && !o.dryRun) {
755
781
  for (const c of candidates) {
756
782
  if (c.type === 'relationship') {
783
+ // SQLite has no FK cascade: clear the connector's tag + diagram-link rows too.
784
+ db.prepare('DELETE FROM t_connectortag WHERE ElementID=?').run(c.id);
785
+ db.prepare('DELETE FROM t_diagramlinks WHERE ConnectorID=?').run(c.id);
757
786
  db.prepare('DELETE FROM t_connector WHERE Connector_ID=?').run(c.id);
787
+ } else if (c.type === 'diagram') {
788
+ db.prepare('DELETE FROM t_diagramobjects WHERE Diagram_ID=?').run(c.id);
789
+ db.prepare('DELETE FROM t_diagramlinks WHERE DiagramID=?').run(c.id);
790
+ db.prepare('DELETE FROM t_diagram WHERE Diagram_ID=?').run(c.id);
758
791
  } else {
792
+ // remove connectors attached to this element (start or end) and their children
793
+ const attached = db.prepare('SELECT Connector_ID FROM t_connector WHERE Start_Object_ID=? OR End_Object_ID=?').all(c.id, c.id);
794
+ for (const a of attached) {
795
+ db.prepare('DELETE FROM t_connectortag WHERE ElementID=?').run(Number(a.Connector_ID));
796
+ db.prepare('DELETE FROM t_diagramlinks WHERE ConnectorID=?').run(Number(a.Connector_ID));
797
+ db.prepare('DELETE FROM t_connector WHERE Connector_ID=?').run(Number(a.Connector_ID));
798
+ }
759
799
  db.prepare('DELETE FROM t_diagramobjects WHERE Object_ID=?').run(c.id);
760
800
  db.prepare('DELETE FROM t_objectproperties WHERE Object_ID=?').run(c.id);
801
+ db.prepare('DELETE FROM t_attribute WHERE Object_ID=?').run(c.id);
802
+ db.prepare('DELETE FROM t_objecttests WHERE Object_ID=?').run(c.id);
761
803
  db.prepare('DELETE FROM t_object WHERE Object_ID=?').run(c.id);
762
804
  }
763
805
  stats.deleted++;
@@ -1948,7 +1948,11 @@ function runQeaProjection(target) {
1948
1948
  return;
1949
1949
  }
1950
1950
  const snapshotDir = path.join(target.workspaceRoot, '.argo', 'temp', 'qea-backups');
1951
- const args = [script, '--mode', 'sync', '--graph', target.graphPath, '--qea', target.qeaPath, '--snapshot-dir', snapshotDir];
1951
+ // -y enables the projection-owned delete reconcile: objects that carry a schema anchor
1952
+ // (t_object.Alias / t_connectortag schema_id) but are no longer in canonical are removed
1953
+ // from the .qea, so a graph-side deletion actually disappears from EA on the next
1954
+ // projection. Human-drawn (un-anchored) content is never a delete candidate.
1955
+ const args = [script, '--mode', 'sync', '--graph', target.graphPath, '--qea', target.qeaPath, '--snapshot-dir', snapshotDir, '-y'];
1952
1956
  const started = Date.now();
1953
1957
  let stderr = '';
1954
1958
  let child;
@@ -2313,7 +2317,9 @@ async function callTool(name, args = {}, dependencies = undefined) {
2313
2317
  }
2314
2318
  }
2315
2319
 
2316
- const journey = await resolveSemanticOperatorJourney(dependencies);
2320
+ const journey = await resolveSemanticOperatorJourney(dependencies, {
2321
+ repositoryRoot: context.workspaceRoot,
2322
+ });
2317
2323
  return applySemanticResponseProfile(await journey.query(query), query, contractOptions);
2318
2324
  }
2319
2325
 
@@ -2429,7 +2435,9 @@ async function memorySearchTool(args = {}, dependencies = undefined) {
2429
2435
  const context = await loadContext(args);
2430
2436
  let retrieved;
2431
2437
  try {
2432
- const journey = await resolveSemanticOperatorJourney(dependencies);
2438
+ const journey = await resolveSemanticOperatorJourney(dependencies, {
2439
+ repositoryRoot: context.workspaceRoot,
2440
+ });
2433
2441
  retrieved = await journey.query({ purpose: 'general', intent: query });
2434
2442
  } catch (error) {
2435
2443
  return {
@@ -2581,10 +2589,21 @@ function queryNeo4jGraphSchemaResult(architecturePath, workspaceRoot) {
2581
2589
  });
2582
2590
  }
2583
2591
 
2584
- async function resolveSemanticOperatorJourney(dependencies) {
2585
- return dependencies && dependencies.semanticOperatorJourney
2586
- ? dependencies.semanticOperatorJourney
2587
- : createDefaultProductionSemanticOperatorJourney();
2592
+ async function resolveSemanticOperatorJourney(dependencies, options = {}) {
2593
+ if (dependencies && dependencies.semanticOperatorJourney) {
2594
+ return dependencies.semanticOperatorJourney;
2595
+ }
2596
+ // Thread the caller's already-resolved workspace root into the journey: the
2597
+ // no-arg resolveWorkspaceRoot() fallback is process.cwd(), i.e. whatever
2598
+ // directory the host launched this server from (a global ~/.argo installation
2599
+ // started by the host process resolves to that host's cwd, e.g.
2600
+ // C:\Windows\System32), never the workspace the caller asked for.
2601
+ const repositoryRoot = options && typeof options.repositoryRoot === 'string'
2602
+ ? options.repositoryRoot
2603
+ : '';
2604
+ return createDefaultProductionSemanticOperatorJourney(
2605
+ repositoryRoot ? { repositoryRoot } : {},
2606
+ );
2588
2607
  }
2589
2608
 
2590
2609
  async function executeSemanticSystemArchitectureQuery(args, dependencies) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "archgraph-argo",
3
- "version": "0.16.0",
3
+ "version": "0.16.2",
4
4
  "description": "Deploy the ArchGraph ARGO toolchain, skills, and rules (schema, scripts, argo-init skill, global rule) with one command.",
5
5
  "license": "MIT",
6
6
  "bin": {