@haaaiawd/loom 1.2.0 → 1.2.2

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 CHANGED
@@ -1,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.2.2 — 2026-07-31
4
+
5
+ ### Fixed
6
+
7
+ - `guide` 只输出下一步命令、Context Pack 提示与完成校验,不再展示文件输入/输出清单;当下一步是角色激活时,由该命令装配 Context Pack。
8
+ - `activate weaver` 直接注入检索方法与哲学维度目录;`activate architect` 在无 Intent 的设计阶段直接注入愿景、当前 Capability Graph、Intent Map 与未闭合 proposal。
9
+ - Capability Graph 未闭合或 proposal 待裁决时,`guide` 重新指向可推进工作的 Architect Context Pack,而不是重复运行只读检查。
10
+ - `loom intent next` 返回开始该 Intent 和获取 Forge Context Pack 的明确后续命令。
11
+
12
+ ## 1.2.1 — 2026-07-31
13
+
14
+ ### Fixed
15
+
16
+ - `verify pass` 现在要求声明独立 Keeper 或人类复核来源,并将该来源写入验证记录;普通自检不能再被快捷命令包装成 `passed`。
17
+ - 高影响 capability 若选择 `adaptive`,必须留下为何不启用外部获取强门的理由;无理由的 `adaptive` 不再是静默绕过路径。
18
+ - Intent 模板不再默认引用不存在的质量契约章节,并增加叙事语义守恒与反例验证的提示。
19
+
20
+ ### Compatibility
21
+
22
+ - 旧版结构化验证记录仍可读取;新的 passed 记录(包括 `verify write`)必须声明验证来源。旧记录可供历史追溯,但不自动获得独立验证声明。
23
+
3
24
  ## 1.2.0 — 2026-07-31
4
25
 
5
26
  ### Added
package/README.md CHANGED
@@ -87,11 +87,14 @@ loom guide --dry-run # 只读诊断,不写 heartbeat
87
87
  guide 检测项目当前在哪个阶段,输出"你在阶段 X,下一步做 Y"。
88
88
  Agent 每完成一步都跑 guide 确认下一步。
89
89
  如果只是审计或探测,不希望产生任何状态写入,用 `loom guide --dry-run`。
90
+
91
+ 当 `guide` 的下一步是 `loom activate <role>` 时,先运行这条命令:它会把该阶段需要的
92
+ 规范与版本化输入编译为 Context Pack。`guide` 只负责导航,不输出待阅读文件清单。
90
93
 
91
94
  ### AUTO 模式
92
95
 
93
96
  ```bash
94
- loom auto on # Agent 自动连续执行,不等确认
97
+ loom auto on # Agent 在允许阶段连续推进;不会跳过契约、证据或 Keeper 门禁
95
98
  loom auto off # 每步需要用户确认
96
99
  ```
97
100
 
package/cli/bin/loom.js CHANGED
@@ -274,9 +274,19 @@ try {
274
274
  }));
275
275
  break;
276
276
  }
277
- case 'next':
278
- output(getNextIntent(versionDir) ?? '没有可执行的 Intent');
279
- break;
277
+ case 'next':
278
+ {
279
+ const next = getNextIntent(versionDir);
280
+ output(next ? {
281
+ ...next,
282
+ suggested_flow: {
283
+ start: `loom intent update ${next.id} --status in_progress`,
284
+ forge_context: `loom activate forge --intent ${next.id}`,
285
+ note: '先显式开始 Intent,再由 activate 命令装配该 Intent 的叙事、契约、图谱路由与专业输入。',
286
+ },
287
+ } : '没有可执行的 Intent');
288
+ }
289
+ break;
280
290
  case 'status': {
281
291
  const s = getStatus(versionDir);
282
292
  const fmt = (ids) => ids.map((id) => s.titles[id] ? `${id}(${s.titles[id]})` : id).join(', ') || '-';
@@ -518,7 +528,7 @@ try {
518
528
  const icon = issue.severity === 'high' ? '⚠' : '·';
519
529
  console.log(` ${icon} [${issue.severity}] ${issue.msg}`);
520
530
  }
521
- console.log('\n参见 meta/PHILOSOPHY_WEAVER.md + dimensions/SEARCH_METHODOLOGY.md。');
531
+ console.log('\n下一步: 运行 loom activate weaver;命令会重新输出哲学阶段所需的 Context Pack。');
522
532
  exit(1);
523
533
  }
524
534
  break;
@@ -592,7 +602,7 @@ try {
592
602
  }
593
603
  case 'pass':
594
604
  case 'fail': {
595
- // loom verify pass <id> --summary "..." [--reproduction-command "..."] [--preservation-evidence "..."] [--quality-proof "..."]
605
+ // loom verify pass <id> --summary "..." --verified-by <id> --verification-context <independent_thread|human_review>
596
606
  // loom verify fail <id> --summary "..." [--deviation "..."] [--reproduction-command "..."]
597
607
  const id = rest[0];
598
608
  if (!id) die(`用法: loom verify ${sub} <id> --summary "..." [--reproduction-command "..."]${sub === 'pass' ? ' [--preservation-evidence "..."] [--quality-proof "..."]' : ' [--deviation "..."]'}`);
@@ -601,6 +611,8 @@ try {
601
611
  const deviationIdx = argv.indexOf('--deviation');
602
612
  const qualityProofIdx = argv.indexOf('--quality-proof');
603
613
  const preservationIdx = argv.indexOf('--preservation-evidence');
614
+ const verifiedByIdx = argv.indexOf('--verified-by');
615
+ const verificationContextIdx = argv.indexOf('--verification-context');
604
616
  const summary = summaryIdx !== -1 ? argv[summaryIdx + 1] : null;
605
617
  if (!summary) die(`缺少 --summary: loom verify ${sub} ${id} --summary "..."`);
606
618
  const intent = getIntent(versionDir, id);
@@ -610,11 +622,15 @@ try {
610
622
  if (sub === 'pass' && intent.quality_contract && !(qualityProofIdx !== -1 && argv[qualityProofIdx + 1])) {
611
623
  die(`Intent ${id} 声明了 quality_contract;通过前必须提供 --quality-proof,指向项目内真实的 Quality Proof Markdown 锚点。`);
612
624
  }
625
+ if (sub === 'pass' && !(verifiedByIdx !== -1 && argv[verifiedByIdx + 1] && verificationContextIdx !== -1 && argv[verificationContextIdx + 1])) {
626
+ die(`Intent ${id} 通过前必须声明独立验证来源:--verified-by <thread/run/人类标识> --verification-context <independent_thread|human_review>。同一会话自检请记录为自检,不得写 passed。`);
627
+ }
613
628
  const extras = {};
614
629
  if (reproIdx !== -1 && argv[reproIdx + 1]) extras.reproduction_command = argv[reproIdx + 1];
615
630
  if (sub === 'fail' && deviationIdx !== -1 && argv[deviationIdx + 1]) extras.deviation_detail = argv[deviationIdx + 1];
616
631
  if (sub === 'pass' && qualityProofIdx !== -1 && argv[qualityProofIdx + 1]) extras.quality_proof_ref = argv[qualityProofIdx + 1];
617
632
  if (sub === 'pass' && preservationIdx !== -1 && argv[preservationIdx + 1]) extras.preservation_evidence = argv[preservationIdx + 1];
633
+ if (sub === 'pass') extras.verification_provenance = { verified_by: argv[verifiedByIdx + 1], context: argv[verificationContextIdx + 1] };
618
634
  const verdict = sub === 'pass' ? 'passed' : 'deviated';
619
635
  const result = createQuickVerification(versionDir, verificationsDir, id, verdict, summary, extras);
620
636
  console.log(`验证记录已写入: ${result.filePath}`);
@@ -732,7 +748,7 @@ try {
732
748
  console.log(` → 修复: ${issue.fix_hint}`);
733
749
  }
734
750
  }
735
- console.log(`\n参见 meta/PHILOSOPHY_WEAVER.md + dimensions/SEARCH_METHODOLOGY.md。`);
751
+ console.log('\n下一步: 运行 loom guide;它会根据当前状态给出可执行动作与所需 Context Pack。');
736
752
  }
737
753
  break;
738
754
  }
@@ -782,17 +798,12 @@ try {
782
798
  };
783
799
  console.log(`模式: ${modeDesc[result.auto_mode] || result.auto_mode}`);
784
800
  console.log(`\n${result.message}`);
785
- console.log(`\n下一步: ${result.next_action}`);
786
- console.log(` → ${result.next_command}`);
787
- if (result.inputs && result.inputs.length > 0) {
788
- console.log(`\n需要读取:`);
789
- for (const f of result.inputs) console.log(` - ${f}`);
790
- }
791
- if (result.outputs && result.outputs.length > 0) {
792
- console.log(`\n需要产出:`);
793
- for (const f of result.outputs) console.log(` - ${f}`);
794
- }
795
- if (result.verify_command) {
801
+ console.log(`\n下一步: ${result.next_action}`);
802
+ console.log(` → ${result.next_command}`);
803
+ if (result.input_delivery === 'context_pack') {
804
+ console.log('\n上下文: 直接运行这条命令;它会输出当前阶段所需的 Context Pack。');
805
+ }
806
+ if (result.verify_command) {
796
807
  console.log(`\n完成后校验: ${result.verify_command}`);
797
808
  }
798
809
  if (result.auto_mode === 'manual' && result.stage_num > 0 && result.stage_num < 6) {
@@ -49,7 +49,9 @@ loom philosophy check
49
49
  快速命令:
50
50
 
51
51
  ```bash
52
- loom verify pass <id> --summary "<证据>" --quality-proof "<ref>"
52
+ loom verify pass <id> --summary "<证据>" --quality-proof "<ref>" \
53
+ --verified-by "<keeper-thread-or-human>" \
54
+ --verification-context independent_thread
53
55
  ```
54
56
 
55
57
  若只达到完成契约,写 `deviated` 或完整验证记录,不要伪造质量通过。
package/cli/help/loop.md CHANGED
@@ -28,7 +28,7 @@ Forge 先形成任务级 Expertise Pack:
28
28
  - 已实际加载的技能、资料、工具及其用途。
29
29
  - Critic 视角与验证方法。
30
30
 
31
- Pack 是任务级认知配置,不写成新的长期规范。External Acquisition Gate required 时,Pack
31
+ Pack 是任务级认知配置,不写成新的长期规范。高影响 capability 默认进入 External Acquisition Gate;若明确选择 `adaptive`,也必须写明为何此处不启用外部获取。Pack
32
32
  必须落盘到 `10_EXPERTISE_PACKS/<intent-id>.json`:Search Plan 可由 AI 派生,但 Capsule 内容
33
33
  必须来自实际打开的外部来源,并写出判断门、失败模式和验证信号。明显的机械任务可以保持
34
34
  `adaptive`;高质量任务必须足以解释为什么某个专业手法适合这个项目。
@@ -60,6 +60,10 @@ Keeper 独立检查基础四维;有质量契约时增加第五维:
60
60
  "verdict": "passed",
61
61
  "timestamp": "2026-07-28T12:00:00.000Z",
62
62
  "summary": "具体、可定位、可复现的判定摘要",
63
+ "verification_provenance": {
64
+ "verified_by": "keeper thread 或人类复核标识",
65
+ "context": "independent_thread"
66
+ },
63
67
  "reproduction_command": "npm test",
64
68
  "dimensions": {
65
69
  "intent_fidelity": {
@@ -87,6 +91,14 @@ Keeper 独立检查基础四维;有质量契约时增加第五维:
87
91
  }
88
92
  ```
89
93
 
94
+ 快捷写入 `passed` 时,也必须声明这一来源:
95
+
96
+ ```bash
97
+ loom verify pass INT-001 --summary "..." \
98
+ --verified-by "keeper-run-123" \
99
+ --verification-context independent_thread
100
+ ```
101
+
90
102
  若 Intent 声明 `continuity_required: true`,Keeper 还必须写入并通过:
91
103
 
92
104
  ```json
@@ -9,6 +9,9 @@ loom context
9
9
 
10
10
  只读探测使用 `loom guide --dry-run`。
11
11
 
12
+ 若 `guide` 指向 `loom activate <role>`,直接运行该命令。它会输出已经装配的 Context Pack;
13
+ `guide` 只负责给出当前动作,不要求 Agent 逐个寻找文件。
14
+
12
15
  ## 1. Doctrine — Weaver
13
16
 
14
17
  ```bash
@@ -1,7 +1,7 @@
1
1
  // activate — compile a role- and intent-scoped Context Pack.
2
2
 
3
- import { readFileSync, existsSync } from 'node:fs';
4
- import { join } from 'node:path';
3
+ import { readFileSync, existsSync, readdirSync } from 'node:fs';
4
+ import { join, relative } from 'node:path';
5
5
  import { getLoomRoot } from './shared/paths.js';
6
6
  import { getIntent, getNarrative } from './intent-map.js';
7
7
  import { getIntentDraft } from './intent-draft.js';
@@ -11,6 +11,7 @@ import { extractMdSection } from './shared/md-utils.js';
11
11
  import { compileCapabilityInputs } from './capability-graph.js';
12
12
  import { getAtelierRecord } from './atelier.js';
13
13
  import { formatExpertisePackForPrompt, getExpertisePack, getExpertisePackState } from './expertise-pack.js';
14
+ import { listCapabilityProposals } from './capability-proposals.js';
14
15
 
15
16
  const VALID_ROLES = ['weaver', 'visionary', 'architect', 'forge', 'keeper'];
16
17
 
@@ -354,6 +355,92 @@ function compileWorkingFacts(versionDir, objective) {
354
355
  return refs.join('\n');
355
356
  }
356
357
 
358
+ function readStageFile(path, label) {
359
+ if (!existsSync(path)) return `> ${label} 当前不存在;不要猜测其内容。按角色契约创建或回流。`;
360
+ return readFileSync(path, 'utf-8').trim();
361
+ }
362
+
363
+ function listMarkdownFiles(root, current = root) {
364
+ if (!existsSync(current)) return [];
365
+ return readdirSync(current, { withFileTypes: true })
366
+ .sort((a, b) => a.name.localeCompare(b.name))
367
+ .flatMap((entry) => {
368
+ const path = join(current, entry.name);
369
+ if (entry.isDirectory()) return listMarkdownFiles(root, path);
370
+ return entry.isFile() && entry.name.endsWith('.md')
371
+ ? [relative(root, path).replaceAll('\\', '/')]
372
+ : [];
373
+ });
374
+ }
375
+
376
+ /**
377
+ * Compile the stage inputs that used to be merely named by `loom guide`.
378
+ * These are intentionally separate from Working Facts: they are the source
379
+ * material for a role's immediate decision, not a request to hunt for paths.
380
+ */
381
+ function compileStageInputs(role, versionDir, objective) {
382
+ if (role === 'weaver') {
383
+ const dimensionsRoot = join(getLoomRoot(), 'dimensions');
384
+ const catalog = listMarkdownFiles(dimensionsRoot)
385
+ .filter((path) => path !== 'SEARCH_METHODOLOGY.md')
386
+ .map((path) => `- dimensions/${path}`);
387
+ return [
388
+ '本节由 `loom activate weaver` 直接装配。不要为了取得这些输入而搜索 CLI 安装目录。',
389
+ '',
390
+ '### Search Methodology',
391
+ '',
392
+ readStageFile(join(dimensionsRoot, 'SEARCH_METHODOLOGY.md'), 'SEARCH_METHODOLOGY.md'),
393
+ '',
394
+ '### Available Dimension Catalog',
395
+ '',
396
+ catalog.length
397
+ ? `${catalog.join('\n')}\n\n只打开会实质改变当前项目原则或取舍的维度;目录本身不是要求逐个阅读。`
398
+ : '> 当前没有可用的 dimensions 文档。',
399
+ ].join('\n');
400
+ }
401
+
402
+ if (role === 'architect' && versionDir && !objective.intent && !objective.draft) {
403
+ let proposals = [];
404
+ let proposalsError = null;
405
+ try {
406
+ proposals = listCapabilityProposals(versionDir, { unresolvedOnly: true });
407
+ } catch (error) {
408
+ proposalsError = error.message;
409
+ }
410
+ return [
411
+ '本节由 `loom activate architect` 直接装配。以下是当前设计决策所需的版本化输入,不要先手动查找路径。',
412
+ '',
413
+ '### Vision Input (01_VISION.md)',
414
+ '',
415
+ readStageFile(join(versionDir, '01_VISION.md'), '01_VISION.md'),
416
+ '',
417
+ '### Capability Graph Input (07_CAPABILITY_GRAPH.json)',
418
+ '',
419
+ readStageFile(join(versionDir, '07_CAPABILITY_GRAPH.json'), '07_CAPABILITY_GRAPH.json'),
420
+ '',
421
+ '### Intent Map Input (04_INTENT_MAP.json)',
422
+ '',
423
+ readStageFile(join(versionDir, '04_INTENT_MAP.json'), '04_INTENT_MAP.json'),
424
+ '',
425
+ '### Open Capability Graph Proposals',
426
+ '',
427
+ proposalsError
428
+ ? `> Proposal 记录无法读取:${proposalsError}。先修复这份记录,不要猜测其状态。`
429
+ : proposals.length
430
+ ? `\`\`\`json\n${JSON.stringify(proposals, null, 2)}\n\`\`\``
431
+ : '无。',
432
+ ].join('\n');
433
+ }
434
+
435
+ if (role === 'visionary') {
436
+ return '产品哲学与决策准则已在上方 Project Judgment 中装配;不要重新搜索 `.loom` 目录。';
437
+ }
438
+ if (role === 'forge' || role === 'keeper') {
439
+ return '当前 Intent 的叙事、契约、图谱路由与已就绪的 Expertise 输入已在上方装配;只有产物本身需要按 Working Facts 检查。';
440
+ }
441
+ return '当前角色没有额外的阶段输入。';
442
+ }
443
+
357
444
  /**
358
445
  * Compile a role-scoped Context Pack.
359
446
  * @param {string} role
@@ -377,9 +464,10 @@ export function activateRole(role, versionDir, intentId = null) {
377
464
  section('3. Hard Invariants', compileInvariants(role, versionDir)),
378
465
  section('4. Success Contracts', compileContracts(role, versionDir, objective)),
379
466
  section('5. Project Judgment', compileProjectJudgment(role, versionDir, objective)),
380
- section('6. Expertise Inputs', compileExpertiseInputs(role, versionDir, objective)),
381
- section('7. Working Facts', compileWorkingFacts(versionDir, objective)),
382
- section('8. Role Contract / Output / Reflow / Stop', readRole(role)),
467
+ section('6. Stage Inputs (command-assembled)', compileStageInputs(role, versionDir, objective)),
468
+ section('7. Expertise Inputs', compileExpertiseInputs(role, versionDir, objective)),
469
+ section('8. Working Facts', compileWorkingFacts(versionDir, objective)),
470
+ section('9. Role Contract / Output / Reflow / Stop', readRole(role)),
383
471
  ];
384
472
  return `${parts.join('\n\n---\n\n')}\n`;
385
473
  }
@@ -78,6 +78,10 @@ function validateNode(id, node, allIds, errors) {
78
78
  && (typeof node.acquisition_rationale !== 'string' || !node.acquisition_rationale.trim())) {
79
79
  errors.push(`nodes["${id}"].acquisition_mode=project_only 必须声明 acquisition_rationale`);
80
80
  }
81
+ if (node.impact === 'high' && node.acquisition_mode === 'adaptive'
82
+ && (typeof node.acquisition_rationale !== 'string' || !node.acquisition_rationale.trim())) {
83
+ errors.push(`nodes["${id}"] 为高影响 capability 且选择 adaptive 时必须声明 acquisition_rationale;说明为何此处不启用 external_required`);
84
+ }
81
85
  if (node.verification !== undefined) {
82
86
  if (node.kind !== 'evidence') {
83
87
  errors.push(`nodes["${id}"].verification 只允许写在 evidence 节点`);
@@ -267,6 +271,7 @@ export function getCapabilityCoverage(versionDir) {
267
271
  const capabilitiesWithoutPlan = [];
268
272
  const routingGaps = [];
269
273
  const outcomesWithoutConcern = [];
274
+ const evidenceArtifactGaps = [];
270
275
 
271
276
  for (const node of nodes) {
272
277
  for (const intentId of node.intent_refs || []) {
@@ -305,6 +310,12 @@ export function getCapabilityCoverage(versionDir) {
305
310
  if (node.kind === 'outcome' && !(node.relationships || []).some((relation) => graph.nodes[relation.target]?.kind === 'concern')) {
306
311
  outcomesWithoutConcern.push({ node_id: node.id, reason: 'outcome 没有连接到 concern,项目初衷尚未展开为问题面' });
307
312
  }
313
+ if (node.kind === 'evidence' && node.verification?.artifact) {
314
+ const ownedCompletedIntent = (node.intent_refs || []).some((intentId) => intentMap.intents[intentId]?.status === 'completed');
315
+ if (ownedCompletedIntent && !resolveEvidenceArtifact(versionDir, node.verification.artifact)) {
316
+ evidenceArtifactGaps.push({ node_id: node.id, reason: `已完成 Intent 的 evidence artifact 不存在或不可读: ${node.verification.artifact}` });
317
+ }
318
+ }
308
319
  if (node.brief_ref) {
309
320
  try { resolveBrief(versionDir, node.brief_ref); } catch (error) {
310
321
  capabilitiesWithoutPlan.push({ node_id: node.id, reason: error.message });
@@ -326,6 +337,7 @@ export function getCapabilityCoverage(versionDir) {
326
337
  routing_gaps: routingGaps.length,
327
338
  outcomes_without_concern: outcomesWithoutConcern.length,
328
339
  high_outcomes_without_observable_evidence: highOutcomesWithoutObservableEvidence.length,
340
+ evidence_artifact_gaps: evidenceArtifactGaps.length,
329
341
  intent_mapping_required: intentMappingRequired,
330
342
  unmapped_intents: unmappedIntents.length,
331
343
  ready: highUnrouted.length === 0
@@ -334,6 +346,7 @@ export function getCapabilityCoverage(versionDir) {
334
346
  && routingGaps.length === 0
335
347
  && outcomesWithoutConcern.length === 0
336
348
  && highOutcomesWithoutObservableEvidence.length === 0
349
+ && evidenceArtifactGaps.length === 0
337
350
  && unmappedIntents.length === 0,
338
351
  },
339
352
  high_unrouted: highUnrouted,
@@ -342,6 +355,7 @@ export function getCapabilityCoverage(versionDir) {
342
355
  routing_gaps: routingGaps,
343
356
  outcomes_without_concern: outcomesWithoutConcern,
344
357
  high_outcomes_without_observable_evidence: highOutcomesWithoutObservableEvidence,
358
+ evidence_artifact_gaps: evidenceArtifactGaps,
345
359
  unmapped_intents: unmappedIntents,
346
360
  };
347
361
  }
@@ -274,6 +274,9 @@ export function doctor(versionDir, verificationsDir, philosophyDir) {
274
274
  for (const item of coverage.high_outcomes_without_observable_evidence) {
275
275
  issues.push({ id: item.node_id, type: 'capability_outcome_unobservable', severity: 'high', msg: `${item.node_id} 缺少真实呈现或交付的验证入口: ${item.reason}` });
276
276
  }
277
+ for (const item of coverage.evidence_artifact_gaps || []) {
278
+ issues.push({ id: item.node_id, type: 'capability_evidence_artifact_missing', severity: 'high', msg: `${item.node_id} 的完成证据不可用: ${item.reason}` });
279
+ }
277
280
  for (const item of coverage.orphan_intent_refs) {
278
281
  issues.push({ id: item.node_id, type: 'intent_graph_unmapped', severity: 'high', msg: `${item.node_id} 引用了不存在的 Intent: ${item.intent_id}` });
279
282
  }
package/cli/src/guide.js CHANGED
@@ -63,35 +63,41 @@ export function guideProject(projectDir, options = {}) {
63
63
  outputs: ['.loom/v1/'],
64
64
  verify_command: 'loom guide',
65
65
  },
66
- need_philosophy: {
66
+ need_philosophy: {
67
67
  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
- need_vision: {
72
- inputs: ['roles/visionary.md', `.loom/${current}/00_PHILOSOPHY/`],
73
- outputs: [`.loom/${current}/01_VISION.md`],
74
- verify_command: 'loom guide',
75
- },
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
+ input_delivery: 'context_pack',
71
+ },
72
+ need_vision: {
73
+ inputs: ['roles/visionary.md', `.loom/${current}/00_PHILOSOPHY/`],
74
+ outputs: [`.loom/${current}/01_VISION.md`],
75
+ verify_command: 'loom guide',
76
+ input_delivery: 'context_pack',
77
+ },
76
78
  need_architecture: {
77
79
  inputs: ['roles/architect.md', `.loom/${current}/01_VISION.md`],
78
80
  outputs: [`.loom/${current}/02_ARCHITECTURE.md`, `.loom/${current}/04_INTENT_MAP.json`],
79
81
  verify_command: 'loom doctor',
82
+ input_delivery: 'context_pack',
80
83
  },
81
84
  need_capability_graph: {
82
85
  inputs: ['roles/architect.md', `.loom/${current}/01_VISION.md`],
83
86
  outputs: [`.loom/${current}/07_CAPABILITY_GRAPH.json`, `.loom/${current}/07_CAPABILITY_BRIEFS/`],
84
87
  verify_command: 'loom capability coverage',
88
+ input_delivery: 'context_pack',
85
89
  },
86
90
  capability_graph_incomplete: {
87
91
  inputs: ['roles/architect.md', `.loom/${current}/07_CAPABILITY_GRAPH.json`, `.loom/${current}/04_INTENT_MAP.json`],
88
92
  outputs: [`.loom/${current}/07_CAPABILITY_GRAPH.json`, `.loom/${current}/07_CAPABILITY_BRIEFS/`],
89
93
  verify_command: 'loom capability coverage',
94
+ input_delivery: 'context_pack',
90
95
  },
91
96
  capability_graph_proposals_pending: {
92
97
  inputs: ['roles/architect.md', `.loom/${current}/07_GRAPH_PROPOSALS/`, `.loom/${current}/07_CAPABILITY_GRAPH.json`],
93
98
  outputs: [`.loom/${current}/07_GRAPH_PROPOSALS/`, `.loom/${current}/07_CAPABILITY_GRAPH.json`, `.loom/${current}/04_INTENT_MAP.json`],
94
99
  verify_command: 'loom capability proposal list',
100
+ input_delivery: 'context_pack',
95
101
  },
96
102
  intent_map_broken: {
97
103
  inputs: [`.loom/${current}/04_INTENT_MAP.json`],
@@ -120,9 +126,10 @@ export function guideProject(projectDir, options = {}) {
120
126
  },
121
127
  };
122
128
  const meta = stageMeta[result.stage] || {};
123
- result.inputs = meta.inputs || [];
124
- result.outputs = meta.outputs || [];
125
- result.verify_command = meta.verify_command || null;
129
+ result.inputs = meta.inputs || [];
130
+ result.outputs = meta.outputs || [];
131
+ result.verify_command = meta.verify_command || null;
132
+ result.input_delivery = meta.input_delivery || null;
126
133
  // 统一后处理:写心跳 + 加 AUTO 提示词 + 判断是否需要人类 review
127
134
  if (existsSync(loomRoot) && !options.dryRun) {
128
135
  try {
@@ -146,7 +153,7 @@ export function guideProject(projectDir, options = {}) {
146
153
  }
147
154
  } else if (result.stage_num >= 4) {
148
155
  if (auto) {
149
- result.message += '\n\n> AUTO 模式开启——直接执行 next_command,无需人类确认。';
156
+ result.message += '\n\n> AUTO 模式开启——可继续进入下一阶段;契约、证据与 Keeper 门禁仍不会被跳过。';
150
157
  } else {
151
158
  result.message += '\n\n> ⚠ AUTO 模式关闭——执行 next_command 后等人类确认再继续。';
152
159
  }
@@ -242,14 +249,14 @@ function diagnoseStage(cwd, loomRoot, auto) {
242
249
  details: { version: current, proposals: pendingProposals.map((proposal) => proposal.id) },
243
250
  auto,
244
251
  next_action: '审计新信息对 Capability Graph、Intent 和契约的影响',
245
- next_command: 'loom capability proposal list',
252
+ next_command: 'loom activate architect',
246
253
  message: `当前版本 ${current} 有 ${pendingProposals.length} 个未闭合的 Capability Graph proposal。它们是新要求、研究或实现发现,不得由 Forge 静默变成当前 Intent 范围。`,
247
254
  };
248
255
  }
249
256
  } catch (error) {
250
257
  return {
251
258
  stage: 'capability_graph_proposals_pending', stage_num: 3, details: { version: current, error: error.message }, auto,
252
- next_action: '修复 Capability Graph proposal', next_command: 'loom capability proposal list',
259
+ next_action: '修复 Capability Graph proposal', next_command: 'loom activate architect',
253
260
  message: `Capability Graph proposal 无法审计: ${error.message}`,
254
261
  };
255
262
  }
@@ -262,7 +269,7 @@ function diagnoseStage(cwd, loomRoot, auto) {
262
269
  details: { version: current, coverage: coverage.summary },
263
270
  auto,
264
271
  next_action: '补齐 Capability Graph 的路由、可观察验证入口与 Intent 回链',
265
- next_command: 'loom capability coverage',
272
+ next_command: 'loom activate architect',
266
273
  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。`,
267
274
  };
268
275
  }
@@ -273,7 +280,7 @@ function diagnoseStage(cwd, loomRoot, auto) {
273
280
  details: { version: current, error: error.message },
274
281
  auto,
275
282
  next_action: '修复 Capability Graph',
276
- next_command: 'loom capability coverage',
283
+ next_command: 'loom activate architect',
277
284
  message: `Capability Graph 无法通过校验: ${error.message}`,
278
285
  };
279
286
  }
package/cli/src/init.js CHANGED
@@ -169,6 +169,7 @@ export function initProject(projectDir) {
169
169
  '3. 只在当前角色的权限内行动;发现目标、契约或架构需要改变时,按 LOOM 回流,不要静默扩展范围。',
170
170
  '4. 完成前运行当前 Intent 的验证方法与 `loom doctor`;声称质量提升时必须提供基线相对 Quality Proof,以磁盘证据而非会话记忆判断状态。',
171
171
  '5. Keeper 验证必须运行在新的 Agent thread 中;同一会话切换角色不构成独立验证。',
172
+ '6. 协作节奏默认是手动;需要让 Agent 在已允许阶段连续推进时,显式运行 `loom auto on`。AUTO 不会跳过契约、证据或 Keeper 门禁。',
172
173
  '',
173
174
  '常用入口:',
174
175
  '- `loom --help`',
package/cli/src/verify.js CHANGED
@@ -11,7 +11,8 @@ import { validateAtelierRecord } from './atelier.js';
11
11
  import { assertExpertiseReady } from './expertise-pack.js';
12
12
 
13
13
  /** 合法判定结果 */
14
- const VALID_VERDICTS = ['passed', 'deviated', 'blocked', 'pending_human'];
14
+ const VALID_VERDICTS = ['passed', 'deviated', 'blocked', 'pending_human'];
15
+ const VALID_VERIFICATION_CONTEXTS = ['independent_thread', 'human_review'];
15
16
 
16
17
  /** 每个 Intent 都必须覆盖的基础验证维度。 */
17
18
  const BASE_DIMENSIONS = [
@@ -59,6 +60,15 @@ export function writeVerification(versionDir, verificationsDir, record) {
59
60
  errors.push(`Intent ${record.intent_id} 当前状态为 ${intent.status};只能为 in_progress 或 needs_review 的 Intent 写入验证记录`);
60
61
  }
61
62
  const requiredDimensions = getRequiredDimensions(intent);
63
+ if (record.verdict === 'passed') {
64
+ const provenance = record.verification_provenance;
65
+ if (!provenance || typeof provenance !== 'object') {
66
+ errors.push('passed 必须声明 verification_provenance(verified_by + context);实现者自检不能单独闭合 Intent');
67
+ } else {
68
+ if (typeof provenance.verified_by !== 'string' || !provenance.verified_by.trim()) errors.push('verification_provenance.verified_by 必须是非空验证者标识');
69
+ if (!VALID_VERIFICATION_CONTEXTS.includes(provenance.context)) errors.push(`verification_provenance.context 非法: ${provenance.context}(合法: ${VALID_VERIFICATION_CONTEXTS.join('|')})`);
70
+ }
71
+ }
62
72
  // dimensions 结构校验:每个维度必须是 { verdict, evidence } 对象
63
73
  if (record.dimensions) {
64
74
  for (const dim of requiredDimensions) {
@@ -164,7 +174,8 @@ export function writeVerification(versionDir, verificationsDir, record) {
164
174
  verification_epoch: getEffectiveVerificationEpoch(intent),
165
175
  verdict: record.verdict,
166
176
  timestamp: record.timestamp,
167
- summary: record.summary,
177
+ summary: record.summary,
178
+ verification_provenance: record.verification_provenance,
168
179
  dimensions: record.dimensions,
169
180
  atelier: atelierEvidence ? {
170
181
  record_ref: `09_ATELIER/${record.intent_id}.json`,
@@ -243,7 +254,7 @@ export function getAcrossVersionVerificationHistory(currentVersionDir, inputRef)
243
254
 
244
255
  /**
245
256
  * 快捷创建验证记录——Agent 不用手动构造完整 JSON。
246
- * 内部用 summary 填充适用维度的 evidence,生成标准记录格式。
257
+ * 快捷记录只能用于低风险的结构化核验;passed 仍必须显式声明独立验证来源。
247
258
  * @param {string} versionDir — 当前 .loom/v{N}/ 目录
248
259
  * @param {string} verificationsDir — verifications/ 目录路径
249
260
  * @param {string} intentId — 如 "INT-001"
@@ -279,6 +290,7 @@ export function createQuickVerification(versionDir, verificationsDir, intentId,
279
290
  summary,
280
291
  dimensions,
281
292
  reproduction_command: extras.reproduction_command || null,
293
+ verification_provenance: extras.verification_provenance || null,
282
294
  deviation_detail: extras.deviation_detail || null,
283
295
  });
284
296
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haaaiawd/loom",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "LOOM — 哲学驱动开发框架。Agent 通过 CLI 访问 Intent Map / 哲学 / 验证记录,不直接读文件",
5
5
  "type": "module",
6
6
  "bin": {
@@ -41,7 +41,9 @@
41
41
  12. `creative_scope` 说明 Forge 可以大胆改变什么、必须保持什么。
42
42
  13. 每个高影响 `outcome` 必须用 `validated_by` 连接到一个 `evidence` 节点。该节点必须写出 `verification.method`、`target`、`procedure`、`pass_criteria` 和 `artifact`,并回链负责把证据真正产出的 Intent。`artifact` 必须是当前版本 `verifications/` 或 `08_ASSET_LIBRARY/files/` 中实际存在的文件。`target` 是结果实际被接收、呈现或消费的位置:用户界面、目标宿主、外部系统、交付物或人工验收现场。不能用“接口返回成功”“URL 可访问”替代目标宿主中的可观察结果。
43
43
  14. 对每个重要 Intent 做简短 Pre-Mortem:最可能出现什么“表面完成”,并将其转成
44
- acceptance 或 verification_method
44
+ acceptance 或 verification_method。当 narrative 的核心现象可能被一个更容易实现、但
45
+ 语义不同的代理替换时,写 `semantic_guard`:明确哪种替代不算完成,以及 Keeper 应
46
+ 使用什么反例验证它。它不把 Intent 切成技术碎片,只防止“看似相近”偷换用户结果。
45
47
  15. 新要求、论文/资料发现、Keeper 或 Forge 发现先进入 `07_GRAPH_PROPOSALS/`,带来源、观察证据和候选类型。Architect 明确判定它已被覆盖、需要改 Graph、生成/修订 Intent、改变 acceptance,还是 Minor/Major;不得把候选静默写入正式 Graph。
46
48
  16. 当项目使用素材时,`08_ASSET_LIBRARY/manifest.json` 是唯一可用素材与来源/作者/许可/哈希的真相源。只允许已批准、可验证的本地资产进入交付;若资产构成结果证据,让 evidence 节点与资产记录双向回链。
47
49
  17. `quality_strategy` 缺失等价于 `adaptive`。只有结果确实需要作者命题、媒介原型和独立候选比较时才设为 `atelier`,且必须同时声明 `quality_contract` 与 `creative_scope`。
package/roles/keeper.md CHANGED
@@ -52,6 +52,8 @@ Keeper 默认运行在新的 Agent thread 中。只接收:
52
52
  | quality_achievement | 仅在存在质量契约时,目标水准是否有证据成立? |
53
53
 
54
54
  每个维度都必须记录“对照了什么、观察到什么、如何复现”。“合规”“没问题”不是证据。
55
+ 若 Intent 声明 `semantic_guard`,Keeper 必须执行其中的反例检查;实现看起来拥有相近
56
+ 控件或数据,不能代替叙事要求的真实现象。
55
57
 
56
58
  若 `continuity_required` 为 true,缺少明确的旧状态、操作和新状态证据时,`preservation_achievement` 不得通过;“页面目前看起来正常”不构成守恒证据。
57
59
  实现方式与 Architect 设想不同不构成偏差,只要公共契约和意图仍成立。
@@ -18,7 +18,7 @@
18
18
  "depends_on": [],
19
19
  "acceptance": "[必须] 完成契约:什么算可观察地完成,包含功能承诺、关键失败边界和防御承诺。可内联或引用 05_VERIFICATION.md。",
20
20
  "continuity_required": "[可选] true:本 Intent 改动既有用户/系统状态,必须在 acceptance 写明保留项与时序验证;验证时额外要求 preservation_achievement。一次性、无既有状态任务省略。",
21
- "quality_contract": "see 05_VERIFICATION.md#int-001-quality",
21
+ "quality_contract": "[可选] 只有声明高于功能正确性的质量目标时才填写;若引用 05_VERIFICATION.md,必须先创建对应章节。",
22
22
  "quality_strategy": "adaptive",
23
23
  "capability_needs": ["visual hierarchy", "responsive interaction"],
24
24
  "creative_scope": "可以改变布局与动效;不得改变业务流程、数据结构和公开接口。",
@@ -58,6 +58,7 @@
58
58
  "quality_strategy": "[可选] adaptive | atelier,缺失等价于 adaptive。atelier 只用于明确需要作者命题、媒介原型和独立候选比较的 Intent,并要求 quality_contract 与 creative_scope。",
59
59
  "capability_needs": "[可选] Expertise Compiler 需要补齐的专业领域字符串数组。",
60
60
  "creative_scope": "[可选] Forge 可以大胆改变什么、必须保持什么。",
61
+ "semantic_guard": "[可选] 当叙事核心容易被相似实现偷换时,写明不算完成的替代物与 Keeper 的反例验证方式。",
61
62
  "philosophy_anchors": "[必须] 哲学文档引用列表。Forge 加载哲学的指引。",
62
63
  "status": "[必须] pending | in_progress | completed | blocked | needs_review",
63
64
  "lifecycle.deprecation": "[可选] { deprecated_at, reason, replacement }。与 status 分离;弃用后 status 保持 completed。",