@haaaiawd/loom 1.2.0 → 1.2.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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.2.1 — 2026-07-31
4
+
5
+ ### Fixed
6
+
7
+ - `verify pass` 现在要求声明独立 Keeper 或人类复核来源,并将该来源写入验证记录;普通自检不能再被快捷命令包装成 `passed`。
8
+ - 高影响 capability 若选择 `adaptive`,必须留下为何不启用外部获取强门的理由;无理由的 `adaptive` 不再是静默绕过路径。
9
+ - Intent 模板不再默认引用不存在的质量契约章节,并增加叙事语义守恒与反例验证的提示。
10
+
11
+ ### Compatibility
12
+
13
+ - 旧版结构化验证记录仍可读取;新的 passed 记录(包括 `verify write`)必须声明验证来源。旧记录可供历史追溯,但不自动获得独立验证声明。
14
+
3
15
  ## 1.2.0 — 2026-07-31
4
16
 
5
17
  ### Added
package/README.md CHANGED
@@ -91,7 +91,7 @@ Agent 每完成一步都跑 guide 确认下一步。
91
91
  ### AUTO 模式
92
92
 
93
93
  ```bash
94
- loom auto on # Agent 自动连续执行,不等确认
94
+ loom auto on # Agent 在允许阶段连续推进;不会跳过契约、证据或 Keeper 门禁
95
95
  loom auto off # 每步需要用户确认
96
96
  ```
97
97
 
package/cli/bin/loom.js CHANGED
@@ -592,7 +592,7 @@ try {
592
592
  }
593
593
  case 'pass':
594
594
  case 'fail': {
595
- // loom verify pass <id> --summary "..." [--reproduction-command "..."] [--preservation-evidence "..."] [--quality-proof "..."]
595
+ // loom verify pass <id> --summary "..." --verified-by <id> --verification-context <independent_thread|human_review>
596
596
  // loom verify fail <id> --summary "..." [--deviation "..."] [--reproduction-command "..."]
597
597
  const id = rest[0];
598
598
  if (!id) die(`用法: loom verify ${sub} <id> --summary "..." [--reproduction-command "..."]${sub === 'pass' ? ' [--preservation-evidence "..."] [--quality-proof "..."]' : ' [--deviation "..."]'}`);
@@ -601,6 +601,8 @@ try {
601
601
  const deviationIdx = argv.indexOf('--deviation');
602
602
  const qualityProofIdx = argv.indexOf('--quality-proof');
603
603
  const preservationIdx = argv.indexOf('--preservation-evidence');
604
+ const verifiedByIdx = argv.indexOf('--verified-by');
605
+ const verificationContextIdx = argv.indexOf('--verification-context');
604
606
  const summary = summaryIdx !== -1 ? argv[summaryIdx + 1] : null;
605
607
  if (!summary) die(`缺少 --summary: loom verify ${sub} ${id} --summary "..."`);
606
608
  const intent = getIntent(versionDir, id);
@@ -610,11 +612,15 @@ try {
610
612
  if (sub === 'pass' && intent.quality_contract && !(qualityProofIdx !== -1 && argv[qualityProofIdx + 1])) {
611
613
  die(`Intent ${id} 声明了 quality_contract;通过前必须提供 --quality-proof,指向项目内真实的 Quality Proof Markdown 锚点。`);
612
614
  }
615
+ if (sub === 'pass' && !(verifiedByIdx !== -1 && argv[verifiedByIdx + 1] && verificationContextIdx !== -1 && argv[verificationContextIdx + 1])) {
616
+ die(`Intent ${id} 通过前必须声明独立验证来源:--verified-by <thread/run/人类标识> --verification-context <independent_thread|human_review>。同一会话自检请记录为自检,不得写 passed。`);
617
+ }
613
618
  const extras = {};
614
619
  if (reproIdx !== -1 && argv[reproIdx + 1]) extras.reproduction_command = argv[reproIdx + 1];
615
620
  if (sub === 'fail' && deviationIdx !== -1 && argv[deviationIdx + 1]) extras.deviation_detail = argv[deviationIdx + 1];
616
621
  if (sub === 'pass' && qualityProofIdx !== -1 && argv[qualityProofIdx + 1]) extras.quality_proof_ref = argv[qualityProofIdx + 1];
617
622
  if (sub === 'pass' && preservationIdx !== -1 && argv[preservationIdx + 1]) extras.preservation_evidence = argv[preservationIdx + 1];
623
+ if (sub === 'pass') extras.verification_provenance = { verified_by: argv[verifiedByIdx + 1], context: argv[verificationContextIdx + 1] };
618
624
  const verdict = sub === 'pass' ? 'passed' : 'deviated';
619
625
  const result = createQuickVerification(versionDir, verificationsDir, id, verdict, summary, extras);
620
626
  console.log(`验证记录已写入: ${result.filePath}`);
@@ -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
@@ -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
@@ -146,7 +146,7 @@ export function guideProject(projectDir, options = {}) {
146
146
  }
147
147
  } else if (result.stage_num >= 4) {
148
148
  if (auto) {
149
- result.message += '\n\n> AUTO 模式开启——直接执行 next_command,无需人类确认。';
149
+ result.message += '\n\n> AUTO 模式开启——可继续进入下一阶段;契约、证据与 Keeper 门禁仍不会被跳过。';
150
150
  } else {
151
151
  result.message += '\n\n> ⚠ AUTO 模式关闭——执行 next_command 后等人类确认再继续。';
152
152
  }
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.1",
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。",