dsh-data-domain-knowledge 0.1.0-alpha.2 → 0.1.0-alpha.3

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.
Files changed (117) hide show
  1. package/README.md +31 -7
  2. package/cordis.patch.yml +11 -5
  3. package/lib/client.js +5557 -3056
  4. package/lib/doc-page.js +7740 -0
  5. package/lib/types/convert/host.d.ts.map +1 -1
  6. package/lib/types/convert/host.js +36 -9
  7. package/lib/types/convert/host.js.map +1 -1
  8. package/lib/types/diagnosis/cards.d.ts +8 -7
  9. package/lib/types/diagnosis/cards.d.ts.map +1 -1
  10. package/lib/types/diagnosis/cards.js +119 -49
  11. package/lib/types/diagnosis/cards.js.map +1 -1
  12. package/lib/types/diagnosis/run.d.ts +51 -0
  13. package/lib/types/diagnosis/run.d.ts.map +1 -0
  14. package/lib/types/diagnosis/run.js +239 -0
  15. package/lib/types/diagnosis/run.js.map +1 -0
  16. package/lib/types/diagnosis/schema.d.ts +25 -2
  17. package/lib/types/diagnosis/schema.d.ts.map +1 -1
  18. package/lib/types/diagnosis/schema.js +64 -25
  19. package/lib/types/diagnosis/schema.js.map +1 -1
  20. package/lib/types/diagnosis/seed-source.d.ts +17 -2
  21. package/lib/types/diagnosis/seed-source.d.ts.map +1 -1
  22. package/lib/types/diagnosis/seed-source.js +18 -1
  23. package/lib/types/diagnosis/seed-source.js.map +1 -1
  24. package/lib/types/diagnosis/seed.d.ts +32 -42
  25. package/lib/types/diagnosis/seed.d.ts.map +1 -1
  26. package/lib/types/diagnosis/seed.js +187 -51
  27. package/lib/types/diagnosis/seed.js.map +1 -1
  28. package/lib/types/diagnosis/store.d.ts +114 -26
  29. package/lib/types/diagnosis/store.d.ts.map +1 -1
  30. package/lib/types/diagnosis/store.js +336 -167
  31. package/lib/types/diagnosis/store.js.map +1 -1
  32. package/lib/types/diagnosis/types.d.ts +168 -16
  33. package/lib/types/diagnosis/types.d.ts.map +1 -1
  34. package/lib/types/diagnosis/types.js +1 -1
  35. package/lib/types/diagnosis/types.js.map +1 -1
  36. package/lib/types/document-page.d.ts +39 -0
  37. package/lib/types/document-page.d.ts.map +1 -0
  38. package/lib/types/document-page.js +43 -0
  39. package/lib/types/document-page.js.map +1 -0
  40. package/lib/types/fs-atomic.d.ts +12 -0
  41. package/lib/types/fs-atomic.d.ts.map +1 -0
  42. package/lib/types/fs-atomic.js +42 -0
  43. package/lib/types/fs-atomic.js.map +1 -0
  44. package/lib/types/index.d.ts.map +1 -1
  45. package/lib/types/index.js +214 -24
  46. package/lib/types/index.js.map +1 -1
  47. package/lib/types/knowledge-bases.d.ts +10 -2
  48. package/lib/types/knowledge-bases.d.ts.map +1 -1
  49. package/lib/types/knowledge-bases.js +20 -18
  50. package/lib/types/knowledge-bases.js.map +1 -1
  51. package/lib/types/proposals.d.ts +166 -0
  52. package/lib/types/proposals.d.ts.map +1 -0
  53. package/lib/types/proposals.js +238 -0
  54. package/lib/types/proposals.js.map +1 -0
  55. package/lib/types/runtime.d.ts +199 -7
  56. package/lib/types/runtime.d.ts.map +1 -1
  57. package/lib/types/runtime.js +728 -145
  58. package/lib/types/runtime.js.map +1 -1
  59. package/lib/types/settings-schema.d.ts +2 -2
  60. package/lib/types/types.d.ts +61 -10
  61. package/lib/types/types.d.ts.map +1 -1
  62. package/lib/types/types.js +56 -8
  63. package/lib/types/types.js.map +1 -1
  64. package/lib/types/wiki-types.d.ts +8 -1
  65. package/lib/types/wiki-types.d.ts.map +1 -1
  66. package/lib/types/wiki-types.js +16 -25
  67. package/lib/types/wiki-types.js.map +1 -1
  68. package/package.json +4 -3
  69. package/preset/tita-data-agent/agent.cordis.yml +11 -5
  70. package/src/client/DirectoryPicker.tsx +12 -12
  71. package/src/client/InfoHint.tsx +21 -8
  72. package/src/client/IntentTab.tsx +20 -26
  73. package/src/client/KnowledgeBaseTab.tsx +190 -80
  74. package/src/client/KnowledgePanel.tsx +42 -9
  75. package/src/client/KnowledgeStepRail.tsx +3 -0
  76. package/src/client/NodeSettings.tsx +20 -22
  77. package/src/client/PreviewSections.tsx +87 -33
  78. package/src/client/ProposalReview.tsx +313 -0
  79. package/src/client/TypeDefinitionDrawer.tsx +72 -38
  80. package/src/client/WikiTypeEditor.tsx +193 -77
  81. package/src/client/arch/AgentArchitectureGraph.tsx +143 -14
  82. package/src/client/arch/NodeLogicPanel.tsx +6 -3
  83. package/src/client/arch/model.ts +164 -26
  84. package/src/client/architecture.css +29 -9
  85. package/src/client/architecture.tsx +54 -29
  86. package/src/client/attribution/AttributionGraph.tsx +481 -34
  87. package/src/client/attribution/AttributionTab.tsx +507 -106
  88. package/src/client/attribution/CaseView.tsx +9 -14
  89. package/src/client/attribution/GraphStage.tsx +25 -18
  90. package/src/client/attribution/RunView.tsx +27 -9
  91. package/src/client/attribution/StageChrome.tsx +296 -140
  92. package/src/client/attribution/graph-options.ts +611 -47
  93. package/src/client/attribution/model.ts +559 -30
  94. package/src/client/convert/ConvertPanel.tsx +6 -1
  95. package/src/client/doc-page.css +134 -0
  96. package/src/client/doc-page.tsx +263 -0
  97. package/src/client/panel-state.ts +105 -0
  98. package/src/client/request.ts +36 -0
  99. package/src/client/styles.css +317 -43
  100. package/src/client/useFitScale.ts +15 -2
  101. package/src/client/wiki-type-groups.ts +86 -0
  102. package/src/convert/host.ts +41 -7
  103. package/src/diagnosis/cards.ts +120 -50
  104. package/src/diagnosis/run.ts +245 -0
  105. package/src/diagnosis/schema.ts +67 -26
  106. package/src/diagnosis/seed-source.ts +28 -3
  107. package/src/diagnosis/seed.ts +187 -85
  108. package/src/diagnosis/store.ts +358 -174
  109. package/src/diagnosis/types.ts +181 -16
  110. package/src/document-page.ts +51 -0
  111. package/src/fs-atomic.ts +42 -0
  112. package/src/index.ts +209 -22
  113. package/src/knowledge-bases.ts +20 -18
  114. package/src/proposals.ts +337 -0
  115. package/src/runtime.ts +736 -135
  116. package/src/types.ts +113 -19
  117. package/src/wiki-types.ts +17 -27
@@ -1,47 +1,27 @@
1
- import type { AttributionGraphData } from './types.js'
1
+ import type { AttributionCase, AttributionGraphData, DiagnosisRunRecord } from './types.js'
2
2
 
3
3
  /**
4
- * 案例 = **一篇 wiki 定义 + 一条有序排查路径**。
4
+ * 内置**示例**:一份排查手册 + 三篇复盘 + 三次排查日志 + 由它们编出来的那张图。
5
5
  *
6
- * 两段各有明确消费者,不能合并:
7
- * - `sections` 是给人读、给向量召回的散文(可 diff、可评审、可追溯);
8
- * - `path` 是给图和剪枝用的结构(有序、可判真假、带被否定的分支)。
6
+ * 它现在只有一重身份:**用户点了「载入内置示例」才写进去的样例,以及回归夹具**。
9
7
  *
10
- * 序和枚举一旦写进散文,归并算法就再也读不到它们了 —— 所以路径单独成段。
11
- */
12
- export interface CaseSection {
13
- heading: string
14
- body: string
15
- }
16
-
17
- export interface CasePathStep {
18
- nodeId: string
19
- label: string
20
- note: string
21
- /** 这一步的结论是「否定」:它把某个假设置于死地,而不是坐实。 */
22
- refuted?: boolean
23
- }
24
-
25
- export interface AttributionCase {
26
- id: string
27
- title: string
28
- symptomId: string
29
- sections: CaseSection[]
30
- path: CasePathStep[]
31
- durationMinutes: number
32
- /** 误导段:最贵的那条知识。没有它,下次还会有人在同一个假设上烧掉四十分钟。 */
33
- misleading?: { nodeId: string; label: string; wastedMinutes: number; why: string }
34
- rootCause: string
35
- }
36
-
37
- /**
38
- * Seed 图谱:与 0021 §11 的三个案例同源,用来说明「同一现象有多个根因、否定边有用、
39
- * 误导边昂贵」。
8
+ * 原来是两重(另一重是「首次启动的起点」,由 `DiagnosisStore.ensure()` 自动写入)。
9
+ * 实跑反馈把它去掉了:这份示例长得像真的业务文档,而它落在用户自己的
10
+ * `01_source_truth/` 与 `02_wiki/` 里 —— 新装一台机器,一打开「智能归因」就凭空多出
11
+ * 几份来源和几十张卡片,读起来像「我的知识库内容跟着包过来了」。示例没有被删除
12
+ * (它仍然是稳定夹具,`tests/diagnosis-cards.test.mjs` 逐字段对着它断言),
13
+ * 只是不再自动落盘 —— 入口是 `DiagnosisStore.loadSample()`。
14
+ *
15
+ * **两条来源分类、两个作者**(v1.1 起):
40
16
  *
41
- * 它现在有两重身份:
42
- * 1. **首次启动的起点** —— `DiagnosisStore.ensure()` 在 `05_diagnosis/` 为空时把它写成
43
- * 真实的 jsonl,之后用户可以手改,不会被覆盖回去;
44
- * 2. **回归样本** —— 上面那三条结论需要一个稳定的夹具,所以它不会被删除。
17
+ * | 来源分类 | 谁写 | 装什么 |
18
+ * | --- | --- | --- |
19
+ * | `question` | 人 | 手册与复盘 —— 卡片 `source_refs` 指向这里 |
20
+ * | `agent_diagnosis_case` | Agent | 一次排查的过程记录(`SEED_RUNS`) |
21
+ *
22
+ * `SEED_GRAPH` 里**只有卡片定义得了的东西**:类型层、引用层资产、案例桩,以及六种卡片边。
23
+ * `evidence` / `cause` 与四条执行边由 `SEED_RUNS` 读时解析出来 —— 把「上次查到什么」
24
+ * 写进卡片,等于让一份可复用的知识绑死在一次执行上。
45
25
  */
46
26
  export const SEED_GRAPH: AttributionGraphData = {
47
27
  nodes: [
@@ -64,85 +44,91 @@ export const SEED_GRAPH: AttributionGraphData = {
64
44
  summary: '观测量:DAU + 单日窗口;放大 3.1 倍且只有 T-1 单日,相邻日期正常。',
65
45
  },
66
46
 
67
- { id: 'H1', kind: 'hypothesis', layer: 'type', title: '上游分区未产出', failureMode: 'upstream-missing', reuseCount: 12,
47
+ { id: 'H1', kind: 'hypothesis', layer: 'type', title: '上游分区未产出', failureReason: 'upstream-missing', reuseCount: 12,
68
48
  summary: '当日 dwd / ods 分区不存在 → 指标必然为 0。反例:分区在但行数为 0 时不是它。',
69
49
  discriminator: '当日 dwd / ods 分区不存在 → 指标必然为 0',
70
50
  notWhen: '分区存在但行数为 0(那是 H4);分区正常而指标仍为 0(那是口径或消费侧)' },
71
- { id: 'H2', kind: 'hypothesis', layer: 'type', title: '口径 / 版本变更', failureMode: 'spec-change', reuseCount: 5,
51
+ { id: 'H2', kind: 'hypothesis', layer: 'type', title: '口径 / 版本变更', failureReason: 'spec-change', reuseCount: 5,
72
52
  summary: '口径版本切换未同步 → 同名词指标在不同版本下口径不同。',
73
53
  discriminator: '口径版本切换后,同名词指标在两版定义下口径不一致',
74
54
  notWhen: '口径未变更时本假设不成立 —— 不要用「名字一样」当作口径一致的证据' },
75
- { id: 'H3', kind: 'hypothesis', layer: 'type', title: '维表映射丢失', failureMode: 'data-processing', reuseCount: 4,
55
+ { id: 'H3', kind: 'hypothesis', layer: 'type', title: '维表映射丢失', failureReason: 'data-processing', reuseCount: 4,
76
56
  summary: '新渠道未补录维表 → join 落 unknown 后被 where 过滤。',
77
57
  discriminator: '维表 join 后 unknown 占比显著升高,且缺口集中在新增维度成员上',
78
58
  notWhen: 'unknown 占比正常时本假设不成立;缺口若均匀分布在各维度上,应转 H4 或 H5' },
79
- { id: 'H4', kind: 'hypothesis', layer: 'type', title: '重复 / 回刷', failureMode: 'data-processing', reuseCount: 6,
59
+ { id: 'H4', kind: 'hypothesis', layer: 'type', title: '重复 / 回刷', failureReason: 'data-processing', reuseCount: 6,
80
60
  summary: '上游重跑写入重复行 → 计数类指标放大。',
81
61
  discriminator: '主键不唯一,或分区行数同比放大',
82
62
  notWhen: '主键唯一性检查通过(T4.1)时本假设不成立 —— 这是最常被误判的一条' },
83
- { id: 'H5', kind: 'hypothesis', layer: 'type', title: '时间窗口 / 时区', failureMode: 'data-processing', reuseCount: 2,
63
+ { id: 'H5', kind: 'hypothesis', layer: 'type', title: '时间窗口 / 时区', failureReason: 'data-processing', reuseCount: 2,
84
64
  summary: '跨天事件被补写进 T-1 分区 → 单日指标异常放大。',
85
65
  discriminator: '分区内事件时间跨天,且只有 T-1 单日异常',
86
66
  notWhen: '相邻日期同时异常时应转 H2;事件时间落在单日内则应转 H4' },
87
- { id: 'H6', kind: 'hypothesis', layer: 'type', title: '权限 / 过滤条件', failureMode: 'permission', reuseCount: 1,
67
+ { id: 'H6', kind: 'hypothesis', layer: 'type', title: '权限 / 过滤条件', failureReason: 'permission', reuseCount: 1,
88
68
  summary: '行级权限或 where 条件变更 → 结果集被静默收窄。',
89
69
  discriminator: '结果集被行级权限或 where 条件静默收窄',
90
70
  notWhen: '权限与过滤条件在本周期未变更时本假设不成立' },
91
- { id: 'H7', kind: 'hypothesis', layer: 'type', title: '下游 BI 缓存 / 增量', failureMode: 'consumption', reuseCount: 3,
71
+ { id: 'H7', kind: 'hypothesis', layer: 'type', title: '下游 BI 缓存 / 增量', failureReason: 'consumption', reuseCount: 3,
92
72
  summary: '缓存或增量刷新未触发 → 展示侧滞后,明细不受影响。',
93
73
  discriminator: 'BI 展示侧滞后,但直连明细的数字正常',
94
74
  notWhen: '明细对账一致(T7.2)时本假设不成立;注意 T7.1「缓存时间正常」不足以否定它' },
95
75
 
96
- // `code` 是这张卡片的稳定标识:假设卡片里写的是编码(`failure_mode: data-processing`),
76
+ // `code` 是这张卡片的稳定标识:假设卡片里写的是编码(`failure_reason: data-processing`),
97
77
  // 边要连到节点 —— 少了它,「编码 → 节点」就只能靠 F1..F5 的位置去猜。
98
- { id: 'F1', kind: 'failure-mode', layer: 'type', title: '上游未产出', code: 'upstream-missing' },
99
- { id: 'F2', kind: 'failure-mode', layer: 'type', title: '口径变更', code: 'spec-change' },
100
- { id: 'F3', kind: 'failure-mode', layer: 'type', title: '数据处理', code: 'data-processing' },
101
- { id: 'F4', kind: 'failure-mode', layer: 'type', title: '消费与展示', code: 'consumption' },
102
- { id: 'F5', kind: 'failure-mode', layer: 'type', title: '权限与过滤', code: 'permission' },
78
+ { id: 'F1', kind: 'failure_reason', layer: 'type', title: '上游未产出', code: 'upstream-missing' },
79
+ { id: 'F2', kind: 'failure_reason', layer: 'type', title: '口径变更', code: 'spec-change' },
80
+ { id: 'F3', kind: 'failure_reason', layer: 'type', title: '数据处理', code: 'data-processing' },
81
+ { id: 'F4', kind: 'failure_reason', layer: 'type', title: '消费与展示', code: 'consumption' },
82
+ { id: 'F5', kind: 'failure_reason', layer: 'type', title: '权限与过滤', code: 'permission' },
103
83
 
104
- { id: 'T1.1', kind: 'check', layer: 'type', title: '查分区存在性', costMinutes: 1, automation: 'auto', owner: '@duty',
84
+ // 检查点:`assets[]` 是 v1.3 的必填 —— 缺了它,这条检查不知道去哪查。
85
+ { id: 'T1.1', kind: 'check_point', layer: 'type', title: '查分区存在性', costMinutes: 1, automation: 'auto', owner: '@duty',
86
+ assets: ['dwd_ord_di'],
105
87
  summary: '分区 dwd_ord_di/dt 是否存在。不存在 → 支持 H1。',
106
88
  verdictTrue: '分区不存在', verdictFalse: '分区存在' },
107
- { id: 'T1.2', kind: 'check', layer: 'type', title: '查任务实例状态', costMinutes: 1, automation: 'auto', owner: '@duty',
89
+ { id: 'T1.2', kind: 'check_point', layer: 'type', title: '查任务实例状态', costMinutes: 1, automation: 'auto', owner: '@duty',
90
+ assets: ['ods_order_sync'],
108
91
  summary: '上游任务实例是否 failed。failed → 支持 H1。',
109
92
  verdictTrue: '实例状态 failed', verdictFalse: '实例状态 success / running' },
110
- { id: 'T2.1', kind: 'check', layer: 'type', title: '口径版本对比', costMinutes: 15, automation: 'manual', owner: '@metric-owner',
93
+ { id: 'T2.1', kind: 'check_point', layer: 'type', title: '口径版本对比', costMinutes: 15, automation: 'manual', owner: '@metric-owner',
94
+ assets: ['metric_daily_gmv'],
111
95
  summary: '对比近两次口径版本定义。口径变化 → 支持 H2。',
112
96
  verdictTrue: '近两次口径定义不一致', verdictFalse: '口径定义逐字一致' },
113
- { id: 'T3.1', kind: 'check', layer: 'type', title: 'unknown 占比', costMinutes: 2, automation: 'auto', owner: '@duty',
97
+ { id: 'T3.1', kind: 'check_point', layer: 'type', title: 'unknown 占比', costMinutes: 2, automation: 'auto', owner: '@duty',
98
+ assets: ['dim_channel_df'],
114
99
  summary: '维表 join 后 unknown 占比。显著升高 → 支持 H3。',
115
100
  verdictTrue: 'unknown 占比 > 5%', verdictFalse: 'unknown 占比 ≤ 5%' },
116
- { id: 'T4.1', kind: 'check', layer: 'type', title: '主键唯一性', costMinutes: 3, automation: 'auto', owner: '@duty',
101
+ { id: 'T4.1', kind: 'check_point', layer: 'type', title: '主键唯一性', costMinutes: 3, automation: 'auto', owner: '@duty',
102
+ assets: ['dwd_ord_di'],
117
103
  summary: '主键唯一性检查。通过 → 否定 H4。这是最常被误判的一条。',
118
104
  verdictTrue: '存在重复主键', verdictFalse: '主键唯一 → 否定 H4' },
119
- { id: 'T5.1', kind: 'check', layer: 'type', title: '事件时间分布', costMinutes: 8, automation: 'auto', owner: '@duty',
105
+ { id: 'T5.1', kind: 'check_point', layer: 'type', title: '事件时间分布', costMinutes: 8, automation: 'auto', owner: '@duty',
106
+ assets: ['dwd_ord_di'],
120
107
  summary: '分区内事件时间分布。跨天 → 支持 H5。',
121
108
  verdictTrue: '分区内事件时间跨出 T-1 单日窗口', verdictFalse: '事件时间全部落在 T-1 内' },
122
- { id: 'T7.1', kind: 'check', layer: 'type', title: '查缓存时间戳', costMinutes: 2, automation: 'auto', owner: '@duty',
109
+ { id: 'T7.1', kind: 'check_point', layer: 'type', title: '查缓存时间戳', costMinutes: 2, automation: 'auto', owner: '@duty',
110
+ assets: ['bi_daily_dashboard'],
123
111
  summary: '缓存时间正常并不能否定 H7 —— 便宜检查给不出结论。',
124
112
  verdictTrue: '缓存时间戳过期', verdictFalse: '缓存时间正常 —— 注意:这一条**不足以**否定 H7' },
125
- { id: 'T7.2', kind: 'check', layer: 'type', title: '直连明细对账', costMinutes: 35, automation: 'manual', owner: '@analyst',
113
+ { id: 'T7.2', kind: 'check_point', layer: 'type', title: '直连明细对账', costMinutes: 35, automation: 'manual', owner: '@analyst',
114
+ assets: ['dwd_ord_di'],
126
115
  summary: '明细对账一致 → 否定 H7。要否定它必须做这次贵检查。',
127
116
  verdictTrue: '明细与报表数字不一致', verdictFalse: '明细与报表一致 → 否定 H7' },
128
117
 
129
118
  // ── 引用层 ────────────────────────────────────────────────────────────
130
- { id: 'dwd_ord_di', kind: 'asset', layer: 'reference', title: 'dwd_ord_di' },
131
- { id: 'ods_order_sync', kind: 'asset', layer: 'reference', title: 'ods_order_sync' },
132
- { id: 'dim_channel_df', kind: 'asset', layer: 'reference', title: 'dim_channel_df' },
119
+ { id: 'dwd_ord_di', kind: 'asset', layer: 'reference', title: 'dwd_ord_di', assetType: 'table' },
120
+ { id: 'ods_order_sync', kind: 'asset', layer: 'reference', title: 'ods_order_sync', assetType: 'job' },
121
+ { id: 'dim_channel_df', kind: 'asset', layer: 'reference', title: 'dim_channel_df', assetType: 'table' },
122
+ { id: 'metric_daily_gmv', kind: 'asset', layer: 'reference', title: 'metric_daily_gmv', assetType: 'metric' },
123
+ { id: 'bi_daily_dashboard', kind: 'asset', layer: 'reference', title: 'bi_daily_dashboard', assetType: 'linkage' },
133
124
 
134
- // ── 实例层(默认折叠,展开后用于演示「证据与复用」) ────────────────────
135
- // 案例节点是**图上的桩**:标题必须与 `SEED_CASES[].title` 是同一个字符串,否则同一份
136
- // 知识会有两份标题(而且还真的不一致过:「只剩」vs「只有」)。画布要在标签前显示案例 id,
125
+ // ── 实例层:案例节点是**图上的桩**(正文与有序路径住在案例卡片里) ──────
126
+ // 标题必须与 `SEED_CASES[].title` 是同一个字符串,否则同一份知识会有两份标题
127
+ // (而且还真的不一致过:「只剩」vs「只有」)。画布要在标签前显示案例 id,
137
128
  // 那是渲染层的事(见 `nodeStyleFor` 的 labelText)。
138
129
  { id: 'CASE-001', kind: 'case', layer: 'instance', title: '日报 GMV 为 0' },
139
130
  { id: 'CASE-002', kind: 'case', layer: 'instance', title: '订单数只有昨日 12%' },
140
131
  { id: 'CASE-003', kind: 'case', layer: 'instance', title: 'DAU 突增 3.1 倍' },
141
- { id: 'C1', kind: 'cause', layer: 'instance', title: 'C1 ODS 任务失败' },
142
- { id: 'C2', kind: 'cause', layer: 'instance', title: 'C2 渠道维表未补录' },
143
- { id: 'C3', kind: 'cause', layer: 'instance', title: 'C3 跨天事件补写' },
144
- { id: 'E1', kind: 'evidence', layer: 'instance', title: 'E1 分区不存在' },
145
- { id: 'E7', kind: 'evidence', layer: 'instance', title: 'E7 明细对账一致' },
146
132
  ],
147
133
  edges: [
148
134
  // 层内:现象 → 假设(含一条僵尸边,用来演示「权重过期」)
@@ -169,7 +155,7 @@ export const SEED_GRAPH: AttributionGraphData = {
169
155
  { id: 'e-s3-h5', source: 'S3', target: 'H5', kind: 'may_be_caused_by' },
170
156
  { id: 'e-s3-h7', source: 'S3', target: 'H7', kind: 'may_be_caused_by' },
171
157
 
172
- // 层内:假设 → 检查(H6 故意没有,它就是图上的空区)
158
+ // 层内:假设 → 检查点(H6 故意没有,它就是图上的空区)
173
159
  { id: 'e-h1-t11', source: 'H1', target: 'T1.1', kind: 'verified_by' },
174
160
  { id: 'e-h1-t12', source: 'H1', target: 'T1.2', kind: 'verified_by' },
175
161
  { id: 'e-h2-t21', source: 'H2', target: 'T2.1', kind: 'verified_by' },
@@ -179,7 +165,7 @@ export const SEED_GRAPH: AttributionGraphData = {
179
165
  { id: 'e-h7-t71', source: 'H7', target: 'T7.1', kind: 'verified_by' },
180
166
  { id: 'e-h7-t72', source: 'H7', target: 'T7.2', kind: 'verified_by' },
181
167
 
182
- // 层内:假设 → 失败模式(切面)
168
+ // 层内:假设 → 失败原因(切面)
183
169
  { id: 'e-h1-f1', source: 'H1', target: 'F1', kind: 'categorized_as' },
184
170
  { id: 'e-h2-f2', source: 'H2', target: 'F2', kind: 'categorized_as' },
185
171
  { id: 'e-h3-f3', source: 'H3', target: 'F3', kind: 'categorized_as' },
@@ -188,21 +174,21 @@ export const SEED_GRAPH: AttributionGraphData = {
188
174
  { id: 'e-h6-f5', source: 'H6', target: 'F5', kind: 'categorized_as' },
189
175
  { id: 'e-h7-f4', source: 'H7', target: 'F4', kind: 'categorized_as' },
190
176
 
191
- // 跨层:资产反向索引
177
+ // 跨层:资产反向索引(这条假设**落在**哪个对象上)
192
178
  { id: 'e-a-dwd-h1', source: 'dwd_ord_di', target: 'H1', kind: 'indexed_from' },
193
179
  { id: 'e-a-ods-h1', source: 'ods_order_sync', target: 'H1', kind: 'indexed_from' },
194
180
  { id: 'e-a-dim-h3', source: 'dim_channel_df', target: 'H3', kind: 'indexed_from' },
195
181
 
196
- // 跨层:检查产出证据、证据支持或否定假设
197
- { id: 'e-t11-e1', source: 'T1.1', target: 'E1', kind: 'produces' },
198
- { id: 'e-e1-h1', source: 'E1', target: 'H1', kind: 'supports' },
199
- { id: 'e-t72-e7', source: 'T7.2', target: 'E7', kind: 'produces' },
200
- { id: 'e-e7-h7', source: 'E7', target: 'H7', kind: 'refutes' },
201
-
202
- // 跨层:假设确认成根因
203
- { id: 'e-h1-c1', source: 'H1', target: 'C1', kind: 'confirmed_as' },
204
- { id: 'e-h3-c2', source: 'H3', target: 'C2', kind: 'confirmed_as' },
205
- { id: 'e-h5-c3', source: 'H5', target: 'C3', kind: 'confirmed_as' },
182
+ // 跨层:检查点 → 资产(`queries`,v1.3)。这条检查**要去查**哪些对象 ——
183
+ // 与上面那条 `indexed_from` 是两条边:两端不同,回答的问题也不同。
184
+ { id: 'e-t11-dwd-q', source: 'T1.1', target: 'dwd_ord_di', kind: 'queries' },
185
+ { id: 'e-t12-ods-q', source: 'T1.2', target: 'ods_order_sync', kind: 'queries' },
186
+ { id: 'e-t21-metric-q', source: 'T2.1', target: 'metric_daily_gmv', kind: 'queries' },
187
+ { id: 'e-t31-dim-q', source: 'T3.1', target: 'dim_channel_df', kind: 'queries' },
188
+ { id: 'e-t41-dwd-q', source: 'T4.1', target: 'dwd_ord_di', kind: 'queries' },
189
+ { id: 'e-t51-dwd-q', source: 'T5.1', target: 'dwd_ord_di', kind: 'queries' },
190
+ { id: 'e-t71-bi-q', source: 'T7.1', target: 'bi_daily_dashboard', kind: 'queries' },
191
+ { id: 'e-t72-dwd-q', source: 'T7.2', target: 'dwd_ord_di', kind: 'queries' },
206
192
 
207
193
  // 跨层:案例实例化(三个案例各归各的现象,但收敛到**同一批假设**)
208
194
  { id: 'e-case1-s1', source: 'CASE-001', target: 'S1', kind: 'instance_of', count: 1 },
@@ -211,7 +197,7 @@ export const SEED_GRAPH: AttributionGraphData = {
211
197
  ],
212
198
  }
213
199
 
214
- /** 三个 seed 案例,与上面图谱里的节点 id 对齐(0021 §11)。 */
200
+ /** 三个 seed 案例卡片,与上面图谱里的节点 id 对齐(0021 §11)。 */
215
201
  export const SEED_CASES: AttributionCase[] = [
216
202
  {
217
203
  id: 'CASE-001',
@@ -289,3 +275,119 @@ export const SEED_CASES: AttributionCase[] = [
289
275
  ],
290
276
  },
291
277
  ]
278
+
279
+ /**
280
+ * 三次排查的**过程记录**(`agent_diagnosis_case`)。
281
+ *
282
+ * 它们与 `SEED_CASES` 一一对应,但**不是同一份东西**:这里是 Agent 跑出来的原始记录
283
+ * (每条检查点真跑了什么、看到什么、这个结果算不算数),`SEED_CASES` 是之后归纳出来的
284
+ * 案例卡片(要经人确认才生效)。一个回答「当时怎么走的」,一个回答「结论是什么」。
285
+ *
286
+ * `E7` 是 CASE-001 里那次**贵的否定**:T7.2 直连明细对账推翻了 H7。它不在案例的
287
+ * 主路径上(主路径早就确认了 H1),但它是这次排查最贵的一条知识 —— 40 分钟的误导段
288
+ * 正是靠它收尾的。
289
+ */
290
+ export const SEED_RUNS: DiagnosisRunRecord[] = [
291
+ {
292
+ runId: 'RUN-20260811-1',
293
+ symptomId: 'S1',
294
+ startedAt: '2026-08-11T02:55:00+08:00',
295
+ durationMinutes: 65,
296
+ path: SEED_CASES[0]!.path,
297
+ evidence: [
298
+ {
299
+ id: 'E1', checkPointId: 'T1.1',
300
+ query: "select count(*) from dwd_ord_di where dt = '2026-08-10'",
301
+ result: '分区不存在', verdict: 'supports', hypothesisId: 'H1',
302
+ why: '与调度系统日志一致:ods_order_sync 02:40 实例 failed 且未启用重试,下游无输入',
303
+ at: '2026-08-11T02:58:00+08:00', by: '@duty', rawRef: 'scheduler://ods_order_sync/instance/20260811',
304
+ },
305
+ {
306
+ id: 'E7', checkPointId: 'T7.2',
307
+ query: "select sum(gmv) from dwd_ord_di_detail where dt = '2026-08-10'",
308
+ result: '明细与报表一致', verdict: 'refutes', hypothesisId: 'H7',
309
+ why: '明细对账一致,缓存不影响明细 —— 这一条花了 35 分钟,是唯一能否定 H7 的检查',
310
+ at: '2026-08-11T03:40:00+08:00', by: '@analyst',
311
+ },
312
+ ],
313
+ rootCause: {
314
+ hypothesisId: 'H1', id: 'C1',
315
+ text: 'ODS 任务 ods_order_sync 02:40 失败且未启用重试,dwd_ord_di/dt 分区未产出。',
316
+ fixAction: '补 dwd_ord_di 分区;给 ods_order_sync 加失败告警与自动重试;分区存在性检查加入日报前置校验。',
317
+ confirmedAt: '2026-08-11T04:00:00+08:00',
318
+ },
319
+ misleading: SEED_CASES[0]!.misleading ? [SEED_CASES[0]!.misleading] : [],
320
+ },
321
+ {
322
+ runId: 'RUN-20260812-1',
323
+ symptomId: 'S2',
324
+ startedAt: '2026-08-12T09:10:00+08:00',
325
+ durationMinutes: 22,
326
+ path: SEED_CASES[1]!.path,
327
+ evidence: [
328
+ {
329
+ id: 'E2a', checkPointId: 'T1.1',
330
+ query: "select count(*) from dwd_ord_di where dt = '2026-08-11'",
331
+ result: '分区存在且有数据', verdict: 'refutes', hypothesisId: 'H1',
332
+ why: '分区有数据,H1 不成立 —— 1 分钟就砍掉了一条先验最高的假设',
333
+ at: '2026-08-12T09:12:00+08:00', by: '@duty',
334
+ },
335
+ {
336
+ id: 'E2b', checkPointId: 'T4.1',
337
+ query: "select order_id, count(*) c from dwd_ord_di where dt = '2026-08-11' group by order_id having c > 1 limit 10",
338
+ result: '主键唯一,无重复', verdict: 'refutes', hypothesisId: 'H4',
339
+ why: '主键唯一性检查通过,无重复行 —— H4 被否定',
340
+ at: '2026-08-12T09:16:00+08:00', by: '@duty',
341
+ },
342
+ {
343
+ id: 'E2c', checkPointId: 'T3.1',
344
+ query: "select channel, count(*) from dwd_ord_di where dt = '2026-08-11' group by channel order by 2 desc",
345
+ result: 'unknown 占比 88%', verdict: 'supports', hypothesisId: 'H3',
346
+ why: 'unknown 占比 88%,且缺口集中在新增的两个渠道 —— 与维表未补录一致',
347
+ at: '2026-08-12T09:24:00+08:00', by: '@duty',
348
+ },
349
+ ],
350
+ rootCause: {
351
+ hypothesisId: 'H3', id: 'C2',
352
+ text: '新增渠道未补录维表,dim_channel_df join 落 unknown 后被 where channel is not null 过滤。',
353
+ fixAction: '补录渠道维表;把 unknown 占比纳入日常巡检;下游过滤改为「先报警再过滤」。',
354
+ confirmedAt: '2026-08-12T09:32:00+08:00',
355
+ },
356
+ },
357
+ {
358
+ runId: 'RUN-20260813-1',
359
+ symptomId: 'S3',
360
+ startedAt: '2026-08-13T14:05:00+08:00',
361
+ durationMinutes: 35,
362
+ path: SEED_CASES[2]!.path,
363
+ evidence: [
364
+ {
365
+ id: 'E3a', checkPointId: 'T4.1',
366
+ query: "select user_id, count(*) c from dwd_dau_di where dt = '2026-08-12' group by user_id having c > 1 limit 10",
367
+ result: '主键唯一,无重复', verdict: 'refutes', hypothesisId: 'H4',
368
+ why: '主键唯一性通过 —— 放大不是重复计数造成的',
369
+ at: '2026-08-13T14:10:00+08:00', by: '@duty',
370
+ },
371
+ {
372
+ id: 'E3b', checkPointId: 'T7.1',
373
+ query: 'select max(refresh_at) from bi_daily_dashboard_meta',
374
+ result: '缓存时间正常', verdict: 'inconclusive', hypothesisId: 'H7',
375
+ why: '缓存时间正常并不能否定 H7(T7.1 的 verdict_false 已经写明);要否定它必须做 35 分钟的直连明细对账 —— 这一条给不出结论',
376
+ at: '2026-08-13T14:18:00+08:00', by: '@duty',
377
+ },
378
+ {
379
+ id: 'E3c', checkPointId: 'T5.1',
380
+ query: "select event_time, count(*) from dwd_dau_di where dt = '2026-08-12' group by 1 order by 1 limit 5",
381
+ result: '分区内事件时间跨天', verdict: 'supports', hypothesisId: 'H5',
382
+ why: '事件时间跨出 T-1 单日窗口,且只有这一天异常 —— 与上游重跑补写一致',
383
+ at: '2026-08-13T14:34:00+08:00', by: '@duty',
384
+ },
385
+ ],
386
+ rootCause: {
387
+ hypothesisId: 'H5', id: 'C3',
388
+ text: '上游重跑把跨天事件补写进 T-1 分区,事件时间与处理时间边界被跨越。',
389
+ fixAction: '重跑加事件时间边界校验;分区写入前检查事件时间下界。',
390
+ confirmedAt: '2026-08-13T14:40:00+08:00',
391
+ },
392
+ },
393
+ ]