archgraph-argo 0.15.3 → 0.16.1

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;
@@ -0,0 +1,39 @@
1
+ ---
2
+ name: ea-human-reconcile
3
+ description: "把人类从 EA 提取的 draft 提议(results/human-draft.md,内嵌 JSON 提议集)交给 Agent 后,Agent 结合当前全局(canonical 意图图 design/KG/SystemArchitecture.json + Neo4j 语义检索)逐条/整体分析并给出建议,最终由人类裁决是否写入。它是『人类 EA 草稿 → 语义 diff 提取 → 人类裁决写回』的最后一步:Agent 只做分析+建议,绝不自作主张 apply。Use when the user hands over an EA human-draft proposal set (human-draft.md) and wants the agent to analyze it against the whole architecture and propose, leaving the final decision to the human. Keywords: EA human draft, reconcile, EA 人类草稿裁决, draft review, 语义diff建议, ARGO preview/apply, 人类裁决."
4
+ argument-hint: human-draft
5
+ disable-model-invocation: true
6
+ ---
7
+
8
+ # EA HUMAN DRAFT RECONCILE(EA 人类草稿 → Agent 分析建议 → 人类裁决)
9
+
10
+ Agent 的职责:结合全局分析 draft 中的每条提议、给出建议与理由、识别风险,把结论呈现给人类,由人类裁决。Agent 不自动执行写入。
11
+
12
+ ## 前置
13
+
14
+ - [ ] 存在 `results/human-draft.md`(人类从 EA 提取的草稿提议集)。
15
+ - [ ] 工作区有 `design/KG/SystemArchitecture.json`(canonical 意图图)——分析全局的依据。
16
+ - [ ] ARGO MCP 可用。关键查询手段:
17
+ - 语义/上下文:`getSystemArchitecture`(带 query.purpose + query.intent)、`getIntentElementContext`(看单个元素/关系/视图的依赖与受影响对象)。
18
+ - 结构/类型:`queryNeo4jGraph`(只读 Cypher,如 `MATCH (e:Element {graphKey: $graphKey, type:'Business Actor'}) ...`;先 `{schema:true}` 查投影 schema)。
19
+ - 校验:`validateSystemArchitecture`;写图:`previewSystemArchitectureMutation` / `applySystemArchitectureMutation`。
20
+
21
+ ## 原则
22
+
23
+ - **人类裁决**:Agent 只分析、给建议、识别风险;**MUST NOT 未经人类裁决就 `applySystemArchitectureMutation`(或任何写图)**。人类拍板(采纳/拒绝/修改)后才写。
24
+ - **全局优先**:不只对着 draft 字段,要用查询手段读 canonical 中相关对象及其依赖(被谁引用、级联影响谁、子视图是否悬空、命名/类型是否与全局一致、是否与既有元素重复冲突),再下判断。
25
+ - **删除/破坏性提议单独把关**:对 `removeElement` / `removeRelationship` / `removeView`,必须指出 canonical 里谁引用它、删除会级联影响谁,并给出「保留 / 改挂 / 确认删除」的建议,交由人类确认。
26
+ - **新增/更新提议**:核对 type / name / description / attributes 是否与全局命名与类型一致、是否与既有元素/关系重复冲突、应挂在哪个 parent / 视图下。
27
+ - **视图提议**:核对成员是否落在正确层级(parent_element_id 是否合理)、删视图会否使相关 subdiagram_views 悬空。
28
+ - **只读取、不泄露**:MUST NOT 读取/复述 `.env` 里的 secret 或推断敏感配置值。
29
+
30
+ ## 目标
31
+
32
+ 读 draft → 结合全局逐条分析 → 给出「逐条建议 + 整体结论 + 待人类裁决项」。具体产出:
33
+
34
+ 1. **操作统计**:add/update/remove × element/relationship/view。
35
+ 2. **逐条建议**:每条含 `提议`(op + id/名称)、`分析`(全局依赖/冲突/层级)、`建议`(采纳/拒绝/修改后采纳,附理由)、`风险`(若有)。
36
+ 3. **整体结论**:这批草稿的整体风险等级、必人工确认的高风险项。
37
+ 4. **待裁决**:明确列出需人类拍板的条目,并询问哪些采纳、哪些修改、哪些拒绝。
38
+
39
+ **等待人类答复之后**才进入写回;若人类同意,才经 `previewSystemArchitectureMutation` →(确认后)`applySystemArchitectureMutation`;拒绝/搁置的不写。
package/install-argo.ps1 CHANGED
@@ -761,6 +761,9 @@ $skillSrc = Join-Path (Join-Path $argoDir 'skills') 'argo-init'
761
761
  $skillDest = Join-Path $SkillsRoot 'argo-init'
762
762
  Write-Host "[4/22] argo\skills\argo-init -> $skillDest"
763
763
  Copy-Tree -Source $skillSrc -Destination $skillDest
764
+ $reconcileSkillSrc = Join-Path (Join-Path $argoDir 'skills') 'ea-human-reconcile'
765
+ Write-Host ' argo\skills\ea-human-reconcile -> $SkillsRoot\ea-human-reconcile (EA human draft reconcile skill)'
766
+ Copy-Tree -Source $reconcileSkillSrc -Destination (Join-Path $SkillsRoot 'ea-human-reconcile')
764
767
 
765
768
  $ruleSrc = Join-Path (Join-Path $argoDir 'rules') 'archgraph.instructions.md'
766
769
  $ruleDest = Join-Path $PromptsRoot 'archgraph.instructions.md'
@@ -776,6 +779,8 @@ Copy-Item -Force -Path $depsSrc -Destination $depsDest
776
779
  $cursorSkillDest = Join-Path $CursorSkillsRoot 'argo-init'
777
780
  Write-Host "[7/22] argo\skills\argo-init -> $cursorSkillDest (Cursor)"
778
781
  Copy-Tree -Source $skillSrc -Destination $cursorSkillDest
782
+ Write-Host ' argo\skills\ea-human-reconcile -> $CursorSkillsRoot\ea-human-reconcile (Cursor)'
783
+ Copy-Tree -Source $reconcileSkillSrc -Destination (Join-Path $CursorSkillsRoot 'ea-human-reconcile')
779
784
 
780
785
  $mcpBridgeSrc = Join-Path $argoDir 'mcp-bridges'
781
786
  $mcpBridgeDest = Join-Path $CursorMcpBridgesRoot ''
@@ -785,6 +790,8 @@ Copy-Tree -Source $mcpBridgeSrc -Destination $mcpBridgeDest
785
790
  $openCodeSkillDest = Join-Path $OpenCodeSkillsRoot 'argo-init'
786
791
  Write-Host "[8/22] argo\skills\argo-init -> $openCodeSkillDest (OpenCode)"
787
792
  Copy-Tree -Source $skillSrc -Destination $openCodeSkillDest
793
+ Write-Host ' argo\skills\ea-human-reconcile -> $OpenCodeSkillsRoot\ea-human-reconcile (OpenCode)'
794
+ Copy-Tree -Source $reconcileSkillSrc -Destination (Join-Path $OpenCodeSkillsRoot 'ea-human-reconcile')
788
795
 
789
796
  Write-Host "[9/22] argo\rules\archgraph.instructions.md -> $OpenCodeAgentsPath (OpenCode global AGENTS.md)"
790
797
  Add-AgentsRule -AgentsPath $OpenCodeAgentsPath -RulePath $ruleSrc
@@ -822,6 +829,8 @@ if ($SkipDsh) {
822
829
 
823
830
  Write-Host "[16/22] argo\skills\argo-init -> $dshSkillDest (DeepSeek Harness skill)"
824
831
  Copy-Tree -Source (Join-Path $argoDir 'skills\argo-init') -Destination $dshSkillDest
832
+ Write-Host ' argo\skills\ea-human-reconcile -> $DshHome\skills\ea-human-reconcile (DeepSeek Harness skill)'
833
+ Copy-Tree -Source (Join-Path $argoDir 'skills\ea-human-reconcile') -Destination (Join-Path (Join-Path $DshHome 'skills') 'ea-human-reconcile')
825
834
 
826
835
  Write-Host "[17/22] argo\rules\<WakeupGuideline> -> $DshHome\plugins\dsh-argo-wakeup\index.js (DeepSeek Harness wakeup plugin)"
827
836
  $wakeupDshPath = New-DshWakeupPlugin -DshHome $DshHome -RuleText $ruleSrcContent
@@ -883,6 +892,8 @@ if ($SkipOpenClaw) {
883
892
 
884
893
  Write-Host "[21/22] argo\skills\argo-init -> $openClawSkillDest (OpenClaw managed skill, all agents)"
885
894
  Copy-Tree -Source (Join-Path $argoDir 'skills\argo-init') -Destination $openClawSkillDest
895
+ Write-Host ' argo\skills\ea-human-reconcile -> $OpenClawHome\skills\ea-human-reconcile (OpenClaw managed skill, all agents)'
896
+ Copy-Tree -Source (Join-Path $argoDir 'skills\ea-human-reconcile') -Destination (Join-Path (Join-Path $OpenClawHome 'skills') 'ea-human-reconcile')
886
897
 
887
898
  Write-Host ' OpenClaw injects AGENTS.md into Project Context on every session, so the wakeup'
888
899
  Write-Host ' gate (UNCONDITIONAL STARTUP GATE) is active on the next OpenClaw session; restart'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "archgraph-argo",
3
- "version": "0.15.3",
3
+ "version": "0.16.1",
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": {
@@ -14,6 +14,7 @@
14
14
  "argo/plugins",
15
15
  "argo/mcp-bridges",
16
16
  "argo/skills/argo-init",
17
+ "argo/skills/ea-human-reconcile",
17
18
  "argo/rules",
18
19
  "argo/package.json",
19
20
  "vendor",