@haaaiawd/loom 1.0.0 → 1.1.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.
@@ -11,6 +11,10 @@ import { validatePatches } from './patch.js';
11
11
  import { formatIntentRef } from './shared/intent-ref.js';
12
12
  import { commandCoversVerificationMethod, getIntentVerificationMethod } from './shared/verification-method.js';
13
13
  import { resolveQualityProofReference } from './shared/proof-reference.js';
14
+ import { getCapabilityCoverage, getCapabilityGraphPath, loadCapabilityGraph } from './capability-graph.js';
15
+ import { listCapabilityProposals } from './capability-proposals.js';
16
+ import { getAssetManifestPath, validateAssetLibrary } from './asset-library.js';
17
+ import { validateAtelierRecord } from './atelier.js';
14
18
 
15
19
  function readIntentMapRaw(versionDir) {
16
20
  const filePath = join(versionDir, '04_INTENT_MAP.json');
@@ -161,7 +165,20 @@ const FIX_HINTS = {
161
165
  preservation_dimension_missing: '为 continuity_required 的 Intent 补写并通过 preservation_achievement,证据必须覆盖旧状态到新操作后的完整序列',
162
166
  patch_changelog_invalid: '运行 loom patch validate 查看具体错误;修正 06_CHANGELOG.json 后重新生成 Markdown 投影',
163
167
  patch_projection_drift: '不要手工编辑 06_CHANGELOG.md;重新运行 loom init 或下一次 loom patch record 生成投影',
164
- };
168
+ capability_graph_missing: '这是 1.0 项目的兼容提示;迁移时运行 loom activate architect,创建 07_CAPABILITY_GRAPH.json 并路由高影响节点',
169
+ capability_graph_template: '运行 loom activate architect,先将愿景展开为 Capability Graph,再创建或修订 Intent Map',
170
+ capability_graph_invalid: '修正 07_CAPABILITY_GRAPH.json 的节点、关系或枚举值后运行 loom capability coverage',
171
+ capability_frontier_open: '为高影响节点继续展开、创建 Capability Brief、编译为 Intent,或明确延后/排除理由',
172
+ capability_plan_missing: '为高影响 capability 节点补齐可用 Brief 与获取计划,或改为明确的其他路由',
173
+ capability_route_evidence: '为该节点补齐路由所需的 Intent 回链、Brief 或延后/排除理由,避免只写一个状态标签',
174
+ capability_outcome_unexpanded: '将 outcome 连接到至少一个 concern,明确项目初衷需要被处理的问题面',
175
+ capability_outcome_unobservable: '为高影响 outcome 新建或补齐 evidence 节点,并以 validated_by 连接;evidence 必须写明观察目标、复现步骤、通过标准、证据产物,并回链承担验证的 Intent',
176
+ intent_graph_unmapped: '将该 Intent 回链到至少一个 Capability Graph 节点;不要让执行承诺失去项目初衷和能力来源',
177
+ capability_proposal_pending: '由 Architect 审核 proposal:判定已覆盖、更新 Graph、生成/修订 Intent、改变 acceptance,或升级 Minor/Major;Forge 不得静默把候选写进正式图谱。',
178
+ asset_library_invalid: '修复 08_ASSET_LIBRARY/manifest.json 的来源、许可、哈希、库内路径或 evidence 双向引用,然后运行 loom asset validate。',
179
+ atelier_record_invalid: '运行 loom atelier init {id} 创建记录,或按校验错误修正后运行 loom atelier validate {id}。',
180
+ atelier_verification_missing: '重新运行独立 Keeper 验证,让 passed 记录绑定当前 Atelier Record 与 stance_revision。',
181
+ };
165
182
 
166
183
  /**
167
184
  * 给 issue 补 fix_hint——把 {id} {dep} 等占位符替换成实际值。
@@ -196,8 +213,76 @@ export function doctor(versionDir, verificationsDir, philosophyDir) {
196
213
  return { issues: issuesWithHints, summary: summarizeIssues(issuesWithHints) };
197
214
  }
198
215
 
199
- const { intents } = mapState.validMap;
200
-
216
+ const { intents } = mapState.validMap;
217
+ const atelierRecords = new Map();
218
+
219
+ for (const [id, intent] of Object.entries(intents)) {
220
+ if (intent.quality_strategy !== 'atelier' || intent.status === 'pending' || intent.status === 'blocked') continue;
221
+ try {
222
+ const record = validateAtelierRecord(versionDir, id);
223
+ atelierRecords.set(id, record);
224
+ if (intent.status === 'completed' && !['selected', 'baseline_retained'].includes(record.status)) {
225
+ issues.push({ id, type: 'atelier_record_invalid', severity: 'high', msg: `${id} 已 completed,但 Atelier Record 仍是 ${record.status}` });
226
+ }
227
+ } catch (error) {
228
+ issues.push({ id, type: 'atelier_record_invalid', severity: 'high', msg: error.message });
229
+ }
230
+ }
231
+
232
+ // 0. Incoming Graph changes are an Architect gate, not Forge implementation scope.
233
+ try {
234
+ for (const proposal of listCapabilityProposals(versionDir, { unresolvedOnly: true })) {
235
+ issues.push({ id: proposal.id, type: 'capability_proposal_pending', severity: 'high', msg: `${proposal.id} 是尚未闭合的 ${proposal.origin} 候选(${proposal.candidate_kind}: ${proposal.title});必须由 Architect 决定其影响。` });
236
+ }
237
+ } catch (error) {
238
+ issues.push({ id: 'capability_proposals', type: 'capability_proposal_pending', severity: 'high', msg: `Capability Graph proposal 无法审计: ${error.message}` });
239
+ }
240
+
241
+ // Asset Library is optional for a project, but once initialized it is a local source of truth.
242
+ if (existsSync(getAssetManifestPath(versionDir))) {
243
+ try { validateAssetLibrary(versionDir); } catch (error) {
244
+ issues.push({ id: 'asset_library', type: 'asset_library_invalid', severity: 'high', msg: error.message });
245
+ }
246
+ }
247
+
248
+ // 0. Capability Graph:旧项目允许缺失但提示迁移;图谱一旦存在则必须可读、可路由。
249
+ const capabilityGraphPath = getCapabilityGraphPath(versionDir);
250
+ if (!existsSync(capabilityGraphPath)) {
251
+ issues.push({ id: 'capability_graph', type: 'capability_graph_missing', severity: 'medium', msg: '缺少 07_CAPABILITY_GRAPH.json;现有项目仍可运行,但不会获得图谱驱动的能力拆分与覆盖检查。' });
252
+ } else {
253
+ try {
254
+ const graph = loadCapabilityGraph(versionDir);
255
+ if (graph._meta?._template === true) {
256
+ issues.push({ id: 'capability_graph', type: 'capability_graph_template', severity: 'high', msg: 'Capability Graph 仍是模板;不得在未路由项目问题与能力缺口时宣告架构完成。' });
257
+ } else {
258
+ const coverage = getCapabilityCoverage(versionDir);
259
+ for (const node of coverage.high_unrouted) {
260
+ issues.push({ id: node.id, type: 'capability_frontier_open', severity: 'high', msg: `${node.id} 是尚未路由的高影响 Capability Graph 节点: ${node.title}` });
261
+ }
262
+ for (const item of coverage.capabilities_without_plan) {
263
+ issues.push({ id: item.node_id, type: 'capability_plan_missing', severity: 'high', msg: `${item.node_id} 的能力获取计划不完整: ${item.reason}` });
264
+ }
265
+ for (const item of coverage.routing_gaps) {
266
+ issues.push({ id: item.node_id, type: 'capability_route_evidence', severity: 'high', msg: `${item.node_id} 的图谱路由缺少依据: ${item.reason}` });
267
+ }
268
+ for (const item of coverage.outcomes_without_concern) {
269
+ issues.push({ id: item.node_id, type: 'capability_outcome_unexpanded', severity: 'high', msg: `${item.node_id} 没有展开为项目问题面: ${item.reason}` });
270
+ }
271
+ for (const item of coverage.high_outcomes_without_observable_evidence) {
272
+ issues.push({ id: item.node_id, type: 'capability_outcome_unobservable', severity: 'high', msg: `${item.node_id} 缺少真实呈现或交付的验证入口: ${item.reason}` });
273
+ }
274
+ for (const item of coverage.orphan_intent_refs) {
275
+ issues.push({ id: item.node_id, type: 'intent_graph_unmapped', severity: 'high', msg: `${item.node_id} 引用了不存在的 Intent: ${item.intent_id}` });
276
+ }
277
+ for (const intentId of coverage.unmapped_intents) {
278
+ issues.push({ id: intentId, type: 'intent_graph_unmapped', severity: 'high', msg: `${intentId} 没有 Capability Graph 回链;执行承诺缺少能力与问题来源。` });
279
+ }
280
+ }
281
+ } catch (error) {
282
+ issues.push({ id: 'capability_graph', type: 'capability_graph_invalid', severity: 'high', msg: error.message });
283
+ }
284
+ }
285
+
201
286
  // 1. 状态一致性:completed 必须由当前 revision 的最后一条 passed 验证支撑。
202
287
  for (const [id, intent] of Object.entries(intents)) {
203
288
  const history = getVerificationHistory(verificationsDir, id);
@@ -229,6 +314,20 @@ export function doctor(versionDir, verificationsDir, philosophyDir) {
229
314
  }
230
315
  }
231
316
  }
317
+ if (intent.quality_strategy === 'atelier' && latest?.verdict === 'passed') {
318
+ const atelier = atelierRecords.get(id);
319
+ if (!atelier
320
+ || latest.atelier?.record_ref !== `09_ATELIER/${id}.json`
321
+ || latest.atelier?.stance_revision !== atelier.stance_revision
322
+ || latest.atelier?.status !== atelier.status) {
323
+ issues.push({
324
+ id,
325
+ type: 'atelier_verification_missing',
326
+ severity: 'high',
327
+ msg: `${id} 的最新 passed 未绑定当前 Atelier Record、status 与 stance_revision`,
328
+ });
329
+ }
330
+ }
232
331
  if (intent.continuity_required && latest?.verdict === 'passed') {
233
332
  const preservation = latest.dimensions?.preservation_achievement;
234
333
  if (!preservation || preservation.verdict !== 'passed') {
package/cli/src/guide.js CHANGED
@@ -6,6 +6,8 @@ import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
6
6
  import { join } from 'node:path';
7
7
  import { readCurrentPointer } from './version.js';
8
8
  import { loadIntentMap } from './intent-map.js';
9
+ import { getCapabilityCoverage } from './capability-graph.js';
10
+ import { listCapabilityProposals } from './capability-proposals.js';
9
11
  import { isAutoOn, getAutoMode, writeHeartbeat, needsHumanReview } from './auto.js';
10
12
  import { doctor } from './diagnostics.js';
11
13
 
@@ -70,11 +72,26 @@ export function guideProject(projectDir, options = {}) {
70
72
  outputs: [`.loom/${current}/01_VISION.md`],
71
73
  verify_command: 'loom guide',
72
74
  },
73
- need_architecture: {
74
- inputs: ['roles/architect.md', `.loom/${current}/01_VISION.md`],
75
- outputs: [`.loom/${current}/02_ARCHITECTURE.md`, `.loom/${current}/04_INTENT_MAP.json`],
76
- verify_command: 'loom doctor',
77
- },
75
+ need_architecture: {
76
+ inputs: ['roles/architect.md', `.loom/${current}/01_VISION.md`],
77
+ outputs: [`.loom/${current}/02_ARCHITECTURE.md`, `.loom/${current}/04_INTENT_MAP.json`],
78
+ verify_command: 'loom doctor',
79
+ },
80
+ need_capability_graph: {
81
+ inputs: ['roles/architect.md', `.loom/${current}/01_VISION.md`],
82
+ outputs: [`.loom/${current}/07_CAPABILITY_GRAPH.json`, `.loom/${current}/07_CAPABILITY_BRIEFS/`],
83
+ verify_command: 'loom capability coverage',
84
+ },
85
+ capability_graph_incomplete: {
86
+ inputs: ['roles/architect.md', `.loom/${current}/07_CAPABILITY_GRAPH.json`, `.loom/${current}/04_INTENT_MAP.json`],
87
+ outputs: [`.loom/${current}/07_CAPABILITY_GRAPH.json`, `.loom/${current}/07_CAPABILITY_BRIEFS/`],
88
+ verify_command: 'loom capability coverage',
89
+ },
90
+ capability_graph_proposals_pending: {
91
+ inputs: ['roles/architect.md', `.loom/${current}/07_GRAPH_PROPOSALS/`, `.loom/${current}/07_CAPABILITY_GRAPH.json`],
92
+ outputs: [`.loom/${current}/07_GRAPH_PROPOSALS/`, `.loom/${current}/07_CAPABILITY_GRAPH.json`, `.loom/${current}/04_INTENT_MAP.json`],
93
+ verify_command: 'loom capability proposal list',
94
+ },
78
95
  intent_map_broken: {
79
96
  inputs: [`.loom/${current}/04_INTENT_MAP.json`],
80
97
  outputs: [`.loom/${current}/04_INTENT_MAP.json`],
@@ -169,8 +186,9 @@ function diagnoseStage(cwd, loomRoot, auto) {
169
186
 
170
187
  const versionDir = join(loomRoot, current);
171
188
  const philosophyDir = join(versionDir, '00_PHILOSOPHY');
172
- const visionPath = join(versionDir, '01_VISION.md');
173
- const intentMapPath = join(versionDir, '04_INTENT_MAP.json');
189
+ const visionPath = join(versionDir, '01_VISION.md');
190
+ const intentMapPath = join(versionDir, '04_INTENT_MAP.json');
191
+ const capabilityGraphPath = join(versionDir, '07_CAPABILITY_GRAPH.json');
174
192
 
175
193
  // 状态 1: 哲学未织造
176
194
  const philosophyFile = join(philosophyDir, 'PRODUCT_PHILOSOPHY.md');
@@ -199,16 +217,77 @@ function diagnoseStage(cwd, loomRoot, auto) {
199
217
  };
200
218
  }
201
219
 
202
- // 状态 3: 愿景已定义,Intent Map 未设计
203
- if (isTemplate(intentMapPath)) {
204
- return {
205
- stage: 'need_architecture',
206
- stage_num: 3,
207
- details: { version: current },
208
- auto,
209
- next_action: '设计系统架构 + Intent Map',
210
- next_command: 'loom activate architect',
211
- message: `当前版本 ${current}:愿景已定义,Intent Map 还是模板,需要 Architect 设计。`,
220
+ // 状态 3: 愿景已定义,先展开项目问题面与能力缺口,再承诺 Intent
221
+ if (existsSync(capabilityGraphPath) && isTemplate(capabilityGraphPath)) {
222
+ return {
223
+ stage: 'need_capability_graph',
224
+ stage_num: 3,
225
+ details: { version: current },
226
+ auto,
227
+ next_action: '展开 Capability Graph,路由高影响问题与能力缺口',
228
+ next_command: 'loom activate architect',
229
+ message: `当前版本 ${current}:愿景已定义,但 Capability Graph 还是模板。Architect 必须先判断哪些体验、系统、资产、风险与能力适用,再创建正式 Intent。`,
230
+ };
231
+ }
232
+
233
+ // 图谱一旦存在,就不能把不完整路由悄悄跨过去。旧项目缺少该文件仍保留兼容路径。
234
+ if (existsSync(capabilityGraphPath)) {
235
+ try {
236
+ const pendingProposals = listCapabilityProposals(versionDir, { unresolvedOnly: true });
237
+ if (pendingProposals.length) {
238
+ return {
239
+ stage: 'capability_graph_proposals_pending',
240
+ stage_num: 3,
241
+ details: { version: current, proposals: pendingProposals.map((proposal) => proposal.id) },
242
+ auto,
243
+ next_action: '审计新信息对 Capability Graph、Intent 和契约的影响',
244
+ next_command: 'loom capability proposal list',
245
+ message: `当前版本 ${current} 有 ${pendingProposals.length} 个未闭合的 Capability Graph proposal。它们是新要求、研究或实现发现,不得由 Forge 静默变成当前 Intent 范围。`,
246
+ };
247
+ }
248
+ } catch (error) {
249
+ return {
250
+ stage: 'capability_graph_proposals_pending', stage_num: 3, details: { version: current, error: error.message }, auto,
251
+ next_action: '修复 Capability Graph proposal', next_command: 'loom capability proposal list',
252
+ message: `Capability Graph proposal 无法审计: ${error.message}`,
253
+ };
254
+ }
255
+ try {
256
+ const coverage = getCapabilityCoverage(versionDir);
257
+ if (!coverage.summary.ready) {
258
+ return {
259
+ stage: 'capability_graph_incomplete',
260
+ stage_num: 3.1,
261
+ details: { version: current, coverage: coverage.summary },
262
+ auto,
263
+ next_action: '补齐 Capability Graph 的路由、可观察验证入口与 Intent 回链',
264
+ next_command: 'loom capability coverage',
265
+ message: `当前版本 ${current}:Capability Graph 尚未闭合(高影响前沿 ${coverage.summary.high_unrouted}、路由缺口 ${coverage.summary.routing_gaps}、不可观察 outcome ${coverage.summary.high_outcomes_without_observable_evidence}、未映射 Intent ${coverage.summary.unmapped_intents})。先由 Architect 补图谱,再继续设计 Intent。`,
266
+ };
267
+ }
268
+ } catch (error) {
269
+ return {
270
+ stage: 'capability_graph_incomplete',
271
+ stage_num: 3.1,
272
+ details: { version: current, error: error.message },
273
+ auto,
274
+ next_action: '修复 Capability Graph',
275
+ next_command: 'loom capability coverage',
276
+ message: `Capability Graph 无法通过校验: ${error.message}`,
277
+ };
278
+ }
279
+ }
280
+
281
+ // 状态 3.5: 图谱已建立,Intent Map 未设计。
282
+ if (isTemplate(intentMapPath)) {
283
+ return {
284
+ stage: 'need_architecture',
285
+ stage_num: 3.5,
286
+ details: { version: current },
287
+ auto,
288
+ next_action: '从 Capability Graph 编译系统架构 + Intent Map',
289
+ next_command: 'loom activate architect',
290
+ message: `当前版本 ${current}:Capability Graph 已建立,Intent Map 还是模板。Architect 需要将已路由的问题面编译为系统架构与可闭合 Intent。`,
212
291
  };
213
292
  }
214
293
 
@@ -291,10 +370,24 @@ function diagnoseStage(cwd, loomRoot, auto) {
291
370
  };
292
371
  }
293
372
 
294
- // 状态 5: 有 in_progress
295
- if (counts.in_progress > 0) {
296
- const inProgressIds = allIntents.filter((i) => i.status === 'in_progress').map((i) => i.id);
297
- return {
373
+ // 状态 5: 有 in_progress
374
+ if (counts.in_progress > 0) {
375
+ const inProgressIds = allIntents.filter((i) => i.status === 'in_progress').map((i) => i.id);
376
+ const atelierWithoutRecord = allIntents.find((intent) => intent.status === 'in_progress'
377
+ && intent.quality_strategy === 'atelier'
378
+ && !existsSync(join(versionDir, '09_ATELIER', `${intent.id}.json`)));
379
+ if (atelierWithoutRecord) {
380
+ return {
381
+ stage: 'in_loop',
382
+ stage_num: 5,
383
+ details: { version: current, counts, in_progress_ids: inProgressIds, atelier_intent: atelierWithoutRecord.id },
384
+ auto,
385
+ next_action: '创建 Atelier Record 并冻结基线',
386
+ next_command: `loom atelier init ${atelierWithoutRecord.id}`,
387
+ message: `${atelierWithoutRecord.id} 已进入 Atelier Path,但还没有唯一创作记录。先创建 Record,再按 Forge Context Pack 形成 Authorial Stance。`,
388
+ };
389
+ }
390
+ return {
298
391
  stage: 'in_loop',
299
392
  stage_num: 5,
300
393
  details: { version: current, counts, in_progress_ids: inProgressIds },
package/cli/src/init.js CHANGED
@@ -46,8 +46,11 @@ export function createVersionStructure(projectDir, version, parentVersion = null
46
46
  '.loom',
47
47
  `.loom/${v}`,
48
48
  `.loom/${v}/00_PHILOSOPHY`,
49
- `.loom/${v}/verifications`,
50
- `.loom/${v}/03_DECISIONS`,
49
+ `.loom/${v}/verifications`,
50
+ `.loom/${v}/03_DECISIONS`,
51
+ `.loom/${v}/07_CAPABILITY_BRIEFS`,
52
+ `.loom/${v}/07_GRAPH_PROPOSALS`,
53
+ `.loom/${v}/08_ASSET_LIBRARY/files`,
51
54
  ];
52
55
 
53
56
  for (const d of dirs) {
@@ -63,7 +66,9 @@ export function createVersionStructure(projectDir, version, parentVersion = null
63
66
  const templates = [
64
67
  ['templates/INTENT_MAP_TEMPLATE.json', `.loom/${v}/04_INTENT_MAP.json`],
65
68
  ['templates/PHILOSOPHY_TEMPLATE.md', `.loom/${v}/00_PHILOSOPHY/PRODUCT_PHILOSOPHY.md`],
66
- ['templates/VISION_TEMPLATE.md', `.loom/${v}/01_VISION.md`],
69
+ ['templates/VISION_TEMPLATE.md', `.loom/${v}/01_VISION.md`],
70
+ ['templates/CAPABILITY_GRAPH_TEMPLATE.json', `.loom/${v}/07_CAPABILITY_GRAPH.json`],
71
+ ['templates/ASSET_LIBRARY_MANIFEST_TEMPLATE.json', `.loom/${v}/08_ASSET_LIBRARY/manifest.json`],
67
72
  ];
68
73
 
69
74
  // 02_ARCHITECTURE.md 和 05_VERIFICATION.md 没有"填空模板"——
@@ -114,7 +119,7 @@ export function createVersionStructure(projectDir, version, parentVersion = null
114
119
  skipped.push(dst);
115
120
  } else if (existsSync(srcPath)) {
116
121
  copyFileSync(srcPath, dstPath);
117
- if (dst.endsWith('04_INTENT_MAP.json')) {
122
+ if (dst.endsWith('04_INTENT_MAP.json') || dst.endsWith('07_CAPABILITY_GRAPH.json') || dst.endsWith('08_ASSET_LIBRARY/manifest.json')) {
118
123
  const map = JSON.parse(readFileSync(dstPath, 'utf-8'));
119
124
  map._meta._loom_version = v;
120
125
  map._meta._parent_version = parentVersion;
@@ -174,6 +174,17 @@ function validateLifecycle(data, id, lifecycle, errors) {
174
174
 
175
175
  function validateOptionalIntentFields(id, intent, errors) {
176
176
  const prefix = `intents["${id}"]`;
177
+ const qualityStrategy = intent.quality_strategy ?? 'adaptive';
178
+
179
+ if (!['adaptive', 'atelier'].includes(qualityStrategy)) {
180
+ errors.push(`${prefix}.quality_strategy 非法: ${JSON.stringify(qualityStrategy)} (合法: adaptive|atelier)`);
181
+ }
182
+
183
+ if ('proposal_refs' in intent) {
184
+ if (!Array.isArray(intent.proposal_refs) || intent.proposal_refs.some((ref) => typeof ref !== 'string' || !ref.trim())) {
185
+ errors.push(`${prefix}.proposal_refs 必须是非空 proposal ID 字符串数组`);
186
+ }
187
+ }
177
188
 
178
189
  if ('continuity_required' in intent && typeof intent.continuity_required !== 'boolean') {
179
190
  errors.push(`${prefix}.continuity_required 必须是布尔值;仅在本 Intent 会变更既有用户或系统状态且必须证明未误伤旧状态时设为 true`);
@@ -210,6 +221,15 @@ function validateOptionalIntentFields(id, intent, errors) {
210
221
  errors.push(`${prefix}.creative_scope 必须说明可以改变什么、必须保持什么`);
211
222
  }
212
223
  }
224
+
225
+ if (qualityStrategy === 'atelier') {
226
+ if (typeof intent.quality_contract !== 'string' || intent.quality_contract.trim().length < 10) {
227
+ errors.push(`${prefix}.quality_strategy=atelier 必须声明有效 quality_contract`);
228
+ }
229
+ if (typeof intent.creative_scope !== 'string' || intent.creative_scope.trim().length < 10) {
230
+ errors.push(`${prefix}.quality_strategy=atelier 必须声明有效 creative_scope`);
231
+ }
232
+ }
213
233
  }
214
234
 
215
235
  function validateLineage(data, id, lineage, errors) {
package/cli/src/verify.js CHANGED
@@ -7,6 +7,7 @@ import { extractMdSection, readJsonFile } from './shared/md-utils.js';
7
7
  import { getIntent, getEffectiveVerificationEpoch, hasLegacyIntentRevision } from './intent-map.js';
8
8
  import { formatIntentRef, resolveIntentRef } from './shared/intent-ref.js';
9
9
  import { resolveQualityProofReference } from './shared/proof-reference.js';
10
+ import { validateAtelierRecord } from './atelier.js';
10
11
 
11
12
  /** 合法判定结果 */
12
13
  const VALID_VERDICTS = ['passed', 'deviated', 'blocked', 'pending_human'];
@@ -44,6 +45,7 @@ function getRequiredDimensions(intent) {
44
45
  */
45
46
  export function writeVerification(versionDir, verificationsDir, record) {
46
47
  const errors = [];
48
+ let atelierEvidence = null;
47
49
  if (!record.intent_id) errors.push('缺少 intent_id');
48
50
  if (!record.verdict || !VALID_VERDICTS.includes(record.verdict)) {
49
51
  errors.push(`verdict 非法: "${record.verdict}" (合法: ${VALID_VERDICTS.join('|')})`);
@@ -102,6 +104,16 @@ export function writeVerification(versionDir, verificationsDir, record) {
102
104
  errors.push(error.message);
103
105
  }
104
106
  }
107
+ if (intent?.quality_strategy === 'atelier' && record.verdict === 'passed') {
108
+ try {
109
+ atelierEvidence = validateAtelierRecord(versionDir, record.intent_id);
110
+ if (!['selected', 'baseline_retained'].includes(atelierEvidence.status)) {
111
+ errors.push(`quality_strategy=atelier 通过前,Atelier Record 必须是 selected 或 baseline_retained(当前: ${atelierEvidence.status})`);
112
+ }
113
+ } catch (error) {
114
+ errors.push(`quality_strategy=atelier 通过前必须有当前且合法的 Atelier Record: ${error.message}`);
115
+ }
116
+ }
105
117
  if (errors.length > 0) {
106
118
  throw new Error(`验证记录校验失败:\n - ${errors.join('\n - ')}`);
107
119
  }
@@ -144,7 +156,12 @@ export function writeVerification(versionDir, verificationsDir, record) {
144
156
  verdict: record.verdict,
145
157
  timestamp: record.timestamp,
146
158
  summary: record.summary,
147
- dimensions: record.dimensions,
159
+ dimensions: record.dimensions,
160
+ atelier: atelierEvidence ? {
161
+ record_ref: `09_ATELIER/${record.intent_id}.json`,
162
+ stance_revision: atelierEvidence.stance_revision,
163
+ status: atelierEvidence.status,
164
+ } : undefined,
148
165
  reproduction_command: record.reproduction_command,
149
166
  deviation_detail: record.deviation_detail,
150
167
  reset_suggested: record.reset_suggested,
@@ -0,0 +1,45 @@
1
+ # Authorship — Identity Compiler 与 Atelier Method
2
+
3
+ 本维度只在 `quality_strategy=atelier` 时加载。目标不是扮演某位大师,而是迫使本次创作
4
+ 形成可反驳的命题、明确的选择与可观察的作品差异。
5
+
6
+ ## Identity Compiler
7
+
8
+ 先读取当前 Intent、Doctrine anchors、Capability Graph / Brief、quality contract、
9
+ creative scope、真实媒介约束与参考机制,再形成 Authorial Stance:
10
+
11
+ 1. `creative_thesis`:作品要让用户以什么不同方式理解或感受问题。
12
+ 2. `gaze`:这次优先看见什么。
13
+ 3. `tension`:哪两个价值必须同时成立。
14
+ 4. `signature_bet`:主张、实现机制与主要代价。
15
+ 5. `refusals`:拒绝哪些安全但平庸的默认解。
16
+ 6. `medium_grammar`:构图、节奏、动效、材质、语言或声音如何承载命题。
17
+ 7. `surprise_budget`:允许陌生到什么程度,哪些边界不可牺牲。
18
+ 8. `anti_fixation`:至少一个主动打破首个构想的约束。
19
+ 9. `verification_lens`:不看阐述时,怎样从作品与用户行为判断命题成立。
20
+
21
+ 如果这些内容不会改变任何构图、交互、资产、语言或验证动作,Stance 无效。
22
+
23
+ ## Atelier
24
+
25
+ 1. 在修改前冻结真实基线。
26
+ 2. 定义至少两个会改变用户体验机制的差异轴。
27
+ 3. 独立产生媒介原型;换色、换皮、同义改写不算不同候选。
28
+ 4. 每个候选先过 Reliability Floor,再进入质量比较。
29
+ 5. 交换顺序或隐藏来源进行比较;没有候选胜过基线时保留基线。
30
+ 6. 完整实现胜出机制,观察真实宿主并修正。
31
+
32
+ 唯一记录位于 `.loom/vN/09_ATELIER/<intent-id>.json`。每个候选必须绑定
33
+ `stance_revision`;Stance 改变后,旧候选要重新资格检查或归档。
34
+
35
+ ## Correction Triage
36
+
37
+ - 当前命题、机制、媒介语法或候选选择失效:写 `corrections[]`,递增
38
+ `stance_revision`。
39
+ - 新用户结果、约束、能力缺口、风险或项目证据:提交带 provenance 的 Capability Graph
40
+ proposal,由 Architect 裁决。
41
+ - Intent、契约或 Doctrine 错误:按 LOOM reflow 回到对应上层。
42
+ - 多个任务经 Quality Proof 重复验证的方法:作为 learning candidate,人工晋升为 Skill;
43
+ 只有跨 Intent 的长期创作判断才考虑 Creative Lineage。
44
+
45
+ Author 不能修改 Graph、Intent 或验收标准,也不能裁决自己的 proposal。
@@ -3,7 +3,7 @@
3
3
  Intent Loop 将一个产品意图变成可验证结果,并在证据不足时回流到真正负责的层。
4
4
 
5
5
  ```text
6
- Doctrine → Intent → Contract
6
+ Doctrine → Intent narrative Capability Graph → Contract
7
7
  → Expertise Compiler → Quality Arena → Quality Proof
8
8
  → Close or Reflow
9
9
  ```
@@ -14,8 +14,8 @@ Doctrine → Intent → Contract
14
14
  |---|---|
15
15
  | 长期价值、卓越标准、反模式 | Weaver |
16
16
  | 产品目标、非目标、Intent narrative | Visionary |
17
- | 系统边界、Intent DAG、完成/质量契约 | Architect |
18
- | Expertise Pack、候选、实现、自测 | Forge |
17
+ | Capability Graph、系统边界、Intent DAG、完成/质量契约 | Architect |
18
+ | Expertise Pack、Authorial Stance、Atelier 候选、实现、自测 | Forge |
19
19
  | 独立判定、Quality Proof | Keeper |
20
20
 
21
21
  Keeper 不修改契约;Forge 不以实现困难改写 Intent;Visionary 不写 acceptance;Weaver 不拆实施模块。
@@ -38,11 +38,22 @@ Keeper 不修改契约;Forge 不以实现困难改写 Intent;Visionary 不
38
38
  - `quality_contract`:相对基线可观察的质量主张与最小有意义差异。
39
39
  - `capability_needs`:任务需要的专业认知、工具或审美能力。
40
40
  - `creative_scope`:允许探索与不得改变的边界。
41
+ - `quality_strategy`:`adaptive | atelier`,缺失等价于 `adaptive`;Atelier 只用于明确需要作者命题、媒介原型与独立候选比较的结果。
41
42
  - `verification_method`:可复现验证方法。
42
43
 
43
44
  `acceptance` 是 Reliability Floor;`quality_contract` 是 Distinctive Ceiling。二者不能合并成一串模糊
44
45
  “高质量要求”,否则完成与卓越都无法诚实判定。
45
46
 
47
+ ### 2.1 Capability Graph Gate
48
+
49
+ Capability Graph 在 Vision 与 Intent Map 之间展开:`outcome`、`concern`、`capability`、`risk`、`evidence` 节点及其关系。它不是执行 DAG;未知、调研和分叉留在 Graph,只有边界清楚、可独立验收的结果才进入 Intent。
50
+
51
+ - 所有高影响节点必须路由为 `expand`、`brief`、`intent`、`defer`、`exclude` 或 `covered_by`,不能停留在 `open`。
52
+ - 每个高影响 `outcome` 必须以 `validated_by` 连接到一个有验证计划的 `evidence` 节点。该计划至少声明:结果在何处被观察(`target`)、怎么复现(`procedure`)、什么算通过(`pass_criteria`)、留下什么证据(`artifact`)和由哪个 Intent 产出它。`artifact` 必须是当前版本内 `verifications/` 或 `08_ASSET_LIBRARY/files/` 下真实存在的普通文件;接口可用、文件存在于版本外或 URL 可访问都不能替代目标宿主、用户界面、外部接收方或交付物中的实际可观察结果。
53
+ - 每个当前 Intent 必须由至少一个 Graph 节点的 `intent_refs` 回链;Graph 是这份关联的唯一真相源,避免双写漂移。
54
+ - 需要专业方法、外部知识、研究或即将进入当前 Intent 的能力节点,才使用 `.loom/vN/07_CAPABILITY_BRIEFS/<node-id>.md` 写项目化 Brief。
55
+ - `loom capability coverage` 是 Architect 完成图谱后的门;`loom capability compile <id>` 是 Forge 的只读编译入口。Forge 发现新的缺口必须回流 Architect,不能把猜测静默变成实现范围。
56
+
46
57
  ## 3. 状态与 revision
47
58
 
48
59
  状态:
@@ -68,7 +79,7 @@ completed → needs_review → in_progress
68
79
  3. Hard Invariants
69
80
  4. Success Contracts
70
81
  5. Project Judgment
71
- 6. Expertise Inputs
82
+ 6. Expertise Inputs(含当前 Intent 编译得到的 Capability Graph 节点与 Brief)
72
83
  7. Working Facts
73
84
  8. Role Contract / Output / Reflow / Stop
74
85
 
@@ -83,6 +94,7 @@ loom intent update <id> --status in_progress
83
94
  ```
84
95
 
85
96
  只选择 pending、所有依赖 completed、未弃用的 Intent。一次 Forge 作用域只包含一个当前 Intent。
97
+ 进入选择前,Graph coverage 必须没有未路由的高影响节点、无计划能力节点和未映射 Intent。
86
98
 
87
99
  ## 6. Expertise Compiler
88
100
 
@@ -90,10 +102,34 @@ Forge 在实现前形成临时 Expertise Pack:
90
102
 
91
103
  - **Domain**:领域机制、失败边界和项目事实。
92
104
  - **Taste**:什么区分普通、可靠和出众。
105
+ - **Author**:这次提出什么可反驳的创作命题,选择什么并拒绝什么。
93
106
  - **Critic**:最可能出现的平庸方案、自我欺骗与反例。
94
107
  - **Verifier**:如何观察、比较和复现。
95
108
 
96
- 这四项是认知功能,不是必须创建四个角色或四份文档。
109
+ 这五项是认知功能,不是必须创建五个角色或五份文档。
110
+
111
+ Capability Graph 先提供当前 Intent 相关的项目事实、风险、约束和 Capability Brief;Expertise Compiler 再按 Brief 的获取计划加载真实技能、工具或资料。它不把整张图或历史会话当成当前任务上下文。
112
+
113
+ `quality_strategy=atelier` 时运行 Identity Compiler:将项目判断编译为可执行的 Authorial
114
+ Stance,而不是模仿名人的 Persona。Forge 在 `.loom/vN/09_ATELIER/<intent-id>.json`
115
+ 保存唯一 Atelier Record;普通 Intent 不创建该文件。
116
+
117
+ ### 2.2 Graph Change Proposal Gate
118
+
119
+ 新用户要求是 `outcome` 或 `constraint` 候选;论文、资料与运行发现是带 provenance 的 `capability`、`risk` 或 `evidence` 候选。它们先写入 `.loom/vN/07_GRAPH_PROPOSALS/CGP-*.json`,必须记录来源、观察时间、具体证据、为什么现在需要处理。Proposal 不是正式 Graph,Forge/Keeper 不得借它静默扩大当前 Intent。
120
+
121
+ Architect 必须把每个 proposal 判定为:已覆盖、Graph 更新、Intent 变更、acceptance 变更、Minor、Major 或拒绝;关闭时必须提交与决策相符的结构化 resolution,CLI 会从决策时磁盘基线验证 Graph / Intent / acceptance / 决策记录的真实变化或现有有效覆盖,不能以任意 implementation_ref 文本关闭。`constraint` 若决定为 Graph 更新,必须进入正式 Graph 的 `constraints` 字段并回链受影响节点。`covered_by` 必须显式指向另一个已覆盖、非 `covered_by` 路由的节点,并同时保留同目标的关系。`loom guide` 与 `loom doctor` 对未闭合 proposal 回流 Architect。
122
+
123
+ Author 的自我更正不得绕过该门:局部命题、机制、媒介语法或候选选择变化写入 Atelier
124
+ Record `corrections[]` 并递增 `stance_revision`;只有新的用户结果、约束、能力缺口、风险
125
+ 或项目证据才提交 Graph proposal。Architect 裁决并修订磁盘真相源后,Capability compile
126
+ 把新输入交回 Author。Author 不得裁决自己的 proposal,也不得修改考纲后自证通过。
127
+
128
+ ### 2.3 Asset Library Protocol
129
+
130
+ 若项目使用图片、音频、视频、模型或其他交付素材,`.loom/vN/08_ASSET_LIBRARY/manifest.json` 与同目录 `files/` 是版本化的一等真相源。每条资产必须有内容派生稳定 ID、kind、中文/其他标签、来源/作者/许可、SHA-256、库内相对路径、status 与 approval。`loom asset import` 只接受明确的本地普通文件、复制后校验哈希,并拒绝路径逃逸、重复字节和未批准/缺少许可元数据。
131
+
132
+ 素材字节能下载不等于素材可呈现;远程 URL 不是呈现证据。资产若用于 Capability Graph 的 evidence,资产 `evidence_refs` 与 evidence 节点 `asset_refs` 必须双向一致,Keeper 仍需在目标宿主验证实际呈现。
97
133
 
98
134
  技能、工具和资料必须经历:
99
135
 
@@ -123,6 +159,16 @@ Discover → Load → Translate → Use
123
159
 
124
160
  候选不强制落盘,不设置固定数量。没有候选胜过基线时,保留原方案或回流契约。
125
161
 
162
+ ### Atelier Path
163
+
164
+ 当 `quality_strategy=atelier` 时,Arena 增加明确作者命题与落盘证据:
165
+
166
+ 1. 编译 Authorial Stance 并冻结修改前基线。
167
+ 2. 定义质量差异轴,独立形成机制不同的媒介原型。
168
+ 3. 候选先过 Reliability Floor,再匿名比较或保留基线。
169
+ 4. 每个候选绑定 `stance_revision`;Stance 改变后重新资格检查或归档旧候选。
170
+ 5. 选择证据、主要代价和 corrections 写入唯一 Atelier Record,再进入完整实现。
171
+
126
172
  ## 8. Independent Quality Proof
127
173
 
128
174
  Keeper 在独立任务中只加载当前 revision、真实产物、契约、Doctrine 和必要验证工具。不要加载 Forge 的
@@ -210,6 +256,7 @@ Codex 的 goal/status 用于驱动循环与恢复工作,不是替代上述证
210
256
  | 长期价值或质量观缺失 | Weaver |
211
257
  | 产品目标、非目标或 narrative 错误 | Visionary |
212
258
  | 系统边界、依赖、契约不可成立 | Architect |
259
+ | 图谱分支遗漏、能力缺口或高影响节点未路由 | Architect 更新 Capability Graph |
213
260
  | 专业能力、候选或实现不足 | Forge |
214
261
  | 证据不足、验证偏差或需人类感知 | Keeper |
215
262
 
@@ -3,7 +3,7 @@
3
3
  角色是决策权边界,不是人格表演。LOOM 的执行链是:
4
4
 
5
5
  ```text
6
- Doctrine → Intent → Contract → Expertise Compiler
6
+ Doctrine → Intent narrative Capability Graph → Contract → Expertise Compiler
7
7
  → Quality Arena → Quality Proof → Reflow
8
8
  ```
9
9
 
@@ -18,11 +18,11 @@ Doctrine → Intent → Contract → Expertise Compiler
18
18
  3. Hard Invariants:BASELINE 摘要与命中的项目底线。
19
19
  4. Success Contracts:acceptance、按需的 continuity_required / quality_contract、verification_method;其中状态守恒规则仍只写在 acceptance。
20
20
  5. Project Judgment:相关 Doctrine anchors 与决策记录。
21
- 6. Expertise Inputscapability_needs、可发现的 Skill / 工具 / 资产入口和获取边界。
21
+ 6. Expertise Inputs:当前 Intent 编译得到的 Capability Graph 节点与 Brief、`capability_needs`、`quality_strategy`、可发现的 Skill / 工具 / 资产入口和获取边界;仅当 `quality_strategy=atelier` 时注入 Authorship Method 与 Atelier Record 要求。
22
22
  7. Working Facts:相关架构、代码、资产、基线和产物路径。
23
23
  8. Output / Reflow / Stop:交付、证据、回流与停止条件。
24
24
 
25
- Context Pack 编译器只选择事实和能力入口。Expertise Compiler 在角色激活后检查真实环境,
25
+ Context Pack 编译器只选择事实和能力入口。Capability Graph 保留项目问题面、能力缺口、风险、证据与 Intent 回链;它不是任务列表,只有当前 Intent 关联的节点和 Brief 会进入 Context Pack。Expertise Compiler 在角色激活后检查真实环境,
26
26
  再形成 Expertise Pack;看见 Skill 名称不等于已经加载能力。
27
27
 
28
28
  Context Pack 不会清除 Agent 既有记忆。发生冲突时,以 system、developer 和用户指令
@@ -42,14 +42,18 @@ Context Pack 不会清除 Agent 既有记忆。发生冲突时,以 system、de
42
42
 
43
43
  ### Architect
44
44
 
45
- - 拥有系统边界、Intent DAG、公共契约、质量契约和验证入口。
46
- - 声明 capability_needs 与 creative_scope。
45
+ - 拥有 Capability Graph、系统边界、Intent DAG、公共契约、质量契约和验证入口。
46
+ - 先路由高影响图谱节点并保证每个 Intent 回链,再声明 capability_needs 与 creative_scope。
47
47
  - 不实现,也不给出通过结论。
48
48
 
49
49
  ### Forge
50
50
 
51
51
  - 为当前 Intent 编译 Expertise Pack,运行 Quality Arena 并完成实现和自测。
52
52
  - 可以做必要局部设计、错误处理和可逆探索。
53
+ - `quality_strategy=atelier` 时增加 Author 认知职能:用 Identity Compiler 形成可反驳的
54
+ Authorial Stance,并将基线、候选、修正和选择写入唯一 Atelier Record。
55
+ - 局部创作假设更正只递增 `stance_revision`;结构性新发现提交 Capability Graph proposal,
56
+ 由 Architect 裁决后再经 Capability compile 进入下一版 Stance。
53
57
  - 不改变上层目标、公共契约或架构边界。
54
58
 
55
59
  ### Keeper
@@ -62,7 +66,8 @@ Context Pack 不会清除 Agent 既有记忆。发生冲突时,以 system、de
62
66
  ### Expertise Compiler
63
67
 
64
68
  按任务组合项目事实、Skill、工具、资产、参考、质量机制、失败模型与验证手段,输出临时
65
- Expertise Pack。Domain、Taste、Critic、Verifier 是按需认知职能,不是新增角色。
69
+ Expertise Pack。Domain、Taste、Author、Critic、Verifier 是按需认知职能,不是新增角色。
70
+ Author 只在需要形成创作命题时启用,不是常驻 Persona。
66
71
 
67
72
  ### Quality Arena
68
73
 
@@ -79,6 +84,7 @@ Reliability Floor,质量契约是 Distinctive Ceiling。没有候选胜过基
79
84
 
80
85
  - 实现错误、遗漏或局部质量不足 → Forge。
81
86
  - acceptance、verification_method、依赖或架构错误 → Architect。
87
+ - 项目问题面遗漏、能力缺口或图谱回链缺失 → Architect 更新 Capability Graph。
82
88
  - 目标、非目标或 narrative 错误 → Visionary。
83
89
  - 长期项目原则持续失效 → Weaver / 新版本。
84
90
  - 缺少外部授权或主观裁决 → 人类。