@haaaiawd/loom 1.2.1 → 1.3.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.
- package/CHANGELOG.md +37 -0
- package/README.md +24 -22
- package/cli/bin/loom.js +83 -76
- package/cli/help/atlas.md +48 -0
- package/cli/help/capability.md +48 -3
- package/cli/help/concepts.md +2 -0
- package/cli/help/doctor.md +8 -1
- package/cli/help/expertise.md +7 -6
- package/cli/help/workflow.md +7 -2
- package/cli/src/activate.js +126 -6
- package/cli/src/atlas.js +282 -0
- package/cli/src/capability-graph.js +300 -6
- package/cli/src/diagnostics.js +108 -15
- package/cli/src/guide.js +82 -22
- package/package.json +1 -1
- package/roles/architect.md +23 -17
- package/roles/forge.md +1 -3
- package/roles/impact-reviewer.md +37 -0
- package/templates/ATLAS_TEMPLATE.html +104 -0
- package/templates/CAPABILITY_BRIEF_TEMPLATE.md +1 -0
- package/templates/CAPABILITY_GRAPH_EXAMPLE.json +188 -0
- package/templates/CAPABILITY_GRAPH_TEMPLATE.json +69 -2
- package/cli/help/preview.md +0 -60
- package/cli/src/preview-prompt.md +0 -337
- package/cli/src/preview.js +0 -73
package/cli/src/guide.js
CHANGED
|
@@ -11,6 +11,7 @@ import { listCapabilityProposals } from './capability-proposals.js';
|
|
|
11
11
|
import { isAutoOn, getAutoMode, writeHeartbeat, needsHumanReview } from './auto.js';
|
|
12
12
|
import { doctor } from './diagnostics.js';
|
|
13
13
|
import { getExpertisePackState } from './expertise-pack.js';
|
|
14
|
+
import { validateAtlas } from './atlas.js';
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* 检测文件是否还是模板(未填充真实内容)。
|
|
@@ -63,35 +64,41 @@ export function guideProject(projectDir, options = {}) {
|
|
|
63
64
|
outputs: ['.loom/v1/'],
|
|
64
65
|
verify_command: 'loom guide',
|
|
65
66
|
},
|
|
66
|
-
need_philosophy: {
|
|
67
|
+
need_philosophy: {
|
|
67
68
|
inputs: ['meta/PHILOSOPHY_WEAVER.md', 'meta/BASELINE.md', 'dimensions/SEARCH_METHODOLOGY.md'],
|
|
68
|
-
outputs: [`.loom/${current}/00_PHILOSOPHY/PRODUCT_PHILOSOPHY.md`, `.loom/${current}/00_PHILOSOPHY/ENGINEERING_CREED.md`, `.loom/${current}/00_PHILOSOPHY/DECISION_RUBRIC.md`],
|
|
69
|
-
verify_command: 'loom philosophy check',
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
69
|
+
outputs: [`.loom/${current}/00_PHILOSOPHY/PRODUCT_PHILOSOPHY.md`, `.loom/${current}/00_PHILOSOPHY/ENGINEERING_CREED.md`, `.loom/${current}/00_PHILOSOPHY/DECISION_RUBRIC.md`],
|
|
70
|
+
verify_command: 'loom philosophy check',
|
|
71
|
+
input_delivery: 'context_pack',
|
|
72
|
+
},
|
|
73
|
+
need_vision: {
|
|
74
|
+
inputs: ['roles/visionary.md', `.loom/${current}/00_PHILOSOPHY/`],
|
|
75
|
+
outputs: [`.loom/${current}/01_VISION.md`],
|
|
76
|
+
verify_command: 'loom guide',
|
|
77
|
+
input_delivery: 'context_pack',
|
|
78
|
+
},
|
|
76
79
|
need_architecture: {
|
|
77
80
|
inputs: ['roles/architect.md', `.loom/${current}/01_VISION.md`],
|
|
78
81
|
outputs: [`.loom/${current}/02_ARCHITECTURE.md`, `.loom/${current}/04_INTENT_MAP.json`],
|
|
79
82
|
verify_command: 'loom doctor',
|
|
83
|
+
input_delivery: 'context_pack',
|
|
80
84
|
},
|
|
81
85
|
need_capability_graph: {
|
|
82
86
|
inputs: ['roles/architect.md', `.loom/${current}/01_VISION.md`],
|
|
83
87
|
outputs: [`.loom/${current}/07_CAPABILITY_GRAPH.json`, `.loom/${current}/07_CAPABILITY_BRIEFS/`],
|
|
84
88
|
verify_command: 'loom capability coverage',
|
|
89
|
+
input_delivery: 'context_pack',
|
|
85
90
|
},
|
|
86
91
|
capability_graph_incomplete: {
|
|
87
92
|
inputs: ['roles/architect.md', `.loom/${current}/07_CAPABILITY_GRAPH.json`, `.loom/${current}/04_INTENT_MAP.json`],
|
|
88
93
|
outputs: [`.loom/${current}/07_CAPABILITY_GRAPH.json`, `.loom/${current}/07_CAPABILITY_BRIEFS/`],
|
|
89
94
|
verify_command: 'loom capability coverage',
|
|
95
|
+
input_delivery: 'context_pack',
|
|
90
96
|
},
|
|
91
97
|
capability_graph_proposals_pending: {
|
|
92
98
|
inputs: ['roles/architect.md', `.loom/${current}/07_GRAPH_PROPOSALS/`, `.loom/${current}/07_CAPABILITY_GRAPH.json`],
|
|
93
99
|
outputs: [`.loom/${current}/07_GRAPH_PROPOSALS/`, `.loom/${current}/07_CAPABILITY_GRAPH.json`, `.loom/${current}/04_INTENT_MAP.json`],
|
|
94
100
|
verify_command: 'loom capability proposal list',
|
|
101
|
+
input_delivery: 'context_pack',
|
|
95
102
|
},
|
|
96
103
|
intent_map_broken: {
|
|
97
104
|
inputs: [`.loom/${current}/04_INTENT_MAP.json`],
|
|
@@ -120,9 +127,10 @@ export function guideProject(projectDir, options = {}) {
|
|
|
120
127
|
},
|
|
121
128
|
};
|
|
122
129
|
const meta = stageMeta[result.stage] || {};
|
|
123
|
-
result.inputs = meta.inputs || [];
|
|
124
|
-
result.outputs = meta.outputs || [];
|
|
125
|
-
result.verify_command = meta.verify_command || null;
|
|
130
|
+
result.inputs = meta.inputs || [];
|
|
131
|
+
result.outputs = meta.outputs || [];
|
|
132
|
+
result.verify_command = meta.verify_command || null;
|
|
133
|
+
result.input_delivery = meta.input_delivery || null;
|
|
126
134
|
// 统一后处理:写心跳 + 加 AUTO 提示词 + 判断是否需要人类 review
|
|
127
135
|
if (existsSync(loomRoot) && !options.dryRun) {
|
|
128
136
|
try {
|
|
@@ -242,14 +250,14 @@ function diagnoseStage(cwd, loomRoot, auto) {
|
|
|
242
250
|
details: { version: current, proposals: pendingProposals.map((proposal) => proposal.id) },
|
|
243
251
|
auto,
|
|
244
252
|
next_action: '审计新信息对 Capability Graph、Intent 和契约的影响',
|
|
245
|
-
next_command: 'loom
|
|
253
|
+
next_command: 'loom activate architect',
|
|
246
254
|
message: `当前版本 ${current} 有 ${pendingProposals.length} 个未闭合的 Capability Graph proposal。它们是新要求、研究或实现发现,不得由 Forge 静默变成当前 Intent 范围。`,
|
|
247
255
|
};
|
|
248
256
|
}
|
|
249
257
|
} catch (error) {
|
|
250
258
|
return {
|
|
251
259
|
stage: 'capability_graph_proposals_pending', stage_num: 3, details: { version: current, error: error.message }, auto,
|
|
252
|
-
next_action: '修复 Capability Graph proposal', next_command: 'loom
|
|
260
|
+
next_action: '修复 Capability Graph proposal', next_command: 'loom activate architect',
|
|
253
261
|
message: `Capability Graph proposal 无法审计: ${error.message}`,
|
|
254
262
|
};
|
|
255
263
|
}
|
|
@@ -261,20 +269,25 @@ function diagnoseStage(cwd, loomRoot, auto) {
|
|
|
261
269
|
stage_num: 3.1,
|
|
262
270
|
details: { version: current, coverage: coverage.summary },
|
|
263
271
|
auto,
|
|
264
|
-
next_action: '补齐 Capability Graph
|
|
265
|
-
next_command: 'loom
|
|
266
|
-
message: `当前版本 ${current}:Capability Graph
|
|
272
|
+
next_action: '补齐 Capability Graph 的透镜审视、路由、可观察验证入口与 Intent 回链',
|
|
273
|
+
next_command: 'loom activate architect',
|
|
274
|
+
message: `当前版本 ${current}:Capability Graph 尚未闭合(透镜契约缺口 ${coverage.summary.lens_contract_gaps || 0}、能力领域缺口 ${coverage.summary.capability_domain_gaps || 0}、高影响前沿 ${coverage.summary.high_unrouted}、路由缺口 ${coverage.summary.routing_gaps}、不可观察 outcome ${coverage.summary.high_outcomes_without_observable_evidence}、未映射 Intent ${coverage.summary.unmapped_intents})。先由 Architect 补图谱,再继续设计 Intent。`,
|
|
267
275
|
};
|
|
268
276
|
}
|
|
269
277
|
} catch (error) {
|
|
278
|
+
const impactGateError = /impact_assessment|impact 必须为 high|外部获取门禁/.test(error.message);
|
|
270
279
|
return {
|
|
271
280
|
stage: 'capability_graph_incomplete',
|
|
272
281
|
stage_num: 3.1,
|
|
273
282
|
details: { version: current, error: error.message },
|
|
274
283
|
auto,
|
|
275
|
-
next_action:
|
|
276
|
-
|
|
277
|
-
|
|
284
|
+
next_action: impactGateError
|
|
285
|
+
? '先完成每个 capability 的 Impact Gate,再决定哪些必须外部获取'
|
|
286
|
+
: '修复 Capability Graph',
|
|
287
|
+
next_command: 'loom activate architect',
|
|
288
|
+
message: impactGateError
|
|
289
|
+
? `Capability Graph 的 Impact Gate 尚未通过:${error.message} 先由 Architect 判断影响的用户结果、错判代价与外部知识是否会改变决定,不能靠降级 impact 继续。`
|
|
290
|
+
: `Capability Graph 无法通过校验: ${error.message}`,
|
|
278
291
|
};
|
|
279
292
|
}
|
|
280
293
|
}
|
|
@@ -310,8 +323,43 @@ function diagnoseStage(cwd, loomRoot, auto) {
|
|
|
310
323
|
};
|
|
311
324
|
}
|
|
312
325
|
|
|
313
|
-
const allIntents = Object.values(intents);
|
|
314
|
-
const
|
|
326
|
+
const allIntents = Object.values(intents);
|
|
327
|
+
const incompleteDesignDocuments = ['02_ARCHITECTURE.md', '05_VERIFICATION.md']
|
|
328
|
+
.filter((filename) => isTemplate(join(versionDir, filename)));
|
|
329
|
+
if (incompleteDesignDocuments.length) {
|
|
330
|
+
return {
|
|
331
|
+
stage: 'need_architecture',
|
|
332
|
+
stage_num: 3.5,
|
|
333
|
+
details: { version: current, incomplete_documents: incompleteDesignDocuments },
|
|
334
|
+
auto,
|
|
335
|
+
next_action: '完成当前版本的系统架构与验证契约文档',
|
|
336
|
+
next_command: 'loom activate architect',
|
|
337
|
+
message: `当前版本 ${current} 的 Intent Map 已存在,但 ${incompleteDesignDocuments.join('、')} 仍缺失或是模板。Architect 必须先把系统边界与完成契约落到真实文档,再进入 Intent Loop。`,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
const documentIntegrityIssues = doctor(versionDir, join(versionDir, 'verifications'), philosophyDir).issues
|
|
341
|
+
.filter((issue) => [
|
|
342
|
+
'project_document_missing',
|
|
343
|
+
'project_document_template',
|
|
344
|
+
'orphan_philosophy_ref',
|
|
345
|
+
'orphan_philosophy_anchor',
|
|
346
|
+
'intent_narrative_invalid',
|
|
347
|
+
'intent_contract_invalid',
|
|
348
|
+
'capability_impact_gate_missing',
|
|
349
|
+
].includes(issue.type));
|
|
350
|
+
if (documentIntegrityIssues.length) {
|
|
351
|
+
const issueTypes = [...new Set(documentIntegrityIssues.map((issue) => issue.type))];
|
|
352
|
+
return {
|
|
353
|
+
stage: 'document_integrity_incomplete',
|
|
354
|
+
stage_num: 3.6,
|
|
355
|
+
details: { version: current, issue_types: issueTypes },
|
|
356
|
+
auto,
|
|
357
|
+
next_action: '修复 LOOM 文档引用与结构完整性,再进入 Intent Loop',
|
|
358
|
+
next_command: 'loom activate architect',
|
|
359
|
+
message: `当前版本 ${current} 的 LOOM 文档完整性尚未通过(${issueTypes.join('、')})。先让 Architect 修复真实文档、章节引用或 Impact Gate;不要带着断开的叙事/契约进入 Intent Loop。`,
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
const counts = {
|
|
315
363
|
pending: allIntents.filter((i) => i.status === 'pending').length,
|
|
316
364
|
in_progress: allIntents.filter((i) => i.status === 'in_progress').length,
|
|
317
365
|
completed: allIntents.filter((i) => i.status === 'completed').length,
|
|
@@ -339,6 +387,18 @@ function diagnoseStage(cwd, loomRoot, auto) {
|
|
|
339
387
|
|
|
340
388
|
// 状态 6: 全部 completed
|
|
341
389
|
if (counts.completed === total && total > 0) {
|
|
390
|
+
const atlas = validateAtlas(join(versionDir, '..', '..'), versionDir);
|
|
391
|
+
if (!atlas.valid) {
|
|
392
|
+
return {
|
|
393
|
+
stage: 'need_decision_atlas',
|
|
394
|
+
stage_num: 5.8,
|
|
395
|
+
details: { version: current, atlas_errors: atlas.errors },
|
|
396
|
+
auto,
|
|
397
|
+
next_action: '生成并校验当前版本的决策图谱交付物',
|
|
398
|
+
next_command: 'loom atlas --regen',
|
|
399
|
+
message: `当前版本 ${current} 的 Intent 已全部完成,但决策图谱尚未交付。它只说明当前架构与决策结构,不展示项目进度;生成 loom-atlas.html 后运行 loom atlas validate。`,
|
|
400
|
+
};
|
|
401
|
+
}
|
|
342
402
|
const health = doctor(versionDir, join(versionDir, 'verifications'), philosophyDir);
|
|
343
403
|
const blocking = health.issues.filter((issue) => issue.severity === 'fatal' || issue.severity === 'high');
|
|
344
404
|
if (blocking.length) {
|
package/package.json
CHANGED
package/roles/architect.md
CHANGED
|
@@ -27,27 +27,33 @@
|
|
|
27
27
|
## Operating Principles
|
|
28
28
|
|
|
29
29
|
1. 一个 Intent 产生可观察的完整结果,能独立验证,并可在一次受控工作周期内完成。
|
|
30
|
-
2. 在创建正式 Intent Map
|
|
31
|
-
3.
|
|
32
|
-
4.
|
|
33
|
-
5.
|
|
34
|
-
6.
|
|
35
|
-
7. `
|
|
36
|
-
8.
|
|
37
|
-
9.
|
|
38
|
-
10.
|
|
30
|
+
2. 在创建正式 Intent Map 前,先完成 Graph 的 `lens_contract`。它不是把 UI、UX、后端、AI 写成一排部门标签,而是强制审视六个方向:用户旅程、交互与可访问性、视觉与信息表达、内容与沟通、系统与数据、横切质量与风险。每项都必须连接具体节点,或以项目事实说明为何不适用;不能静默略过。
|
|
31
|
+
3. 再从项目事实派生 `capability_domains`:这是会改变方案或验证方法的专业领域,例如 UI/UX、3D 建模、光影与材质、网络安全、行为心理学、生物学、供应链或法律。不要维护一个万能学科目录,也不要因为名称听起来高级就加入领域。每个领域必须说明“它现在要回答的专业问题”和“为什么它会改变本项目的决定”,并连接到至少一个具体 capability。
|
|
32
|
+
4. 透镜是检查方向,能力领域是专业知识来源,Capability 才是可交付的能力边界。不要创建 `CAP-UI`、`CAP-UX` 或“做好视觉”这类空节点;改写成用户可观察、可取舍、可验证的能力,例如“让错误状态、恢复入口与键盘路径保持可理解”“让三维光影传达空间尺度”“让长报告的证据层级在窄屏上仍可扫读”。一个具体 capability 可以同时服务多个 outcome,也可以回链多个专业领域、被多个透镜引用。
|
|
33
|
+
5. Graph 必须出现真实关系,不要把它排成 `一个 Outcome → 一个 Capability → 一个 Intent` 的整齐队列。至少检查:一个 concern 是否约束多个能力、一个 capability 是否支撑多个结果、风险/证据是否横切不同 Intent;若不存在,要写出基于项目事实的理由。
|
|
34
|
+
6. Graph 不是任务列表:未知与调研留在 Graph;只有边界清楚、可独立验证的结果才进入 Intent Map。
|
|
35
|
+
7. 在写 Intent 之前,对每个具体 capability 先完成 **Impact Gate**。写出 `impact_assessment`:它影响的用户结果、错判/省略的代价(`low` / `material` / `hard_to_reverse`)、外部知识会不会改变决定,以及理由。若代价不可逆,或外部知识会改变设计/验证判断,`impact` 必须为 `high`;不得把它标成 medium/low 来省掉调研。
|
|
36
|
+
8. Architect 不能独自确认这项判断。创建一个新的 Agent thread / 子代理,运行 `loom activate impact-reviewer`,让它逐项给出独立的 `impact_review`。它可以上调任何被低估的节点;Graph schema 1.3 还要求 high capability 至少占全部具体 capability 的 30%(向上取整,至少一个)。这是防止用大量“普通节点”稀释关键问题的底线,不是用标签凑数。
|
|
37
|
+
9. 将每个 high capability 视为一个**主动探索候选**,而不只是“等 Forge 想起来再搜索”的标签。其 Brief 要说清:哪一个专业判断仍未知、哪些项目事实决定检索方向、什么发现会改变设计/验收、以及没有可靠资料时应如何降级或回流。若中低影响节点的外部知识仍可能改变方案,宁可上调为 high;不为满足比例制造无关节点,也不把网址、Skill 名称或关键词写进 Graph。
|
|
38
|
+
10. Graph schema 1.3 中,高影响 capability 必须进入外部获取强门:`acquisition_mode` 只能为 `external_required`(或省略并使用默认值),并创建短小的 Capability Brief。`adaptive` 与 `project_only` 只属于经 Impact Gate 明确不需要外部来源的较低影响能力。Graph 不保存网站、Skill 或关键词,不为低价值叶子制造文档。
|
|
39
|
+
11. 每个 Intent 必须回链至少一个 Graph 节点;每个高影响 Graph 节点必须有明确路由。
|
|
40
|
+
12. 只引入当前目标确实需要的边界和抽象;不为想象中的扩展性提前付费。
|
|
41
|
+
13. `acceptance` 是完成契约:包含功能承诺、关键失败边界和防御承诺。
|
|
42
|
+
14. 若 Intent 会写入、重组、迁移、同步或覆盖既有用户/系统状态,设 `continuity_required: true`;在同一份 acceptance 写明哪些旧价值不得消失,以及一条“旧状态 → 操作 → 新状态”的验收序列。删除、替换和清空必须显式授权,不能由“更新”一词暗示。
|
|
43
|
+
15. `quality_contract` 是可选质量契约:只在结果需要高于功能正确性时声明。
|
|
44
|
+
16. 声明相对提升时,质量契约写清修改前基线、可感知或可测量的质量主张、
|
|
39
45
|
最小有意义差异与证据方式。
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
46
|
+
17. `capability_needs` 是兼容摘要;新项目优先通过 Capability Graph 和 Brief 声明下游需要补齐的专业领域,不假装能力已经加载。
|
|
47
|
+
18. `creative_scope` 说明 Forge 可以大胆改变什么、必须保持什么。
|
|
48
|
+
19. 每个高影响 `outcome` 必须用 `validated_by` 连接到一个 `evidence` 节点。该节点必须写出 `verification.method`、`target`、`procedure`、`pass_criteria` 和 `artifact`,并回链负责把证据真正产出的 Intent。设计阶段的 `artifact` 是计划中的本地证据路径;负责它的 Intent 一旦 completed,该文件必须实际存在于当前版本 `verifications/` 或 `08_ASSET_LIBRARY/files/`。`target` 是结果实际被接收、呈现或消费的位置:用户界面、目标宿主、外部系统、交付物或人工验收现场。不能用“接口返回成功”“URL 可访问”替代目标宿主中的可观察结果。
|
|
49
|
+
20. 对每个重要 Intent 做简短 Pre-Mortem:最可能出现什么“表面完成”,并将其转成
|
|
44
50
|
acceptance 或 verification_method。当 narrative 的核心现象可能被一个更容易实现、但
|
|
45
51
|
语义不同的代理替换时,写 `semantic_guard`:明确哪种替代不算完成,以及 Keeper 应
|
|
46
52
|
使用什么反例验证它。它不把 Intent 切成技术碎片,只防止“看似相近”偷换用户结果。
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
53
|
+
21. 新要求、论文/资料发现、Keeper 或 Forge 发现先进入 `07_GRAPH_PROPOSALS/`,带来源、观察证据和候选类型。Architect 明确判定它已被覆盖、需要改 Graph、生成/修订 Intent、改变 acceptance,还是 Minor/Major;不得把候选静默写入正式 Graph。
|
|
54
|
+
22. 当项目使用素材时,`08_ASSET_LIBRARY/manifest.json` 是唯一可用素材与来源/作者/许可/哈希的真相源。只允许已批准、可验证的本地资产进入交付;若资产构成结果证据,让 evidence 节点与资产记录双向回链。
|
|
55
|
+
23. `quality_strategy` 缺失等价于 `adaptive`。只有结果确实需要作者命题、媒介原型和独立候选比较时才设为 `atelier`,且必须同时声明 `quality_contract` 与 `creative_scope`。
|
|
56
|
+
24. Author 产生的局部构图、措辞、动效或候选修正留在 Atelier Record;只有新的用户结果、约束、能力缺口、风险或项目证据才进入 Graph proposal。Author 不得裁决自己的 proposal。
|
|
51
57
|
|
|
52
58
|
## Output Contract
|
|
53
59
|
|
package/roles/forge.md
CHANGED
|
@@ -46,9 +46,7 @@ medium grammar、surprise budget、anti-fixation 与 verification lens。人格
|
|
|
46
46
|
名号和风格形容词不能替代这些决策。
|
|
47
47
|
|
|
48
48
|
Context Pack 中出现 Skill 名称只代表可发现。若 External Acquisition Gate 为 OPEN,
|
|
49
|
-
|
|
50
|
-
find skill、网络、官方文档或研究资料。模型自行生成的常识、未打开的搜索摘要和只有标题的
|
|
51
|
-
结果不能成为来源。
|
|
49
|
+
把每个 `external_required` capability 当作一项主动探索,不要只为“完成检索”随手找一页泛泛资料。先从 Capability question、项目事实、媒介约束与已观察缺口派生 Search Plan,并实际使用 find skill、网络、官方文档或研究资料;随后追问“这条资料让哪一个设计、实现或验证决定不同了”。优先补齐会决定用户体验、风险边界或质量上限的节点;若首个来源过于通用、彼此冲突或无法落入具体决策,继续换通道或回流,而不是把模型常识包装成结论。模型自行生成的常识、未打开的搜索摘要和只有标题的结果不能成为来源。
|
|
52
50
|
|
|
53
51
|
required Pack 写入 `.loom/vN/10_EXPERTISE_PACKS/<intent-id>.json`,只保存来源定位和
|
|
54
52
|
项目化 Capability Capsules,不复制第三方内容。每个 Capsule 必须直接引用已打开的外部
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Impact Reviewer — 独立影响审查契约
|
|
2
|
+
|
|
3
|
+
## Mission
|
|
4
|
+
|
|
5
|
+
只判断 Capability Graph 中每一项具体 capability 是否被低估,以及外部知识是否会实质改变设计或验证。你不是 Architect 的润色器,也不替 Forge 实现。
|
|
6
|
+
|
|
7
|
+
## Independence
|
|
8
|
+
|
|
9
|
+
你必须在新的 Agent thread / 子代理中运行。不要继承 Architect 的结论为前提;先从 Vision、非目标、用户结果、失败后果和 Graph 关系自行判断。若无法获得独立上下文,不得写入 `impact_review.reviewer_mode: "independent_agent_thread"`,应交给 human 或另开线程。
|
|
10
|
+
|
|
11
|
+
## Procedure
|
|
12
|
+
|
|
13
|
+
1. 逐项审查所有 `kind: capability` 节点,而不是只挑 Architect 已标 high 的节点。
|
|
14
|
+
2. 对每项给出 `recommended_impact`、`external_acquisition_required` 和简短理由。问:错误会伤到什么用户结果?它能否在之后低成本补救?外部研究、规范、真实案例或专业方法会不会改变设计/验收?
|
|
15
|
+
3. 不确定时宁可上调为 high 并要求外部获取;不要以“通常做法”“看起来简单”降低等级。尤其检查那些名字像基础实现、实际上会决定用户尊严、可访问性、信任、长期可逆性或体验气质的节点。
|
|
16
|
+
4. 核对至少 30% 的 capability 被标为 high(向上取整,至少一个)。这是一条反稀释底线,不是把所有节点都标 high 的借口。
|
|
17
|
+
5. 将结论写入 Graph 根级 `impact_review`。只有 review 与节点 `impact`、`acquisition_mode` 一致,Graph 才能通过;若你上调了节点,交回 Architect 更新图谱和 Brief,再由你复审。不要替 Forge 伪造来源:你只判定“这件事值得主动探索”,实际搜索和来源化记录留给 Expertise Pack。
|
|
18
|
+
|
|
19
|
+
## Output Shape
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"impact_review": {
|
|
24
|
+
"reviewer_mode": "independent_agent_thread",
|
|
25
|
+
"assessments": [
|
|
26
|
+
{
|
|
27
|
+
"capability_id": "CAP-EXAMPLE",
|
|
28
|
+
"recommended_impact": "high",
|
|
29
|
+
"external_acquisition_required": true,
|
|
30
|
+
"rationale": "外部无障碍规范会改变交互和验收,误判会让用户失去完成路径。"
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
不要创建 Intent、Capability Brief、实现或验证记录;你的结论只是进入它们之前的独立门禁。
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<meta name="loom-atlas-source-digest" content="{{SOURCE_DIGEST}}">
|
|
7
|
+
<title>LOOM 决策图谱</title>
|
|
8
|
+
<style>
|
|
9
|
+
:root { --ink:#f2ecdf; --panel:#fbf7ef; --panel-2:#e9dfce; --paper:#24201c; --muted:#746b60; --line:rgba(48,38,28,.22); --acid:#a42a2a; --danger:#b95b3f; }
|
|
10
|
+
* { box-sizing:border-box; } html { background:var(--ink); } body { height:100dvh; min-height:100dvh; margin:0; overflow:hidden; background:var(--ink); color:var(--paper); font:500 16px/1.45 ui-sans-serif,system-ui,"Microsoft YaHei",sans-serif; }
|
|
11
|
+
button { font:inherit; } button:focus-visible { outline:2px solid var(--acid); outline-offset:3px; }
|
|
12
|
+
.atlas-shell { height:100dvh; min-height:100dvh; display:grid; grid-template-rows:auto minmax(0,1fr) auto; }
|
|
13
|
+
.atlas-topbar { position:relative; z-index:2; display:flex; justify-content:space-between; gap:1rem; padding:1.05rem clamp(1rem,3vw,3.5rem); border-top:5px solid var(--acid); border-bottom:1px solid var(--line); }
|
|
14
|
+
.atlas-title { margin:0; color:var(--paper); font-family:Georgia,"Songti SC",STSong,serif; font-size:1rem; font-weight:700; letter-spacing:.05em; } .atlas-title span { color:var(--acid); }
|
|
15
|
+
.atlas-views { display:flex; gap:.4rem; } .atlas-views button, .atlas-control, .atlas-chapter-nav button { appearance:none; border:0; background:transparent; color:var(--muted); cursor:pointer; }
|
|
16
|
+
.atlas-views button { padding:.35rem .55rem; border-bottom:1px solid transparent; } .atlas-views button[aria-pressed="true"] { color:var(--paper); border-color:var(--acid); }
|
|
17
|
+
.atlas-stage { position:relative; min-width:0; min-height:0; overflow:hidden; } .atlas-track { width:500%; height:100%; min-height:0; display:flex; transform:translate3d(0,0,0); transition:transform .68s cubic-bezier(.2,.75,.2,1); }
|
|
18
|
+
.atlas-slide { width:20%; min-height:calc(100dvh - 132px); display:grid; grid-template-columns:minmax(0,1.1fr) minmax(280px,.9fr); align-items:start; gap:clamp(2rem,6vw,7rem); padding:clamp(2.25rem,5vw,5.25rem) clamp(2rem,7vw,7rem); overflow:auto; }
|
|
19
|
+
.atlas-copy { max-width:42rem; } .atlas-copy p { max-width:38rem; color:var(--muted); } .atlas-kicker { margin:0 0 1.25rem; color:var(--acid) !important; font-size:.78rem; letter-spacing:.08em; text-transform:uppercase; }
|
|
20
|
+
h1, h2 { max-width:13ch; margin:0; font-family:Georgia,"Songti SC",STSong,serif; font-size:clamp(2.8rem,4.3vw,4.5rem); line-height:1; letter-spacing:-.045em; } h2 { font-size:clamp(2.35rem,3.7vw,3.8rem); }
|
|
21
|
+
.atlas-ledger { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:0; margin:1.75rem 0 0; border-top:2px solid var(--paper); border-bottom:1px solid var(--line); } .atlas-ledger div { min-height:5.2rem; padding:1rem .8rem 1rem 0; border-right:1px solid var(--line); } .atlas-ledger div + div { padding-left:.8rem; } .atlas-ledger div:last-child { border-right:0; } .atlas-ledger dt { color:var(--acid); font-size:.72rem; font-weight:700; letter-spacing:.06em; } .atlas-ledger dd { margin:.5rem 0 0; color:var(--paper); font-size:.88rem; line-height:1.35; }
|
|
22
|
+
.atlas-visual { position:relative; min-height:min(55vw,34rem); display:grid; align-content:center; gap:.8rem; } .atlas-visual svg { width:min(100%,36rem); height:auto; overflow:visible; }
|
|
23
|
+
.atlas-visual text { fill:var(--paper); font:500 13px ui-sans-serif,system-ui,sans-serif; } .atlas-visual .muted { fill:var(--muted); } .atlas-visual .accent { fill:var(--acid); } .atlas-visual .line { stroke:var(--line); } .atlas-visual .active-line { stroke:var(--acid); } .atlas-visual path.line, .atlas-visual path.active-line { fill:none; }
|
|
24
|
+
.atlas-note { margin-top:1.1rem; color:var(--muted); font-size:.85rem; } .atlas-note code { color:var(--paper); } .atlas-diagram-note { max-width:36rem; color:var(--muted); font-size:.82rem; } .atlas-diagram-note b { color:var(--paper); font-weight:500; }
|
|
25
|
+
.atlas-controls { position:relative; z-index:2; display:grid; grid-template-columns:1fr auto 1fr; align-items:center; gap:.8rem; padding:1rem clamp(1rem,3vw,3.5rem); border-top:1px solid var(--line); }
|
|
26
|
+
.atlas-control { display:inline-flex; align-items:center; gap:.65rem; width:max-content; padding:.4rem 0; color:var(--paper); } .atlas-control:last-child { justify-self:end; } .atlas-control b { color:var(--acid); font-size:1.25rem; font-weight:400; }
|
|
27
|
+
#atlas-position { color:var(--muted); font-variant-numeric:tabular-nums; font-size:.78rem; letter-spacing:.08em; }
|
|
28
|
+
.atlas-chapter-nav { position:absolute; z-index:3; inset:auto 0 4.7rem; display:flex; justify-content:center; gap:.45rem; pointer-events:none; } .atlas-chapter-nav button { width:.55rem; height:.55rem; padding:0; border:1px solid var(--muted); border-radius:50%; pointer-events:auto; }
|
|
29
|
+
.atlas-chapter-nav button[aria-current="true"] { border-color:var(--acid); background:var(--acid); }
|
|
30
|
+
.atlas-map-stage { display:none; align-content:center; gap:clamp(1.5rem,4vw,4rem); padding:clamp(1.5rem,5vw,5rem); overflow:auto; }
|
|
31
|
+
.atlas-map-heading { display:flex; justify-content:space-between; align-items:end; gap:2rem; } .atlas-map-heading h2 { max-width:14ch; } .atlas-map-heading p { max-width:34rem; margin:0; color:var(--muted); }
|
|
32
|
+
#atlas-audit-map { display:grid; grid-template-columns:minmax(0,1.65fr) minmax(15rem,.7fr); gap:1rem; align-items:stretch; } .atlas-map-canvas { min-height:22rem; border:1px solid var(--line); background:var(--panel); } .atlas-map-canvas svg { display:block; width:100%; height:100%; min-height:22rem; }
|
|
33
|
+
.atlas-map-canvas .edge { fill:none; stroke:var(--muted); stroke-width:1.25; } .atlas-map-canvas .node { fill:var(--panel-2); stroke:var(--muted); stroke-width:1.2; cursor:pointer; } .atlas-map-canvas .node:hover, .atlas-map-canvas .node:focus { stroke:var(--acid); } .atlas-map-canvas .node.active { fill:var(--acid); } .atlas-map-canvas .node-text { fill:var(--paper); font-size:12px; pointer-events:none; } .atlas-map-canvas .node.active + text { fill:var(--ink); }
|
|
34
|
+
.atlas-inspector { display:flex; flex-direction:column; justify-content:space-between; gap:2rem; padding:1.35rem; border:1px solid var(--line); background:var(--panel-2); } .atlas-inspector h3 { margin:0; font-family:Georgia,"Songti SC",STSong,serif; font-size:1.35rem; } .atlas-inspector p { color:var(--muted); } .atlas-source { margin:0; color:var(--acid) !important; font:12px/1.45 ui-monospace,SFMono-Regular,monospace; overflow-wrap:anywhere; }
|
|
35
|
+
[data-atlas-mode="map"] { overflow:auto; } [data-atlas-mode="map"] .atlas-stage, [data-atlas-mode="map"] .atlas-controls, [data-atlas-mode="map"] .atlas-chapter-nav { display:none; } [data-atlas-mode="map"] .atlas-map-stage { display:grid; }
|
|
36
|
+
@media (max-width:760px) { .atlas-slide { min-height:0; height:100%; grid-template-columns:1fr; grid-template-rows:auto minmax(16rem,1fr); align-content:start; gap:1.2rem; padding:2rem 1.25rem 3rem; overflow-y:auto; } .atlas-visual { min-height:15rem; } .atlas-copy p { font-size:.94rem; } .atlas-ledger { grid-template-columns:repeat(3,minmax(0,1fr)); } .atlas-ledger dd { font-size:.76rem; } .atlas-ledger dt { font-size:.65rem; } .atlas-visual text { font-size:16px; } .atlas-map-canvas .node-text { font-size:22px; } h1, h2 { max-width:12ch; } .atlas-topbar { padding:1rem 1.25rem; } .atlas-title { align-self:center; } .atlas-views button { font-size:.82rem; } .atlas-controls { padding:.85rem 1.25rem; } #atlas-audit-map { grid-template-columns:1fr; } .atlas-map-heading { display:block; } .atlas-map-heading p { margin-top:1rem; } }
|
|
37
|
+
@media (prefers-reduced-motion:reduce) { .atlas-track { transition:none; } }
|
|
38
|
+
</style>
|
|
39
|
+
</head>
|
|
40
|
+
<body data-atlas-experience="deck-map-v1" data-atlas-mode="deck">
|
|
41
|
+
<div class="atlas-shell">
|
|
42
|
+
<header class="atlas-topbar">
|
|
43
|
+
<p class="atlas-title">LOOM <span>Decision Atlas</span></p>
|
|
44
|
+
<div class="atlas-views" aria-label="阅读方式">
|
|
45
|
+
<button type="button" data-atlas-view="deck" aria-pressed="true">放映</button>
|
|
46
|
+
<button type="button" data-atlas-view="map" aria-pressed="false">审查地图</button>
|
|
47
|
+
</div>
|
|
48
|
+
</header>
|
|
49
|
+
|
|
50
|
+
<main class="atlas-stage" aria-label="Atlas 放映">
|
|
51
|
+
<div class="atlas-track" id="atlas-track">
|
|
52
|
+
<section class="atlas-slide" id="atlas-origin" data-atlas-slide aria-label="起点">
|
|
53
|
+
<div class="atlas-copy" data-atlas-example>
|
|
54
|
+
<p class="atlas-kicker">从一个承诺开始</p><h1>为什么要这样做?</h1>
|
|
55
|
+
<p>在这里把原则、北极星与非目标收成一个可被质疑的起点。不要逐段复述文档,让每条取舍都看得见。</p>
|
|
56
|
+
<dl class="atlas-ledger"><div><dt>要守住</dt><dd>北极星承诺</dd></div><div><dt>明确不做</dt><dd>非目标边界</dd></div><div><dt>可被挑战</dt><dd>取舍理由</dd></div></dl>
|
|
57
|
+
<p class="atlas-note">来源 <code>00_PHILOSOPHY/*</code></p>
|
|
58
|
+
</div>
|
|
59
|
+
<div class="atlas-visual" data-atlas-example><svg viewBox="0 0 520 400" role="img" aria-labelledby="origin-title origin-desc"><title id="origin-title">原则与非目标的取舍构图</title><desc id="origin-desc">中心北极星被原则支撑,非目标位于边缘以示明确排除。</desc><circle cx="260" cy="200" r="145" fill="none" class="line"/><circle cx="260" cy="200" r="76" fill="none" class="active-line"/><path d="M260 50V350M110 200H410" class="line"/><circle cx="260" cy="200" r="24" fill="var(--acid)"/><text x="260" y="206" text-anchor="middle" fill="var(--ink)">北极星</text><text x="260" y="28" text-anchor="middle" class="accent">原则</text><text x="438" y="204" class="muted">非目标</text><text x="260" y="372" text-anchor="middle" class="muted">可质疑的取舍</text></svg><p class="atlas-diagram-note"><b>读图:</b>中心不是结论,外围才是判断成立的边界。</p></div>
|
|
60
|
+
</section>
|
|
61
|
+
<section class="atlas-slide" id="atlas-structure" data-atlas-slide aria-label="结构">
|
|
62
|
+
<div class="atlas-copy" data-atlas-example><p class="atlas-kicker">把承诺落成结构</p><h2>系统如何不背叛它?</h2><p>用架构边界与 Intent 依赖代替长文说明。每层只回答一件事:它保护了什么,它依赖什么。</p><dl class="atlas-ledger"><div><dt>上层</dt><dd>用户可感知边界</dd></div><div><dt>中层</dt><dd>系统与数据约束</dd></div><div><dt>下层</dt><dd>可验证交付</dd></div></dl><p class="atlas-note">来源 <code>01_VISION.md · 02_ARCHITECTURE.md · 04_INTENT_MAP.json</code></p></div>
|
|
63
|
+
<div class="atlas-visual" data-atlas-example><svg viewBox="0 0 520 400" role="img" aria-labelledby="structure-title structure-desc"><title id="structure-title">架构层与 Intent 依赖</title><desc id="structure-desc">三层架构通过一条意图依赖线连接。</desc><rect x="88" y="72" width="344" height="64" rx="2" fill="var(--panel-2)" class="line"/><rect x="118" y="166" width="284" height="64" rx="2" fill="var(--panel)" class="line"/><rect x="148" y="260" width="224" height="64" rx="2" fill="var(--panel-2)" class="active-line"/><text x="112" y="111">体验与接口边界</text><text x="142" y="205">系统与数据边界</text><text x="172" y="299">可验证的交付</text><path d="M260 136V166M260 230V260" class="active-line"/><circle cx="260" cy="150" r="6" fill="var(--acid)"/><circle cx="260" cy="245" r="6" fill="var(--acid)"/><text x="445" y="151" class="accent">Intent</text></svg><p class="atlas-diagram-note"><b>读图:</b>亮线代表从体验承诺到可验证交付的追溯路径。</p></div>
|
|
64
|
+
</section>
|
|
65
|
+
<section class="atlas-slide" id="atlas-capabilities" data-atlas-slide aria-label="能力">
|
|
66
|
+
<div class="atlas-copy" data-atlas-example><p class="atlas-kicker">能力不是装饰</p><h2>为结果配置真实专长。</h2><p>把 Outcome、Concern、Capability、专业领域与证据连成一张图。点击一个节点时,应能看到它具体服务哪条承诺。</p><dl class="atlas-ledger"><div><dt>结果</dt><dd>用户最终获得什么</dd></div><div><dt>能力</dt><dd>需要解决什么问题</dd></div><div><dt>证据</dt><dd>如何确认它成立</dd></div></dl><p class="atlas-note">来源 <code>07_CAPABILITY_GRAPH.json · 07_CAPABILITY_BRIEFS/*</code></p></div>
|
|
67
|
+
<div class="atlas-visual" data-atlas-example><svg viewBox="0 0 520 400" role="img" aria-labelledby="capability-title capability-desc"><title id="capability-title">能力链关系图</title><desc id="capability-desc">结果、关注点、能力和专业领域形成一条可追溯能力链。</desc><path d="M86 200H202M316 200H434M260 94V306" class="active-line"/><circle cx="72" cy="200" r="48" fill="var(--panel-2)" class="line"/><circle cx="260" cy="94" r="38" fill="var(--panel)" class="line"/><circle cx="260" cy="200" r="58" fill="var(--acid)"/><circle cx="260" cy="306" r="38" fill="var(--panel)" class="line"/><circle cx="448" cy="200" r="48" fill="var(--panel-2)" class="line"/><text x="72" y="205" text-anchor="middle">结果</text><text x="260" y="99" text-anchor="middle" class="muted">关注点</text><text x="260" y="205" text-anchor="middle" fill="var(--ink)">能力</text><text x="260" y="311" text-anchor="middle" class="muted">领域</text><text x="448" y="205" text-anchor="middle">证据</text></svg><p class="atlas-diagram-note"><b>读图:</b>不是所有专业名词都值得上图,只有能连回结果的能力才留下。</p></div>
|
|
68
|
+
</section>
|
|
69
|
+
<section class="atlas-slide" id="atlas-decisions" data-atlas-slide aria-label="决策">
|
|
70
|
+
<div class="atlas-copy" data-atlas-example><p class="atlas-kicker">不是唯一正确</p><h2>每个选择都保留代价。</h2><p>把 ADR 处理成分叉,而不是胜利清单。读者应该能一眼看到选择什么、没有选择什么,以及依据来自哪里。</p><dl class="atlas-ledger"><div><dt>问题</dt><dd>必须作出的取舍</dd></div><div><dt>选择</dt><dd>被采纳的方向</dd></div><div><dt>代价</dt><dd>仍需承担的后果</dd></div></dl><p class="atlas-note">来源 <code>03_DECISIONS/*</code></p></div>
|
|
71
|
+
<div class="atlas-visual" data-atlas-example><svg viewBox="0 0 520 400" role="img" aria-labelledby="decision-title decision-desc"><title id="decision-title">决策分叉与取舍</title><desc id="decision-desc">一个问题分出两种方案,选定路径以亮色表达,另一条保留其代价。</desc><path d="M72 200H214M214 200L420 92M214 200L420 308" class="line"/><path d="M72 200H214L420 92" class="active-line" stroke-width="3"/><circle cx="72" cy="200" r="34" fill="var(--panel-2)" class="line"/><circle cx="214" cy="200" r="26" fill="var(--acid)"/><circle cx="420" cy="92" r="42" fill="var(--panel-2)" class="active-line"/><circle cx="420" cy="308" r="42" fill="var(--panel)" class="line"/><text x="72" y="205" text-anchor="middle">问题</text><text x="214" y="205" text-anchor="middle" fill="var(--ink)">取舍</text><text x="420" y="97" text-anchor="middle">选择</text><text x="420" y="313" text-anchor="middle" class="muted">舍弃</text></svg><p class="atlas-diagram-note"><b>读图:</b>暗线不是被隐藏的失败,它是这条选择真实的成本。</p></div>
|
|
72
|
+
</section>
|
|
73
|
+
<section class="atlas-slide" id="atlas-review" data-atlas-slide aria-label="审查">
|
|
74
|
+
<div class="atlas-copy" data-atlas-example><p class="atlas-kicker">把缺口留在光下</p><h2>哪里仍然不够好?</h2><p>这里不宣告完成。它只留下结构上的断线、待确认项和能返回原始资料的入口,供下一次判断使用。</p><dl class="atlas-ledger"><div><dt>断线</dt><dd>需要补回的关系</dd></div><div><dt>待确认</dt><dd>尚无依据的判断</dd></div><div><dt>回到资料</dt><dd>每项都有来源</dd></div></dl><p class="atlas-note">来源 <code>Atlas structural_review · source_refs</code></p></div>
|
|
75
|
+
<div class="atlas-visual" data-atlas-example><svg viewBox="0 0 520 400" role="img" aria-labelledby="review-title review-desc"><title id="review-title">结构性缺口审查</title><desc id="review-desc">完整链路和断开的链路并置,提醒审查者回到来源资料。</desc><path d="M72 200H168M224 200H304M360 200H448" class="active-line"/><path d="M168 200H224M304 200H360" stroke="var(--danger)" stroke-width="2" stroke-dasharray="7 8"/><circle cx="72" cy="200" r="28" fill="var(--panel-2)" class="line"/><circle cx="196" cy="200" r="28" fill="var(--panel-2)" stroke="var(--danger)" stroke-width="2"/><circle cx="332" cy="200" r="28" fill="var(--panel-2)" stroke="var(--danger)" stroke-width="2"/><circle cx="448" cy="200" r="28" fill="var(--panel-2)" class="line"/><text x="72" y="205" text-anchor="middle">目标</text><text x="196" y="205" text-anchor="middle">?</text><text x="332" y="205" text-anchor="middle">?</text><text x="448" y="205" text-anchor="middle">证据</text></svg><p class="atlas-diagram-note"><b>读图:</b>断线是待查路径,不是完成率,也不替人下结论。</p></div>
|
|
76
|
+
</section>
|
|
77
|
+
</div>
|
|
78
|
+
</main>
|
|
79
|
+
|
|
80
|
+
<section class="atlas-map-stage" aria-label="Atlas 审查地图">
|
|
81
|
+
<div class="atlas-map-heading"><div><p class="atlas-kicker">从关系开始审查</p><h2>一条链,还是一团雾?</h2></div><p>这里应从 Atlas Model 生成可点击节点。选择节点后,检查台只展示必要解释、连接对象和来源,不重复整篇文档。</p></div>
|
|
82
|
+
<div id="atlas-audit-map">
|
|
83
|
+
<div class="atlas-map-canvas" data-atlas-example><svg viewBox="0 0 760 430" role="img" aria-labelledby="map-title map-desc"><title id="map-title">目标到验证设计的审查链</title><desc id="map-desc">从目标、能力、专业领域、Intent 到验证设计的一条可点击关系链。</desc><path class="edge" d="M126 215H258M350 215H454M546 215H652"/><g data-atlas-node="目标" role="button" tabindex="0"><rect class="node active" x="30" y="170" width="96" height="90" rx="2"/><text class="node-text" x="78" y="220" text-anchor="middle">目标</text></g><g data-atlas-node="能力" role="button" tabindex="0"><rect class="node" x="258" y="170" width="92" height="90" rx="2"/><text class="node-text" x="304" y="220" text-anchor="middle">能力</text></g><g data-atlas-node="领域" role="button" tabindex="0"><rect class="node" x="454" y="170" width="92" height="90" rx="2"/><text class="node-text" x="500" y="220" text-anchor="middle">领域</text></g><g data-atlas-node="Intent" role="button" tabindex="0"><rect class="node" x="652" y="170" width="78" height="90" rx="2"/><text class="node-text" x="691" y="220" text-anchor="middle">Intent</text></g><path class="edge" d="M691 260V350H304V260"/><text class="node-text" x="496" y="380" text-anchor="middle">验证设计</text></svg></div>
|
|
84
|
+
<aside class="atlas-inspector" aria-live="polite"><div><p class="atlas-kicker">检查台</p><h3 id="atlas-inspector-title">目标</h3><p id="atlas-inspector-copy">选择一个节点,查看它在当前决策结构中的作用与连接。</p></div><p class="atlas-source" id="atlas-inspector-source">source_refs: Atlas Model</p></aside>
|
|
85
|
+
</div>
|
|
86
|
+
</section>
|
|
87
|
+
|
|
88
|
+
<nav class="atlas-chapter-nav" aria-label="章节定位"><button type="button" data-atlas-jump="0" aria-current="true" aria-label="起点"></button><button type="button" data-atlas-jump="1" aria-current="false" aria-label="结构"></button><button type="button" data-atlas-jump="2" aria-current="false" aria-label="能力"></button><button type="button" data-atlas-jump="3" aria-current="false" aria-label="决策"></button><button type="button" data-atlas-jump="4" aria-current="false" aria-label="审查"></button></nav>
|
|
89
|
+
<footer class="atlas-controls"><button type="button" class="atlas-control" data-atlas-control="previous"><b>←</b> 上一章</button><span id="atlas-position" aria-live="polite">起点 1 / 5</span><button type="button" class="atlas-control" data-atlas-control="next">下一章 <b>→</b></button></footer>
|
|
90
|
+
</div>
|
|
91
|
+
<script>
|
|
92
|
+
(() => {
|
|
93
|
+
const root = document.body; const track = document.getElementById('atlas-track'); const slides = [...document.querySelectorAll('[data-atlas-slide]')]; const jumps = [...document.querySelectorAll('[data-atlas-jump]')]; const position = document.getElementById('atlas-position'); const names = ['起点', '结构', '能力', '决策', '审查']; let current = 0; let touchStart = null;
|
|
94
|
+
const go = (index) => { current = Math.max(0, Math.min(slides.length - 1, index)); track.style.transform = `translate3d(${-current * 20}%,0,0)`; jumps.forEach((button, i) => button.setAttribute('aria-current', String(i === current))); position.textContent = `${names[current]} ${current + 1} / ${slides.length}`; };
|
|
95
|
+
document.querySelector('[data-atlas-control="previous"]').addEventListener('click', () => go(current - 1)); document.querySelector('[data-atlas-control="next"]').addEventListener('click', () => go(current + 1)); jumps.forEach((button) => button.addEventListener('click', () => go(Number(button.dataset.atlasJump))));
|
|
96
|
+
document.querySelectorAll('[data-atlas-view]').forEach((button) => button.addEventListener('click', () => { const mode = button.dataset.atlasView; root.dataset.atlasMode = mode; document.querySelectorAll('[data-atlas-view]').forEach((view) => view.setAttribute('aria-pressed', String(view === button))); }));
|
|
97
|
+
window.addEventListener('keydown', (event) => { if (root.dataset.atlasMode !== 'deck' || /INPUT|TEXTAREA|SELECT/.test(document.activeElement.tagName)) return; if (event.key === 'ArrowLeft') go(current - 1); if (event.key === 'ArrowRight') go(current + 1); });
|
|
98
|
+
track.addEventListener('touchstart', (event) => { touchStart = event.changedTouches[0].clientX; }, { passive:true }); track.addEventListener('touchend', (event) => { if (touchStart === null) return; const distance = event.changedTouches[0].clientX - touchStart; if (Math.abs(distance) > 44) go(current + (distance < 0 ? 1 : -1)); touchStart = null; }, { passive:true });
|
|
99
|
+
const nodeDetails = { 目标:['目标','它定义这张图最终要守住的用户结果。','source_refs: capability_graph.outcomes'], 能力:['能力','它是为结果解决具体问题的可观察专长。','source_refs: capability_graph.capabilities'], 领域:['领域','它标出需要引入的专业知识,而不是贴标签。','source_refs: capability_domains'], Intent:['Intent','它把能力落成可实现、可验证的产品承诺。','source_refs: intent_structure'] };
|
|
100
|
+
document.querySelectorAll('[data-atlas-node]').forEach((node) => { const inspect = () => { const [title, copy, source] = nodeDetails[node.dataset.atlasNode]; document.getElementById('atlas-inspector-title').textContent = title; document.getElementById('atlas-inspector-copy').textContent = copy; document.getElementById('atlas-inspector-source').textContent = source; document.querySelectorAll('[data-atlas-node] .node').forEach((shape) => shape.classList.remove('active')); node.querySelector('.node').classList.add('active'); }; node.addEventListener('click', inspect); node.addEventListener('keydown', (event) => { if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); inspect(); } }); });
|
|
101
|
+
})();
|
|
102
|
+
</script>
|
|
103
|
+
</body>
|
|
104
|
+
</html>
|