clearai-dsh 0.3.0 → 0.4.0

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 (148) hide show
  1. package/CHANGELOG.md +147 -0
  2. package/README.md +28 -28
  3. package/README.zh-CN.md +28 -28
  4. package/lib/client.js +861 -2555
  5. package/lib/domain-language.js +552 -48
  6. package/lib/fold.js +351 -1015
  7. package/lib/host.js +47 -557
  8. package/lib/invariant.js +9 -12
  9. package/lib/knowledge-view.js +363 -226
  10. package/lib/lang.js +81 -0
  11. package/package.json +1 -1
  12. package/presets/clearai/agent.cordis.yml +54 -82
  13. package/presets/clearai/clearai.patch.yml +54 -82
  14. package/presets/clearai/plugins/clearai-kernel.js +1590 -5437
  15. package/presets/clearai/plugins/ontology.js +56 -14
  16. package/presets/clearai/plugins/prompts.js +73 -363
  17. package/presets/clearai/skills/clearai-loop/SKILL.md +73 -59
  18. package/presets/clearai/plugins/brain.js +0 -547
  19. package/presets/clearai/plugins/commands.js +0 -199
  20. package/presets/clearai/template/knowledge/README.md +0 -25
  21. package/presets/clearai/template/memory/README.md +0 -34
  22. package/presets/clearai/template/project.md +0 -49
  23. package/presets/clearai/template/skills/README.md +0 -37
  24. package/presets/clearai/template/skills/chart-diagram-qa/SKILL.md +0 -43
  25. package/presets/clearai/template/skills/citation-management/SKILL.md +0 -73
  26. package/presets/clearai/template/skills/citation-management/references/bibtex_formatting.md +0 -908
  27. package/presets/clearai/template/skills/citation-management/references/citation_validation.md +0 -794
  28. package/presets/clearai/template/skills/citation-management/references/google_scholar_search.md +0 -725
  29. package/presets/clearai/template/skills/citation-management/references/metadata_extraction.md +0 -870
  30. package/presets/clearai/template/skills/citation-management/references/pubmed_search.md +0 -839
  31. package/presets/clearai/template/skills/citation-management/scripts/doi_to_bibtex.py +0 -204
  32. package/presets/clearai/template/skills/citation-management/scripts/extract_metadata.py +0 -569
  33. package/presets/clearai/template/skills/citation-management/scripts/format_bibtex.py +0 -349
  34. package/presets/clearai/template/skills/citation-management/scripts/generate_schematic.py +0 -139
  35. package/presets/clearai/template/skills/citation-management/scripts/generate_schematic_ai.py +0 -817
  36. package/presets/clearai/template/skills/citation-management/scripts/search_google_scholar.py +0 -282
  37. package/presets/clearai/template/skills/citation-management/scripts/search_pubmed.py +0 -398
  38. package/presets/clearai/template/skills/citation-management/scripts/validate_citations.py +0 -497
  39. package/presets/clearai/template/skills/data-analysis/SKILL.md +0 -92
  40. package/presets/clearai/template/skills/data-analysis/checklists/readiness_check.md +0 -23
  41. package/presets/clearai/template/skills/data-analysis/templates/analysis_report.md.tpl +0 -63
  42. package/presets/clearai/template/skills/data-analysis/templates/cleaning_rules_draft.yaml.tpl +0 -32
  43. package/presets/clearai/template/skills/data-analysis/templates/data_dictionary.md.tpl +0 -12
  44. package/presets/clearai/template/skills/data-analysis/templates/domain_knowledge_template.md.tpl +0 -316
  45. package/presets/clearai/template/skills/data-analysis/templates/feature_candidates.json.tpl +0 -20
  46. package/presets/clearai/template/skills/data-analysis/templates/quality_scorecard.md.tpl +0 -30
  47. package/presets/clearai/template/skills/data-analysis/workflows/01-data-profiling.md +0 -42
  48. package/presets/clearai/template/skills/data-analysis/workflows/02-quality-audit.md +0 -36
  49. package/presets/clearai/template/skills/data-analysis/workflows/03-physical-correlation.md +0 -25
  50. package/presets/clearai/template/skills/data-analysis/workflows/04-unstructured-mining.md +0 -26
  51. package/presets/clearai/template/skills/data-qa-analysis/SKILL.md +0 -102
  52. package/presets/clearai/template/skills/data-qa-analysis/checklists/readiness_check.md +0 -62
  53. package/presets/clearai/template/skills/data-qa-analysis/templates/best_in_class_report.md.tpl +0 -56
  54. package/presets/clearai/template/skills/data-qa-analysis/templates/cleaning_rules_draft.yaml.tpl +0 -56
  55. package/presets/clearai/template/skills/data-qa-analysis/templates/data_dictionary.md.tpl +0 -13
  56. package/presets/clearai/template/skills/data-qa-analysis/templates/data_source_inventory_and_lineage.md.tpl +0 -146
  57. package/presets/clearai/template/skills/data-qa-analysis/templates/data_status_report.md.tpl +0 -60
  58. package/presets/clearai/template/skills/data-qa-analysis/templates/steady_state_rules.yaml.tpl +0 -41
  59. package/presets/clearai/template/skills/data-qa-analysis/templates/subsystem_registry.md.tpl +0 -101
  60. package/presets/clearai/template/skills/data-qa-analysis/templates/unified_execution_plan.md.tpl +0 -100
  61. package/presets/clearai/template/skills/data-qa-analysis/workflows/01-data-source-inventory-and-lineage.md +0 -194
  62. package/presets/clearai/template/skills/data-qa-analysis/workflows/02-data-alignment-and-tag-semantics.md +0 -122
  63. package/presets/clearai/template/skills/data-qa-analysis/workflows/03-steady-state-identification.md +0 -126
  64. package/presets/clearai/template/skills/data-qa-analysis/workflows/04-consumption-analysis.md +0 -152
  65. package/presets/clearai/template/skills/data-qa-analysis/workflows/05-best-in-class-and-optimization-space.md +0 -78
  66. package/presets/clearai/template/skills/domain-presearch/SKILL.md +0 -131
  67. package/presets/clearai/template/skills/domain-presearch/checklists/domain_checklist.md +0 -24
  68. package/presets/clearai/template/skills/domain-presearch/references/figure_code.md +0 -78
  69. package/presets/clearai/template/skills/domain-presearch/references/strategic_frameworks.md +0 -38
  70. package/presets/clearai/template/skills/exploration-loop/SKILL.md +0 -81
  71. package/presets/clearai/template/skills/exploratory-data-analysis/SKILL.md +0 -77
  72. package/presets/clearai/template/skills/exploratory-data-analysis/references/bioinformatics_genomics_formats.md +0 -664
  73. package/presets/clearai/template/skills/exploratory-data-analysis/references/chemistry_molecular_formats.md +0 -664
  74. package/presets/clearai/template/skills/exploratory-data-analysis/references/general_scientific_formats.md +0 -518
  75. package/presets/clearai/template/skills/exploratory-data-analysis/references/microscopy_imaging_formats.md +0 -620
  76. package/presets/clearai/template/skills/exploratory-data-analysis/references/proteomics_metabolomics_formats.md +0 -517
  77. package/presets/clearai/template/skills/exploratory-data-analysis/references/spectroscopy_analytical_formats.md +0 -633
  78. package/presets/clearai/template/skills/exploratory-data-analysis/scripts/eda_analyzer.py +0 -547
  79. package/presets/clearai/template/skills/hypothesis-generation/SKILL.md +0 -73
  80. package/presets/clearai/template/skills/hypothesis-generation/references/experimental_design_patterns.md +0 -329
  81. package/presets/clearai/template/skills/hypothesis-generation/references/hypothesis_quality_criteria.md +0 -198
  82. package/presets/clearai/template/skills/hypothesis-generation/references/literature_search_strategies.md +0 -622
  83. package/presets/clearai/template/skills/hypothesis-generation/scripts/generate_schematic.py +0 -139
  84. package/presets/clearai/template/skills/hypothesis-generation/scripts/generate_schematic_ai.py +0 -817
  85. package/presets/clearai/template/skills/literature-review/SKILL.md +0 -72
  86. package/presets/clearai/template/skills/literature-review/references/citation_styles.md +0 -166
  87. package/presets/clearai/template/skills/literature-review/references/database_strategies.md +0 -455
  88. package/presets/clearai/template/skills/literature-review/scripts/generate_pdf.py +0 -176
  89. package/presets/clearai/template/skills/literature-review/scripts/generate_schematic.py +0 -139
  90. package/presets/clearai/template/skills/literature-review/scripts/generate_schematic_ai.py +0 -817
  91. package/presets/clearai/template/skills/literature-review/scripts/search_databases.py +0 -303
  92. package/presets/clearai/template/skills/literature-review/scripts/verify_citations.py +0 -221
  93. package/presets/clearai/template/skills/paper-lookup/SKILL.md +0 -59
  94. package/presets/clearai/template/skills/paper-lookup/references/arxiv.md +0 -161
  95. package/presets/clearai/template/skills/paper-lookup/references/biorxiv.md +0 -118
  96. package/presets/clearai/template/skills/paper-lookup/references/core.md +0 -150
  97. package/presets/clearai/template/skills/paper-lookup/references/crossref.md +0 -181
  98. package/presets/clearai/template/skills/paper-lookup/references/medrxiv.md +0 -104
  99. package/presets/clearai/template/skills/paper-lookup/references/openalex.md +0 -174
  100. package/presets/clearai/template/skills/paper-lookup/references/pmc.md +0 -152
  101. package/presets/clearai/template/skills/paper-lookup/references/pubmed.md +0 -124
  102. package/presets/clearai/template/skills/paper-lookup/references/semantic-scholar.md +0 -203
  103. package/presets/clearai/template/skills/paper-lookup/references/unpaywall.md +0 -127
  104. package/presets/clearai/template/skills/process-presearch/SKILL.md +0 -196
  105. package/presets/clearai/template/skills/process-presearch/checklists/process_checklist.md +0 -18
  106. package/presets/clearai/template/skills/process-presearch/references/figure_code.md +0 -107
  107. package/presets/clearai/template/skills/process-presearch/references/source_attribution_example.md +0 -22
  108. package/presets/clearai/template/skills/process-understanding-extraction/SKILL.md +0 -69
  109. package/presets/clearai/template/skills/process-understanding-extraction/checklists/readiness_check.md +0 -34
  110. package/presets/clearai/template/skills/process-understanding-extraction/templates/docx_raw_dump_extractor.py.tpl +0 -132
  111. package/presets/clearai/template/skills/process-understanding-extraction/templates/entity_map_unit_topology.json.tpl +0 -86
  112. package/presets/clearai/template/skills/process-understanding-extraction/templates/process_brief.md.tpl +0 -89
  113. package/presets/clearai/template/skills/process-understanding-extraction/templates/process_brief_builder_from_raw_dump.py.tpl +0 -203
  114. package/presets/clearai/template/skills/process-understanding-extraction/templates/process_flow_mermaid.md.tpl +0 -41
  115. package/presets/clearai/template/skills/process-understanding-extraction/templates/unified_execution_plan.md.tpl +0 -53
  116. package/presets/clearai/template/skills/process-understanding-extraction/workflows/01-process-doc-discovery.md +0 -173
  117. package/presets/clearai/template/skills/process-understanding-extraction/workflows/02-process-understanding-and-diagramming.md +0 -106
  118. package/presets/clearai/template/skills/scientific-brainstorming/SKILL.md +0 -64
  119. package/presets/clearai/template/skills/scientific-brainstorming/references/brainstorming_methods.md +0 -326
  120. package/presets/clearai/template/skills/scientific-critical-thinking/SKILL.md +0 -72
  121. package/presets/clearai/template/skills/scientific-critical-thinking/references/common_biases.md +0 -364
  122. package/presets/clearai/template/skills/scientific-critical-thinking/references/evidence_hierarchy.md +0 -485
  123. package/presets/clearai/template/skills/scientific-critical-thinking/references/experimental_design.md +0 -496
  124. package/presets/clearai/template/skills/scientific-critical-thinking/references/logical_fallacies.md +0 -478
  125. package/presets/clearai/template/skills/scientific-critical-thinking/references/scientific_method.md +0 -169
  126. package/presets/clearai/template/skills/scientific-critical-thinking/references/statistical_pitfalls.md +0 -506
  127. package/presets/clearai/template/skills/skill-creator/SKILL.md +0 -109
  128. package/presets/clearai/template/skills/skill-creator/references/authoring-guide.md +0 -89
  129. package/presets/clearai/template/skills/statistical-analysis/SKILL.md +0 -79
  130. package/presets/clearai/template/skills/statistical-analysis/references/assumptions_and_diagnostics.md +0 -369
  131. package/presets/clearai/template/skills/statistical-analysis/references/bayesian_statistics.md +0 -653
  132. package/presets/clearai/template/skills/statistical-analysis/references/effect_sizes_and_power.md +0 -578
  133. package/presets/clearai/template/skills/statistical-analysis/references/reporting_standards.md +0 -469
  134. package/presets/clearai/template/skills/statistical-analysis/references/test_selection_guide.md +0 -129
  135. package/presets/clearai/template/skills/statistical-analysis/scripts/assumption_checks.py +0 -538
  136. package/presets/clearai/template/skills/web-artifact/SKILL.md +0 -165
  137. package/presets/clearai/template/skills/web-artifact/assets/renderer/renderer.css +0 -229
  138. package/presets/clearai/template/skills/web-artifact/assets/renderer/renderer.js +0 -373
  139. package/presets/clearai/template/skills/web-artifact/assets/vendor/elkjs/LICENSE +0 -263
  140. package/presets/clearai/template/skills/web-artifact/assets/vendor/elkjs/UPSTREAM.md +0 -26
  141. package/presets/clearai/template/skills/web-artifact/assets/vendor/elkjs/elk.bundled.js +0 -6605
  142. package/presets/clearai/template/skills/web-artifact/references/when-drawing-a-topology.md +0 -150
  143. package/presets/clearai/template/skills/web-artifact/references/when-the-page-must-work-offline.md +0 -62
  144. package/presets/clearai/template/skills/web-artifact/scripts/check_artifact.py +0 -167
  145. package/presets/clearai/template/skills/web-artifact/scripts/render_topology.js +0 -272
  146. package/presets/clearai/template/skills/what-if-oracle/LICENSE.txt +0 -5
  147. package/presets/clearai/template/skills/what-if-oracle/SKILL.md +0 -72
  148. package/presets/clearai/template/skills/what-if-oracle/references/scenario-templates.md +0 -154
@@ -1,150 +0,0 @@
1
- # 画一张拓扑图
2
-
3
- 流程与空间拓扑图这一门类的具体工艺。目标是让关系可读到足以评审与建模讨论,不是复刻一张
4
- 完整的 PFD——每条 DCS 管线、每个阀门、每种公用介质都画上去,反而没人能看出接错了哪一根。
5
-
6
- ## 布局契约是 ELK JSON
7
-
8
- ```text
9
- 工艺理解 → ELK 输入 JSON → ELK 输出 JSON → 单文件 HTML
10
- ```
11
-
12
- 不要先去设计一套完美的通用图 schema。先把这张拓扑画得能看懂;等几张图之后稳定的结构自己
13
- 浮现出来,再谈抽象。中间的输入与输出 JSON 都留盘,这样布局质量的问题和渲染的问题能分开
14
- 定位——图不好看时,先看是 ELK 排得不好,还是渲染器画错了。
15
-
16
- ```bash
17
- node scripts/render_topology.js \
18
- --elk-input topo.json \
19
- --elk-output topo.laid.json \
20
- --out topo.html \
21
- --title "蒸发工段拓扑检查" \
22
- --source topo.json \
23
- --proves "设备间的物料与公用工程连接关系" \
24
- --not-proves "阀门、控制回路与管径"
25
- ```
26
-
27
- ## 参数基线
28
-
29
- 工业流程链走 `layered`,从左到右,正交走线:
30
-
31
- ```json
32
- {
33
- "id": "root",
34
- "layoutOptions": {
35
- "elk.algorithm": "layered",
36
- "elk.direction": "RIGHT",
37
- "elk.edgeRouting": "ORTHOGONAL",
38
- "elk.layered.considerModelOrder.strategy": "NODES_AND_EDGES",
39
- "elk.layered.cycleBreaking.strategy": "MODEL_ORDER",
40
- "elk.layered.nodePlacement.strategy": "BRANDES_KOEPF",
41
- "elk.layered.crossingMinimization.strategy": "LAYER_SWEEP",
42
- "elk.layered.unnecessaryBendpoints": "true",
43
- "elk.spacing.nodeNode": "34",
44
- "elk.spacing.edgeNode": "22",
45
- "elk.spacing.edgeEdge": "14",
46
- "elk.spacing.labelNode": "8",
47
- "elk.layered.spacing.nodeNodeBetweenLayers": "62"
48
- },
49
- "children": [
50
- {"id": "feed", "width": 150, "height": 46,
51
- "labels": [{"text": "过滤来精液"}, {"text": "Inlet"}],
52
- "properties": {"位号": "V-101"}},
53
- {"id": "plate", "width": 150, "height": 46,
54
- "labels": [{"text": "板换组"}, {"text": "HeatExchanger"}]}
55
- ],
56
- "edges": [
57
- {"id": "m01", "sources": ["feed"], "targets": ["plate"],
58
- "labels": [{"text": "精液"}],
59
- "properties": {"role": "main", "medium": "精液", "relation": "主物料"}}
60
- ]
61
- }
62
- ```
63
-
64
- `labels[0]` 是节点主名,`labels[1]` 渲染成副标题。`properties` 里除 `role` 与 `elk.*`
65
- 之外的键会原样透传到侧栏——位号、设计参数、出处、备注都可以放,渲染器不挑。
66
-
67
- ## 关系分类
68
-
69
- 每条可见的边都该声明 `properties.role`,它是边的颜色与线型的唯一来源:
70
-
71
- | role | 含义 |
72
- |---|---|
73
- | `main` | 主物料/主工艺链 |
74
- | `branch` | 次要物料支线或旁路去向 |
75
- | `seed` | 晶种、料浆一类的返回关系 |
76
- | `recycle` | 母液、滤液、溢流、循环回用 |
77
- | `utility` | 水、汽、风、清洗、冷却、控制等公用介质 |
78
- | `unknown` | 有意义但尚未确认的关系 |
79
-
80
- **工业拓扑图里缺 role 是缺陷**:一张图上分不出主链与公用管线,评审的人得逐条问你。生成器
81
- 遇到缺 role 的边会给出提示,并按中性样式渲染——它不会替你伪造成 `unknown`,因为「作者说这
82
- 条不确定」和「这张图不用关系分类」是两回事,混在一起会让极简图整张报成存疑。
83
-
84
- 拿不准某条关系时,显式写 `role: "unknown"`,把不确定留在 `properties.note` 里。这些边会被
85
- 自动汇总进页面顶部的入口区,专家一打开就先看它们。
86
-
87
- ## 排序规则
88
-
89
- 主链清楚时:
90
-
91
- 1. `children` 里主链节点排在最前,按工艺顺序。
92
- 2. `edges` 里主链边排在最前,按工艺顺序。
93
- 3. 旁系、返回、支线节点排在主链之后。
94
- 4. 保留 `considerModelOrder.strategy: NODES_AND_EDGES`。
95
- 5. 有回路把主链顺序搅乱时,用 `cycleBreaking.strategy: MODEL_ORDER`。
96
-
97
- 这不只是好看的问题。工业图里回路很常见,而 ELK 默认会优先考虑断环,断出来的顺序未必符合
98
- 工艺上的心智顺序。model order 是把「你认为的主链是哪条」这件事告诉 ELK 的唯一途径。
99
-
100
- 若某条返回边在视觉上压过了主链,可以只为布局把它反向,同时在渲染出的元数据里保留真实
101
- 流向——用了就在 `properties.note` 里写明,别让后来者以为工艺反了。
102
-
103
- ## 密度只调 ELK
104
-
105
- 标签贴到节点、走线挤在一起时,调 spacing,不要去改坐标:
106
-
107
- - `elk.layered.spacing.nodeNodeBetweenLayers` — 横向段落的长短
108
- - `elk.spacing.edgeNode` — 线离节点太近时
109
- - `elk.spacing.edgeEdge` — 并行边挤在一起时
110
- - `elk.spacing.labelNode` — 标签离线或节点太近时
111
-
112
- 先从紧凑值起步,撞上了再放。
113
-
114
- ## 节点里放什么
115
-
116
- 克制。概览图的节点只放两行:
117
-
118
- ```text
119
- 设备名
120
- 简短类型
121
- ```
122
-
123
- 变量表、DCS 点位、长假设、会撑大节点的徽标,都不要塞进节点——它们让每个盒子尺寸不可预测,
124
- 布局随之抖动。这些内容放侧栏,通过 `properties` 透传过去,点选时再看。
125
-
126
- ## 边标签
127
-
128
- 短,面向介质:`精液`、`冷却精液`、`精液+细晶种`、`附聚料浆`、`粗种子`、`母液/滤液`。
129
-
130
- 解释性的长句放侧栏,不放线上。一条线上挂一句话,图就没法看了。
131
-
132
- ## 验证
133
-
134
- 生成后在浏览器里确认(用 http origin,`file://` 下某些宿主不跑 requestAnimationFrame):
135
-
136
- ```js
137
- window.__RENDERED__ // {nodes, edges, labels} —— 与源数据计数比对
138
- document.querySelectorAll('.node').length
139
- document.querySelectorAll('.edge').length
140
- document.querySelectorAll('.legend label').length // 关系类别数
141
- ```
142
-
143
- 要看的是:页面不空白、控制台无错误、节点与边的数量对得上、多种关系并存时图例里能分辨、
144
- 缩放平移只影响画布而不动页面其他部分。
145
-
146
- ## 以后再抽象
147
-
148
- 几张图跑顺之后,再考虑把工艺事实抽成 `graph.yaml`、把视觉意图抽成 `view.yaml`、把点位绑定
149
- 抽成 `data_bindings.yaml`,以及一个从它们生成 ELK JSON 的转换器。在那之前,ELK JSON 保持
150
- 显式且可检查——它现在就是布局契约。
@@ -1,62 +0,0 @@
1
- # 页面必须能离线打开
2
-
3
- 制品的读者常常不在 Workbench 里,也未必有网。会议室、客户现场、飞机上、内网隔离的工位——
4
- 一份打开是白屏的评审件等于没交付。
5
-
6
- ## 约束的本意
7
-
8
- **运行时零网络请求。** 构建期随便联网(本技能自己就在构建期跑 ELK 布局、算摘要)。
9
-
10
- 把本意记住比记住禁令有用。「不许引 CDN」只覆盖了一种情况,而实际会漏网的有好几处:
11
-
12
- - Google Fonts 也是外部请求。所以字体只用系统栈,`system-ui` 打头,中文补
13
- `PingFang SC` 与 `Microsoft YaHei`。想要更强的字体个性,代价是把字体文件内联成
14
- data URI——通常不值得,除非是对外交付档。
15
- - 图片走 data URI,或者干脆用 SVG 画出来。
16
- - `<link rel="preconnect">`、`<link rel="dns-prefetch">` 这类提示也删掉,它们没用且误导。
17
- - 动态 `import()` 一个裸模块名同样会失败,因为没有 import map,也不该有。
18
-
19
- `scripts/check_artifact.py` 会把这几类都扫一遍。
20
-
21
- ## 体积预算
22
-
23
- 原生渲染器的产物大致这样构成:
24
-
25
- | 部分 | 量级 |
26
- |---|---|
27
- | 渲染器 JS | ~13 KB |
28
- | 样式 | ~6 KB |
29
- | 页面骨架 | ~2 KB |
30
- | 数据 | 随规模,7 节点约 5 KB |
31
- | **合计** | **20–40 KB** |
32
-
33
- 作为对照,内联 React + ReactDOM + 一个图库轻松到 450 KB 以上,换来的能力在检查类制品上
34
- 基本用不到。详见 [SKILL.md](../SKILL.md) 的「为什么不用框架」。
35
-
36
- 真需要一个重型库时(比如某种专门的图表),先问它是否只在构建期需要——布局、统计、聚合都
37
- 可以在生成时算完,把结果写进数据,浏览器端只负责画。本技能的 ELK 就是这么用的:1.6 MB 的
38
- 布局引擎跑在 node 端,一个字节都没进产物。
39
-
40
- ## 与 App 侧 freeze 的区别
41
-
42
- 两者都产出一个能双击打开的自包含文件,来路完全不同:
43
-
44
- | | 本技能的制品 | App 侧的 freeze |
45
- |---|---|---|
46
- | 来源 | 数据文件 | 运行中的 App 实例 |
47
- | 重新生成 | 改数据再跑一次生成器 | 需要实例还活着 |
48
- | 版本标识 | 源数据摘要 | source revision |
49
- | 场景 | 还没建 App,或根本不需要 | 已有 App,要给圈外人看当前状态 |
50
-
51
- 判断见「你可能还不需要 App」
52
- 一节。
53
-
54
- ## 自查
55
-
56
- ```bash
57
- grep -oE 'https?://[^"'"'"' )]+' out/artifact.html | grep -v w3.org
58
- python3 scripts/check_artifact.py out/artifact.html --source data.json
59
- ```
60
-
61
- 真要确认,断网后双击打开一次。这一步只花十秒,而它挡住的是「专家打开是白屏」这种当场
62
- 丢脸的事故。
@@ -1,167 +0,0 @@
1
- #!/usr/bin/env python3
2
- """单文件网页制品的确定性检查。
3
-
4
- 能规则化的判断不交给 LLM。全部 PASS 才算可交付。
5
-
6
- python3 check_artifact.py out/topology.html [--source data.json]
7
- """
8
- from __future__ import annotations
9
-
10
- import argparse
11
- import json
12
- import re
13
- import sys
14
- from pathlib import Path
15
-
16
- FAILED: list[str] = []
17
- PASSED = 0
18
- RESULTS: list[dict] = []
19
-
20
- # SVG/XML 命名空间是标识符,浏览器不会去取它
21
- ALLOWED_URLS = {
22
- "http://www.w3.org/2000/svg",
23
- "http://www.w3.org/1999/xlink",
24
- "http://www.w3.org/XML/1998/namespace",
25
- }
26
-
27
-
28
- def check(name: str, ok: bool, detail: str = "") -> None:
29
- global PASSED
30
- RESULTS.append({"check": name, "ok": bool(ok), "detail": detail})
31
- if ok:
32
- PASSED += 1
33
- else:
34
- FAILED.append(name)
35
-
36
-
37
- def check_offline(html: str) -> None:
38
- """运行时零网络请求:产物在没网的环境里也要完整打开。"""
39
- urls = {u.rstrip('\'").,;') for u in re.findall(r"https?://[^\s\"'<>)]+", html)}
40
- leaked = sorted(u for u in urls if u not in ALLOWED_URLS)
41
- check("offline.no_external_url", not leaked,
42
- f"{len(leaked)} 处外部引用:{leaked[:3]}")
43
-
44
- tags = re.findall(r"<(script|link|img|iframe|source|video|audio)\b[^>]*>", html, re.I)
45
- external = [t for t in tags if re.search(r"\b(src|href)\s*=\s*[\"'](?!#|data:)", t)]
46
- check("offline.no_external_asset_tag", not external, f"{external[:2]}")
47
-
48
- check("offline.no_import_map", "importmap" not in html)
49
- check("offline.no_bare_import",
50
- not re.search(r"\bimport\s+.*\bfrom\s+[\"'](?!\.|/|data:)", html))
51
-
52
-
53
- def check_provenance(html: str) -> dict:
54
- """制品要自陈来历:从什么生成、什么时候、生成器是谁。"""
55
- m = re.search(r'<script id="artifact-data"[^>]*>(.*?)</script>', html, re.S)
56
- check("data.embedded", bool(m), "找不到内嵌数据块")
57
- if not m:
58
- return {}
59
- try:
60
- data = json.loads(m.group(1).replace("\\u003c", "<"))
61
- except json.JSONDecodeError as exc:
62
- check("data.valid_json", False, str(exc))
63
- return {}
64
- check("data.valid_json", True)
65
-
66
- prov = data.get("provenance") or {}
67
- check("provenance.generated_at", bool(prov.get("generatedAt")))
68
- check("provenance.generator", bool(prov.get("generator")))
69
-
70
- footer = re.search(r"<footer>(.*?)</footer>", html, re.S)
71
- check("provenance.visible_in_page",
72
- bool(footer and prov.get("generator", "?") in footer.group(1)),
73
- "页脚没有把来历写出来,读者看不到")
74
- return data
75
-
76
-
77
- def check_graph(data: dict, source: Path | None) -> None:
78
- graph = data.get("graph") or {}
79
- nodes, edges = graph.get("nodes") or [], graph.get("edges") or []
80
- check("graph.non_empty", bool(nodes), "没有任何节点")
81
-
82
- ids = [n.get("id") for n in nodes] + [e.get("id") for e in edges]
83
- check("graph.ids_present", all(ids), "存在缺 id 的元素")
84
- check("graph.ids_unique", len(ids) == len(set(ids)),
85
- "id 重复会让标注对不回来")
86
-
87
- node_ids = {n.get("id") for n in nodes}
88
- dangling = [e.get("id") for e in edges
89
- if e.get("source") not in node_ids or e.get("target") not in node_ids]
90
- check("graph.no_dangling_edge", not dangling, f"悬挂边 {dangling[:3]}")
91
-
92
- no_points = [e.get("id") for e in edges if len(e.get("points") or []) < 2]
93
- check("graph.edges_have_geometry", not no_points,
94
- f"{len(no_points)} 条边没有布局器给的路径点")
95
-
96
- if source and source.exists():
97
- try:
98
- src = json.loads(source.read_text())
99
- except json.JSONDecodeError:
100
- return
101
- sn, se = len(src.get("children") or []), len(src.get("edges") or [])
102
- check("graph.node_count_matches_source", sn == len(nodes), f"源 {sn} / 产物 {len(nodes)}")
103
- check("graph.edge_count_matches_source", se == len(edges), f"源 {se} / 产物 {len(edges)}")
104
-
105
-
106
- def check_theme(html: str) -> None:
107
- """三态都要有:显式 light、显式 dark、以及什么都没标的系统态。"""
108
- check("theme.root_tokens", ":root {" in html or ":root{" in html)
109
- check("theme.system_dark",
110
- bool(re.search(r"prefers-color-scheme:\s*dark", html)),
111
- "缺了未标记的系统态,最常见的漏配")
112
- check("theme.explicit_dark", '[data-theme="dark"]' in html)
113
- check("theme.body_background",
114
- bool(re.search(r"body\s*\{[^}]*background", html, re.S)),
115
- "body 不上底色会借用宿主的底,深浅主题下会串")
116
-
117
-
118
- def main() -> int:
119
- ap = argparse.ArgumentParser()
120
- ap.add_argument("artifact", type=Path)
121
- ap.add_argument("--source", type=Path, default=None,
122
- help="源数据,给了就比对计数")
123
- ap.add_argument("--json", type=Path, default=None,
124
- help="把结果写成 JSON,供 done_criteria 引用")
125
- args = ap.parse_args()
126
-
127
- if not args.artifact.exists():
128
- print(f"找不到 {args.artifact}")
129
- return 2
130
-
131
- html = args.artifact.read_text(encoding="utf-8")
132
- size_kb = args.artifact.stat().st_size / 1024
133
-
134
- check_offline(html)
135
- data = check_provenance(html)
136
- if data:
137
- check_graph(data, args.source)
138
- check_theme(html)
139
-
140
- print(f"检查 {args.artifact}({size_kb:.0f} KB)\n")
141
- for r in RESULTS:
142
- mark = "PASS" if r["ok"] else "FAIL"
143
- line = f" {mark} {r['check']}"
144
- if not r["ok"] and r["detail"]:
145
- line += f" — {r['detail']}"
146
- print(line)
147
-
148
- total = PASSED + len(FAILED)
149
- print(f"\n{PASSED}/{total} 通过" + (f",失败:{', '.join(FAILED)}" if FAILED else ""))
150
-
151
- if args.json:
152
- args.json.write_text(json.dumps({
153
- "artifact": str(args.artifact),
154
- "size_kb": round(size_kb, 1),
155
- "passed": PASSED,
156
- "total": total,
157
- "ok": not FAILED,
158
- "failed": FAILED,
159
- "checks": RESULTS,
160
- }, ensure_ascii=False, indent=2), encoding="utf-8")
161
- print(f"结果已写入 {args.json}")
162
-
163
- return 1 if FAILED else 0
164
-
165
-
166
- if __name__ == "__main__":
167
- sys.exit(main())
@@ -1,272 +0,0 @@
1
- #!/usr/bin/env node
2
- /* 拓扑检查件生成器 —— ELK 算几何,原生 SVG 渲染,产出零外部请求的单文件 HTML。
3
- *
4
- * node render_topology.js --elk-input in.json --out topology.html --title "流程拓扑检查"
5
- * node render_topology.js --elk-output laid-out.json --out topology.html
6
- *
7
- * 数据无关:节点与边上除 ELK 必需字段之外的属性原样透传给渲染器,有就在侧栏展示。
8
- * 本脚本不要求输入数据带任何自定义字段。
9
- */
10
- 'use strict';
11
-
12
- const fs = require('fs');
13
- const path = require('path');
14
- const crypto = require('crypto');
15
-
16
- const VERSION = '1.0.0';
17
- const HERE = __dirname;
18
- const ASSETS = path.join(HERE, '..', 'assets');
19
-
20
- function parseArgs(argv) {
21
- const out = {};
22
- for (let i = 2; i < argv.length; i++) {
23
- const a = argv[i];
24
- if (!a.startsWith('--')) continue;
25
- const key = a.slice(2);
26
- const next = argv[i + 1];
27
- if (next && !next.startsWith('--')) { out[key] = next; i++; } else { out[key] = true; }
28
- }
29
- return out;
30
- }
31
-
32
- function usage() {
33
- console.log([
34
- '用法:',
35
- ' --elk-input <file> ELK 输入 JSON;给了就先在本地跑 elkjs',
36
- ' --elk-output <file> ELK 输出 JSON;作为输入时直接渲染,作为输出时留盘备查',
37
- ' --out <file> 产出的 HTML(必填)',
38
- ' --title <text> 页面标题',
39
- ' --source <path> 源数据路径,用于页脚自陈来历并算摘要',
40
- ' --proves <text> 这份东西证明了什么',
41
- ' --not-proves <text> 它不证明什么',
42
- ' --annotations <file> 预置已有标注,形状同导出的 annotations.json',
43
- '',
44
- 'ELK 输入与输出都留盘,是为了让布局问题与渲染问题能分开定位。'
45
- ].join('\n'));
46
- }
47
-
48
- /* ── ELK:只在本地 vendor 里找,不下载 ── */
49
- async function runElk(input) {
50
- const bundle = path.join(ASSETS, 'vendor', 'elkjs', 'elk.bundled.js');
51
- if (!fs.existsSync(bundle)) {
52
- throw new Error('缺少 ' + bundle + '(见同目录 UPSTREAM.md 的获取步骤)');
53
- }
54
- const ELK = require(bundle);
55
- return new ELK().layout(input);
56
- }
57
-
58
- /* ── ELK 输出 → 渲染器数据 ── */
59
- const NODE_RESERVED = new Set(['id', 'x', 'y', 'width', 'height', 'labels', 'properties', 'layoutOptions', 'children', 'ports', 'edges']);
60
- const EDGE_RESERVED = new Set(['id', 'sources', 'targets', 'sections', 'labels', 'properties', 'source', 'target', 'container', 'junctionPoints']);
61
- const ROLES = new Set(['main', 'branch', 'seed', 'recycle', 'utility', 'unknown']);
62
-
63
- function labelOf(o, i) {
64
- if (!o) return undefined;
65
- if (i === 0 && typeof o.label === 'string') return o.label;
66
- if (i === 1 && typeof o.sub === 'string') return o.sub;
67
- const L = o.labels || [];
68
- return L[i] && typeof L[i].text === 'string' ? L[i].text : undefined;
69
- }
70
-
71
- function passthrough(obj, reserved) {
72
- const extra = {};
73
- Object.keys(obj || {}).forEach(k => {
74
- if (reserved.has(k) || k === 'label' || k === 'sub') return;
75
- if (k.charAt(0) === '$') return; // elkjs 转译产物的内部字段
76
- const v = obj[k];
77
- if (v == null || typeof v === 'function') return;
78
- extra[k] = v;
79
- });
80
- Object.keys((obj && obj.properties) || {}).forEach(k => {
81
- const v = obj.properties[k];
82
- if (v == null) return;
83
- if (typeof k === 'string' && k.indexOf('elk.') === 0) return; // ELK 自己的布局属性不外泄
84
- extra[k] = v;
85
- });
86
- return extra;
87
- }
88
-
89
- function edgePoints(edge) {
90
- const s = (edge.sections || [])[0];
91
- if (!s) return [];
92
- const pts = [];
93
- if (s.startPoint) pts.push([s.startPoint.x, s.startPoint.y]);
94
- (s.bendPoints || []).forEach(p => pts.push([p.x, p.y]));
95
- if (s.endPoint) pts.push([s.endPoint.x, s.endPoint.y]);
96
- return pts;
97
- }
98
-
99
- function toGraph(layout) {
100
- const nodes = (layout.children || []).map(c => {
101
- const n = {
102
- id: c.id,
103
- x: c.x || 0, y: c.y || 0,
104
- width: c.width || 120, height: c.height || 40,
105
- label: labelOf(c, 0) || c.id
106
- };
107
- const sub = labelOf(c, 1);
108
- if (sub) n.sub = sub;
109
- Object.assign(n, passthrough(c, NODE_RESERVED));
110
- return n;
111
- });
112
-
113
- const edges = (layout.edges || []).map(e => {
114
- const props = e.properties || {};
115
- // 显式声明的 role 才采用。缺失时不伪造 unknown——「作者说不确定」与
116
- // 「这张图不用 role 语义」是两回事,混为一谈会把极简图全报成存疑。
117
- const role = ROLES.has(props.role) ? props.role : (ROLES.has(e.role) ? e.role : null);
118
- const o = {
119
- id: e.id,
120
- source: (e.sources || [e.source])[0],
121
- target: (e.targets || [e.target])[0],
122
- points: edgePoints(e)
123
- };
124
- if (role) o.role = role;
125
- const lab = (e.labels || [])[0];
126
- const text = labelOf(e, 0);
127
- if (text) {
128
- o.label = text;
129
- if (lab && lab.x != null) {
130
- o.labelX = lab.x + (lab.width || 0) / 2;
131
- o.labelY = lab.y + (lab.height || 0) / 2 + 3;
132
- }
133
- }
134
- Object.assign(o, passthrough(e, EDGE_RESERVED));
135
- if (role) o.role = role; else delete o.role;
136
- return o;
137
- });
138
-
139
- return { nodes, edges };
140
- }
141
-
142
- /* 入口区:只用契约内已有的语义(role=unknown),不引入新字段。
143
- 数据里自带 entry 时以数据为准。 */
144
- function buildEntry(src, graph) {
145
- if (Array.isArray(src.entry)) return src.entry;
146
- const unknown = graph.edges.filter(e => e.role === 'unknown').map(e => e.id); // 只算显式声明的
147
- const items = [];
148
- if (unknown.length) items.push({ label: '关系存疑', count: unknown.length, ids: unknown });
149
- const isolated = graph.nodes
150
- .filter(n => !graph.edges.some(e => e.source === n.id || e.target === n.id))
151
- .map(n => n.id);
152
- if (isolated.length) items.push({ label: '孤立节点', count: isolated.length, ids: isolated });
153
- return items;
154
- }
155
-
156
- function esc(s) {
157
- return String(s == null ? '' : s)
158
- .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
159
- .replace(/"/g, '&quot;');
160
- }
161
-
162
- function html(payload, title) {
163
- const css = fs.readFileSync(path.join(ASSETS, 'renderer', 'renderer.css'), 'utf8');
164
- const js = fs.readFileSync(path.join(ASSETS, 'renderer', 'renderer.js'), 'utf8');
165
- const p = payload.provenance || {};
166
- const data = JSON.stringify(payload).replace(/</g, '\\u003c');
167
-
168
- const foot = [
169
- p.source ? '<b>源数据</b> ' + esc(p.source) + (p.digest ? ' · ' + esc(p.digest) : '') : '',
170
- p.generatedAt ? '<b>生成于</b> ' + esc(p.generatedAt) : '',
171
- '<b>生成器</b> render_topology ' + esc(p.generator || VERSION)
172
- ].filter(Boolean).join(' &nbsp;·&nbsp; ');
173
-
174
- const claims = [
175
- p.proves ? '<b>证明</b>:' + esc(p.proves) : '',
176
- p.doesNotProve ? '<b>不证明</b>:' + esc(p.doesNotProve) : ''
177
- ].filter(Boolean).join(' ');
178
-
179
- return `<!doctype html>
180
- <html lang="zh-CN">
181
- <head>
182
- <meta charset="utf-8">
183
- <meta name="viewport" content="width=device-width,initial-scale=1">
184
- <title>${esc(title)}</title>
185
- <style>
186
- ${css}
187
- </style>
188
- </head>
189
- <body>
190
- <header>
191
- <h1>${esc(title)}</h1>
192
- <span class="counts" id="counts"></span>
193
- <span class="spacer"></span>
194
- <button class="chip" id="fit">适应窗口</button>
195
- <button class="chip" id="export" disabled>导出反馈</button>
196
- <button class="chip" id="theme" aria-pressed="false">深色</button>
197
- </header>
198
- <div id="entry"></div>
199
- <main>
200
- <div id="stage">
201
- <svg id="canvas" aria-label="${esc(title)}"></svg>
202
- <div id="minimap" aria-hidden="true"></div>
203
- </div>
204
- <aside id="side" class="empty"></aside>
205
- </main>
206
- <div class="legend" id="legend"></div>
207
- <footer>
208
- ${foot}
209
- ${claims ? '<div class="claims">' + claims + '</div>' : ''}
210
- </footer>
211
- <script id="artifact-data" type="application/json">${data}</script>
212
- <script>
213
- window.__ARTIFACT__ = JSON.parse(document.getElementById('artifact-data').textContent);
214
- ${js}
215
- </script>
216
- </body>
217
- </html>
218
- `;
219
- }
220
-
221
- async function main() {
222
- const args = parseArgs(process.argv);
223
- if (args.help || (!args['elk-input'] && !args['elk-output']) || !args.out) { usage(); return 1; }
224
-
225
- let layout;
226
- if (args['elk-input']) {
227
- const input = JSON.parse(fs.readFileSync(args['elk-input'], 'utf8'));
228
- layout = await runElk(input);
229
- if (args['elk-output'] && typeof args['elk-output'] === 'string') {
230
- fs.writeFileSync(args['elk-output'], JSON.stringify(layout, null, 2));
231
- }
232
- } else {
233
- layout = JSON.parse(fs.readFileSync(args['elk-output'], 'utf8'));
234
- }
235
-
236
- const srcRaw = args['elk-input'] ? JSON.parse(fs.readFileSync(args['elk-input'], 'utf8')) : layout;
237
- const graph = toGraph(layout);
238
-
239
- const provenance = { generatedAt: new Date().toISOString(), generator: VERSION };
240
- if (typeof args.source === 'string') {
241
- provenance.source = args.source;
242
- if (fs.existsSync(args.source)) {
243
- provenance.digest = 'sha256:' + crypto.createHash('sha256')
244
- .update(fs.readFileSync(args.source)).digest('hex').slice(0, 12);
245
- }
246
- }
247
- if (typeof args.proves === 'string') provenance.proves = args.proves;
248
- if (typeof args['not-proves'] === 'string') provenance.doesNotProve = args['not-proves'];
249
-
250
- let annotations = [];
251
- if (typeof args.annotations === 'string' && fs.existsSync(args.annotations)) {
252
- const a = JSON.parse(fs.readFileSync(args.annotations, 'utf8'));
253
- annotations = Array.isArray(a) ? a : (a.annotations || []);
254
- }
255
-
256
- const title = typeof args.title === 'string' ? args.title : '拓扑检查';
257
- const payload = { title, provenance, graph, entry: buildEntry(srcRaw, graph), annotations };
258
-
259
- fs.writeFileSync(args.out, html(payload, title));
260
- const noRole = graph.edges.filter(e => !e.role).length;
261
- if (noRole) {
262
- console.log('提示:' + noRole + '/' + graph.edges.length +
263
- ' 条边没有声明 properties.role,已按中性样式渲染。' +
264
- '工业流程拓扑里这通常是缺陷,见 references/when-drawing-a-topology.md。');
265
- }
266
- console.log('已生成 ' + args.out +
267
- '(' + graph.nodes.length + ' 节点 / ' + graph.edges.length + ' 连线 / ' +
268
- (fs.statSync(args.out).size / 1024).toFixed(0) + ' KB)');
269
- return 0;
270
- }
271
-
272
- main().then(c => process.exit(c || 0)).catch(e => { console.error(String(e && e.message || e)); process.exit(1); });
@@ -1,5 +0,0 @@
1
- What-If Oracle skill adapted from:
2
- https://github.com/ashrafkahoush-ux/claude-consciousness-skills
3
-
4
- Licensed under CC BY-NC-SA 4.0
5
- https://creativecommons.org/licenses/by-nc-sa/4.0/