clearai-dsh 0.2.0 → 0.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/lib/host.js CHANGED
@@ -18,7 +18,7 @@
18
18
  import { readdirSync, statSync } from 'node:fs'
19
19
  import { join, relative, resolve, sep } from 'node:path'
20
20
  import { z } from 'zod'
21
- import { HUMAN_GATE_ACTIONS, HUMAN_GATE_MARK, MUTATION_KIND, STATE_VERSION, applyEvent, applyMutations, derive, emptyState, renderCard, view } from './fold.js'
21
+ import { HUMAN_GATE_ACTIONS, HUMAN_GATE_MARK, MUTATION_KIND, STATE_VERSION, applyEvent, applyMutations, derive, emptyState, inspectGraphSelection, renderCard, view } from './fold.js'
22
22
  import { describeDomainShelf, formatAssertion, validateAssertions, validatePredicate, validateTerm } from './domain-language.js'
23
23
  import { install as installInvariants } from './invariant.js'
24
24
 
@@ -655,6 +655,36 @@ export function apply(ctx) {
655
655
  return reply(200, { ok: true, complete: true, entries })
656
656
  })
657
657
 
658
+ /**
659
+ * `GET /api/clearai/inspector?sessionId=…&kind=…&id=…`
660
+ *
661
+ * **知识 Inspector**:图上点了一个节点或边,把它的定义 / 关系 / 断言 / 证据链 / 历史取回来。
662
+ *
663
+ * 为什么走路由而不是塞进 `view()`:选择是**动态**的——把每个节点每条边的完整链都预先
664
+ * 推进投影,等于对一张 61 节点 / 147 边的图各算一遍,而人一次只看一个。
665
+ * 组装仍然只有一处实现(`inspectGraphSelection`,纯函数在宿主半),所以
666
+ * 「客户端自己拼证据链」这条口子没有开。
667
+ *
668
+ * 只读:**不产生任何变更**,也拿不到写入口。
669
+ */
670
+ route('/api/clearai/inspector', ['GET'], async (httpRequest) => {
671
+ const url = new URL(httpRequest.url)
672
+ const sessionId = url.searchParams.get('sessionId') ?? ''
673
+ const session = ctx.sessions.get(sessionId)
674
+ if (session === undefined) return reply(404, { ok: false, error: 'no_live_session' })
675
+ /**
676
+ * 直接用本模块的折法读状态,不走 `ctx.get('clearai')`:那条门面是同一条 fiber 上
677
+ * 提供给**预设侧**用的,而这条路由只是把同一个纯函数接到 HTTP 上——
678
+ * 中间多一跳服务解析,只会多一种「服务没接上」的失败模式。
679
+ */
680
+ const state = ctx.sessionProjections.stateOf(session, 'clearai')
681
+ if (state === null || state === undefined) return reply(200, { ok: true, found: false })
682
+ const found = inspectGraphSelection(state, { kind: url.searchParams.get('kind') ?? '', id: url.searchParams.get('id') ?? '' }, derive(state))
683
+ /** 找不到不是错误:那个对象可能刚被废止或本来就不在(如实说 `found: false`,不编一份空的)。 */
684
+ if (found === null) return reply(200, { ok: true, found: false })
685
+ return reply(200, { ok: true, found: true, inspector: found })
686
+ })
687
+
658
688
  })
659
689
 
660
690
  ctx.effect(
@@ -720,6 +750,16 @@ export function apply(ctx) {
720
750
  /** 一条断言的一行人话(货架 / 卡片 / 查询共用同一句话,免得三处各写一套)。 */
721
751
  format: (sessionId, assertion) => formatAssertion(stateOf(sessionId).lexicon, assertion),
722
752
  },
753
+ /**
754
+ * **知识 Inspector**:一个选择 → 它的定义 / 关系 / 断言 / 证据链 / 历史。
755
+ *
756
+ * 组装住在纯函数里(`inspectGraphSelection`),这里只把当前状态喂给它——
757
+ * 客户端因此永远拿不到「自己拼链」的机会,凡是读到链的地方都同源。
758
+ */
759
+ inspector: (sessionId, selection) => {
760
+ const state = stateOf(sessionId)
761
+ return inspectGraphSelection(state, selection, derive(state))
762
+ },
723
763
  }),
724
764
  'clearai: read facade',
725
765
  )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clearai-dsh",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "ClearAI: The Epistemic Loop, native to DSH.",
5
5
  "type": "module",
6
6
  "private": false,
@@ -35,6 +35,10 @@
35
35
  "node": ">=22"
36
36
  },
37
37
  "dependencies": {
38
+ "@xyflow/react": "^12.11.6",
39
+ "docx": "^9.7.1",
40
+ "graphology": "^0.26.0",
41
+ "graphology-layout-forceatlas2": "^0.10.1",
38
42
  "zod": "^4.6.1"
39
43
  },
40
44
  "dsh": {
@@ -59,7 +63,7 @@
59
63
  "worldline"
60
64
  ],
61
65
  "scripts": {
62
- "test": "bash test/run.sh",
66
+ "test": "node tools/build-vendor.mjs && bash test/run.sh",
63
67
  "build": "node tools/build-package.mjs",
64
68
  "verify": "node tools/verify-package.mjs",
65
69
  "verify:deploy": "node tools/verify-deploy.mjs",
@@ -67,6 +71,13 @@
67
71
  "pack": "node tools/build-package.mjs && npm pack ./dist/clearai-dsh",
68
72
  "release": "node tools/build-package.mjs && node tools/verify-package.mjs && node tools/verify-clean-install.mjs && npm pack ./dist/clearai-dsh --pack-destination dist",
69
73
  "e2e": "node tools/e2e-run.mjs",
70
- "e2e:long": "node tools/e2e-parallel.mjs"
74
+ "e2e:long": "node tools/e2e-parallel.mjs",
75
+ "vendor": "node tools/build-vendor.mjs",
76
+ "check:browser": "node tools/browser-graph-check.mjs",
77
+ "shots": "node tools/graph-shots.mjs"
78
+ },
79
+ "devDependencies": {
80
+ "esbuild": "^0.28.2",
81
+ "playwright-core": "^1.63.0"
71
82
  }
72
83
  }
@@ -150,6 +150,13 @@
150
150
  # 只有一个猜想时,「验证」容易退化成找证据支持自己。修订目标不受此限。
151
151
  # 内核缺省是 0(=机制中立);**这里写 2 是产品立场**,所以它是硬门,不是文案。
152
152
  minHypotheses: 2
153
+ # 知识门:将要升格的命题必须已有断言的形态,否则结案被拒(在派评估者**之前**拦)。
154
+ # 为什么要有它:断言一直是「加法,不是门槛」,于是模型的最优策略就是
155
+ # 「检索 → 总结 → 写报告」——本体图、实体图、认识论三张图都长不出来,
156
+ # 因为**完成函数里没有它们**。让缺口进卡只解决「看得见」,这一道解决「绕不过」。
157
+ # 它与 minHypotheses 是两条不同的立场(开工要有候选对比 / 结论要有形态),所以是两个键。
158
+ # 内核缺省 false(= 断言始终是加法);这里写 true 是产品立场。
159
+ requireTypedPromotion: true
153
160
  # deny_rules 十条里可移植的九条(路径越狱那条不搬:宿主沙箱已经拥有它,ClearAI 自己也说「不重复」)
154
161
  bashDenyRules: true
155
162
  # 独立评估者:spawn = fresh context(fork 会继承历史,做的人与判的人就分不开了)
@@ -461,6 +461,7 @@ export const CONFIG_KEYS = [
461
461
  'templateDir',
462
462
  'l4RejectSelfWritten',
463
463
  'minHypotheses',
464
+ 'requireTypedPromotion',
464
465
  'bashDenyRules',
465
466
  'auditProvider',
466
467
  'auditTimeoutMs',
@@ -642,6 +643,14 @@ export function apply(ctx, config = {}) {
642
643
  l4RejectSelfWritten: config.l4RejectSelfWritten !== false,
643
644
  /** ClearAI 代码里「≥2 条假设」只是文案;默认不强制。 */
644
645
  minHypotheses: config.minHypotheses ?? 0,
646
+ /**
647
+ * **知识门**:将要升格的命题必须已有断言的形态,否则结案被拒。
648
+ *
649
+ * 与 `minHypotheses` 是**两条不同的立场**,所以是两个键,不是一个:
650
+ * 前者说「开工要有候选对比」,这条说「结论要有形态」。一个部署完全可以只要前者。
651
+ * 机制侧缺省关(= 断言始终是加法),preset 里写 true——与 `blockedThreshold` 同一个模式。
652
+ */
653
+ requireTypedPromotion: config.requireTypedPromotion === true,
645
654
  bashDenyRules: config.bashDenyRules !== false,
646
655
  auditProvider: config.auditProvider ?? 'spawn',
647
656
  auditTimeoutMs: config.auditTimeoutMs ?? 240000,
@@ -720,6 +729,24 @@ export function apply(ctx, config = {}) {
720
729
  return process.cwd()
721
730
  }
722
731
 
732
+ /**
733
+ * 这个会话是不是**派出去的子会话**(评估者 / 侦察 / 执行者 / 横评仲裁)。
734
+ *
735
+ * 判据读宿主的会话头:`dsh-subagent` 生成子会话时写死 `parentSession`——与它写死
736
+ * `cwd: parentHeader.cwd` 是同一处,所以「共享工作区」与「身份是子会话」总是一起出现。
737
+ * 会话服务问不出来时按「不是子会话」处理:与 `sessionCwd` 的退路同一个方向,
738
+ * 拿不到证据时维持既有行为,不新增一条静默分支。
739
+ */
740
+ function isSpawnedChild(sessionId) {
741
+ try {
742
+ const header = ctx.get('sessions')?.get?.(sessionId)?.header
743
+ if (header === null || typeof header !== 'object') return false
744
+ return header.origin === 'subagent' || header.parentSession !== undefined
745
+ } catch {
746
+ return false
747
+ }
748
+ }
749
+
723
750
  /**
724
751
  * 认一条假设:`id` 最稳,**原文**与**唯一前缀**(≥8 字)也认。
725
752
  *
@@ -2604,6 +2631,11 @@ export function apply(ctx, config = {}) {
2604
2631
 
2605
2632
  /** 写货架;内容没变就返回 null(调用方据此决定要不要在卡里提一句)。 */
2606
2633
  function ensureFactsShelf(sessionId, state) {
2634
+ /**
2635
+ * 与词汇货架**同一条所有权规则,同一个位置**(写入口):子会话的投影里
2636
+ * 没有主线的事实,让它铺只会按它自己那份重写 `INDEX.md`。
2637
+ */
2638
+ if (isSpawnedChild(sessionId)) return null
2607
2639
  /**
2608
2640
  * 货架要显示「被推翻」那个读数,而它是**派生的**(fold 的 derive),不在原始状态里。
2609
2641
  * 所以这里问一次读面,而不是在货架里重算一遍(重算 = 第二份判据,必然漂)。
@@ -2631,8 +2663,23 @@ export function apply(ctx, config = {}) {
2631
2663
  *
2632
2664
  * 带 `mutations` 时按**这一步之后**的样子渲染:工具返回前货架就已更新,读的人不必等下一回合。
2633
2665
  */
2666
+ /**
2667
+ * 铺领域词汇货架(幂等)。
2668
+ *
2669
+ * **写入口自带所有权判据:派出去的子会话结构上写不进这份文件。**
2670
+ *
2671
+ * 规则一句话:工作区级读面属于拥有账本的会话。子会话(评估者 / 侦察 / 执行者)与主线
2672
+ * 共享同一个工作区,却持有**另一份(空的)投影**——让它照自己的投影重铺,
2673
+ * `renderShelf(子会话)` 渲染出的就是「还没有词条」的占位版。真跑里评估者两次读到
2674
+ * 7 行占位版、主线连读三次都是 96 行 21 词条,两边各自稳定:文件在「谁最后铺了一拍」
2675
+ * 之间摆动,而两边谁都没说谎。子会话**读**这份货架(评估者核对判据正要读它),但不写。
2676
+ *
2677
+ * 判据放在**写函数里**而不是调用点,与 `tools/pre-execute` 拒模型写 `clear/` 是同一条
2678
+ * 纪律:边界住在咽喉点,新增多少调用点都绕不过(不可表达优于不可违反)。
2679
+ */
2634
2680
  function ensureDomainShelf(hostService, sessionId, mutations = []) {
2635
2681
  if (hostService?.domain?.renderShelf === undefined) return ''
2682
+ if (isSpawnedChild(sessionId)) return ''
2636
2683
  try {
2637
2684
  const body = hostService.domain.renderShelf(sessionId, Array.isArray(mutations) ? mutations : [])
2638
2685
  const file = join(sessionCwd(sessionId), 'clear', 'ontology', 'domain.md')
@@ -2834,6 +2881,35 @@ export function apply(ctx, config = {}) {
2834
2881
  const goalId = isRevision ? state.goal.id : uniqueId('g')
2835
2882
  const revision = isRevision ? state.goal.revision + 1 : 1
2836
2883
  const promoteAtLevel = LEVELS.includes(args.promote_at_level) ? args.promote_at_level : 'L3'
2884
+ /**
2885
+ * **修订不许给同一句话发新身份。**
2886
+ *
2887
+ * 真跑踩出来的:一轮长跑里目标改过一次版,卡上就出现 4 条主张的 6~8 行读数——
2888
+ * 同一句话挂着两个 id、各报一个状态(一个「已支持」、另一个「未触及」),
2889
+ * 模型得自己去调和两份自相矛盾的读数。而 id 是身份:主张原文没变就该用回原来的 id,
2890
+ * 这样「这条猜想被验到哪一级」跨版本仍然接着算。
2891
+ *
2892
+ * 反过来,**这一版没再列出来的**要如实落成 `hypothesis/superseded`:
2893
+ * 折法早就认识这条变更,只是从来没有人发过它(与 `retracted` 当年那个「声明了没有生产者」
2894
+ * 是同一种病)。不发它,被放弃的猜想会永远挂在 `proposed` 上,结案时又变成一条假的「没看过」。
2895
+ */
2896
+ const existing = state.hypotheses.filter((item) => item.goal === goalId)
2897
+ const claimKey = (text) => String(text ?? '').trim().replace(/\s+/g, ' ')
2898
+ const idByClaim = new Map(existing.map((item) => [claimKey(item.claim), item.id]))
2899
+ const reused = new Set()
2900
+ const nextHypotheses = hypotheses.map((hypothesis, index) => {
2901
+ const claim = hypothesis.claim.trim()
2902
+ const carried = idByClaim.get(claimKey(claim))
2903
+ if (carried !== undefined) reused.add(carried)
2904
+ return {
2905
+ id: carried ?? `h-${Math.random().toString(36).slice(2, 8)}`,
2906
+ claim,
2907
+ refute_when: hypothesis.refute_when.trim(),
2908
+ /** 断言随假设落账;没写就是 null(加法,不是门槛)。 */
2909
+ assertions: Array.isArray(hypothesis.assertions) ? hypothesis.assertions : null,
2910
+ version: index + 1,
2911
+ }
2912
+ })
2837
2913
  mutations.push({
2838
2914
  t: 'goal/set',
2839
2915
  id: goalId,
@@ -2842,15 +2918,14 @@ export function apply(ctx, config = {}) {
2842
2918
  promote_at_level: promoteAtLevel,
2843
2919
  revision,
2844
2920
  reason: isRevision ? String(args.reason).trim() : null,
2845
- hypotheses: hypotheses.map((hypothesis, index) => ({
2846
- id: `h-${Math.random().toString(36).slice(2, 8)}`,
2847
- claim: hypothesis.claim.trim(),
2848
- refute_when: hypothesis.refute_when.trim(),
2849
- /** 断言随假设落账;没写就是 null(加法,不是门槛)。 */
2850
- assertions: Array.isArray(hypothesis.assertions) ? hypothesis.assertions : null,
2851
- version: index + 1,
2852
- })),
2921
+ hypotheses: nextHypotheses,
2853
2922
  })
2923
+ for (const dropped of existing) {
2924
+ if (reused.has(dropped.id)) continue
2925
+ const promoted = (state.facts ?? []).some((fact) => fact.hypothesis === dropped.id)
2926
+ if (promoted) continue
2927
+ mutations.push({ t: 'hypothesis/superseded', goal: goalId, id: dropped.id, claim: dropped.claim, by: `rev${revision}` })
2928
+ }
2854
2929
  let scoutNote = ''
2855
2930
  if (!isRevision && CFG.precommitRecon) {
2856
2931
  // 立约前侦察:harness 发起(不是模型请求),一生一次,且只在真的有人给过材料时做
@@ -2945,6 +3020,43 @@ export function apply(ctx, config = {}) {
2945
3020
  )
2946
3021
  }
2947
3022
  const derived = hostService.derive(sessionId)
3023
+ /**
3024
+ * **知识门:核心结论不许以纯散文升格。**
3025
+ *
3026
+ * 位置有讲究——它坐在「计划已收尾」之后、**派评估者之前**。判据与准入同一条顺序纪律:
3027
+ * 先把能做的前提查完,再花钱请人裁决;等评估卡回来才发现没形态,那一次子 run 就白花了。
3028
+ *
3029
+ * 为什么需要它:断言一直是「加法,不是门槛」,于是真跑里模型的最优策略就是
3030
+ * 「检索 → 总结 → 写报告」——本体、实体、认识论三张图都长不出来,因为完成函数里没有它们。
3031
+ * 让缺口进卡(见 `renderCard`)只解决「看得见」;这一道解决「绕不过」。
3032
+ *
3033
+ * **判据是结构谓词,不是词面**:将要升格的命题里,只要有一条没有断言就拦。
3034
+ * 出口有两条,都是诚实的:补上断言的形态再结,或者如实 `abandoned`。
3035
+ * 缺口不许被伪装成 support(那是「造证」,比不结案坏得多)。
3036
+ *
3037
+ * 开关是 `requireTypedPromotion`(机制缺省关,preset 里开):它与 `minHypotheses`
3038
+ * 是两条不同的立场,所以不共用一个键。另外它**只在知识模式下生效**——
3039
+ * 没有登记的命题就没有「形态」可谈,那时拦下来的只是一句空话。
3040
+ */
3041
+ if (CFG.requireTypedPromotion && derived.knowledge.mode === 'knowledge') {
3042
+ const threshold = levelIndexOf(goal.promote_at_level)
3043
+ /** 与下面那段升格循环**逐字同一套谓词**:将要升格的就是这几条,一条不多一条不少。 */
3044
+ const promotable = derived.hypotheses.filter(
3045
+ (hypothesis) =>
3046
+ (hypothesis.status === 'alive' || hypothesis.status === 'proposed') &&
3047
+ (hypothesis.refutations ?? 0) === 0 &&
3048
+ levelIndexOf(hypothesis.supportedLevel) >= threshold,
3049
+ )
3050
+ const untyped = promotable.filter((hypothesis) => !Array.isArray(hypothesis.assertions) || hypothesis.assertions.length === 0)
3051
+ if (untyped.length > 0) {
3052
+ return fail(
3053
+ 'claims_untyped',
3054
+ `有 ${untyped.length} 条命题已经验到门槛、却**没有断言的形态**,再往下就是散文升格:\n${untyped
3055
+ .map((hypothesis) => `- ${hypothesis.id}(${hypothesis.supportedLevel}):${hypothesis.claim}`)
3056
+ .join('\n')}\n把结论写成「主词 · 谓词 = 宾语」才进得了实体图,下一轮也才按概念取用得到。两条路:\n① **补形态再结**:词汇里没有对应的概念 / 谓词就先 \`RegisterTerm\` / \`RegisterPredicate\`,再用 \`SetGoal\` 修订目标、把这些命题连断言一起重列一遍(主张原文一字不动就会用回原 id,验到哪一级接着算),然后重新结案;\n② **如实放弃**:这些结论不值得留下形态,就用 \`CloseGoal(outcome="abandoned")\` 说清阻塞收兵。\n别为了让门放行而编一个词——词汇是约定,它将长期约束这个项目怎么写结论。`,
3057
+ )
3058
+ }
3059
+ }
2948
3060
  const unfinished = plan === null ? [] : plan.steps.filter((step) => step.status === 'open')
2949
3061
  const syntheticStep = { id: `goal:${goal.id}`, ordinal: 0, do: `核验目标 ${goal.id} 的判据与转写忠实度`, done_criteria: goal.done_criteria, artifacts: [], tests: null }
2950
3062
  const gate = {
@@ -6439,6 +6551,7 @@ export function apply(ctx, config = {}) {
6439
6551
  * 为什么不像过程本体那样只铺一次:那一份随**发布版本**,这一份随**会话**——
6440
6552
  * 一个项目今天没用词汇、明天开始用,货架必须自己长出来,而不是等人记得去建。
6441
6553
  * 它也不进卡:货架的位置在提示词里说一次就够,每拍重复就是往上下文里灌水。
6554
+ * 所有权判据(子会话不写)在写入口——见 `ensureDomainShelf`。
6442
6555
  */
6443
6556
  ensureDomainShelf(host(), sessionId)
6444
6557
  let brainNote = ''
@@ -6563,6 +6676,7 @@ export function apply(ctx, config = {}) {
6563
6676
  /**
6564
6677
  * **事实货架**:事实变了才重写、才在卡里提一句——**变了才发**,与目录同一条纪律。
6565
6678
  * 事实很少变(升格一次),所以这句话在大多数回合里都不出现。
6679
+ * 所有权判据(子会话不写)在写入口——见 `ensureFactsShelf`。
6566
6680
  */
6567
6681
  const shelf = ensureFactsShelf(sessionId, state)
6568
6682
  if (shelf !== null) factsNote = `\n- 事实库多了一条(或边界改了):${shelf}——引用前先看它的边界(推翻条件)。`
@@ -255,16 +255,31 @@ SOP 里的「确认 / 经确认才进入下一 workflow」要求的是一次**
255
255
 
256
256
  这一节讲**知识用什么语言写**。认识论循环管「凭什么信」,领域本体管「用什么语言说」——两层分开,不许互相代替。
257
257
 
258
+ ## 进入知识模式后的自主操作顺序(不等用户提醒)
259
+ 知识模式的卡里会带一行**相关已知**:当前主张文本命中的概念 / 谓词 / 可复用事实。按这个顺序走:
260
+ 1. **先复用**:命中的词条直接用 id 引用,不要重复登记同义词;
261
+ 2. **缺什么补什么**:要写断言但谓词不存在时,自主调用 \`RegisterTerm\` / \`RegisterPredicate\` 立最小的一组(每个都带依据);
262
+ 3. **命题带形态**:核心假设在 \`SetGoal\` 里连 \`assertions\` 一起写;
263
+ 4. **不够精确时才查**:\`QueryKnowledge\` 按概念 / 谓词 / 主体精确取——预检的摘要不够用时用它,不要拿一次空查询断言世上没有。
264
+
265
+ 不要为只出现一次且不需要比较的表述造词——写进 claim 就够了。
266
+
258
267
  ## 什么时候值得先立词
259
268
  - 同一件事你会反复写(某个炉次、某个速率、某个指标),而且**两条结论要能互相比对**时:先 \`RegisterTerm\` 立概念,再 \`RegisterPredicate\` 立关系(写清主词域、值域、是否单值)。
260
269
  - 词条是**约定**,不是主张:它不需要证据等级,但**依据必填**——哪份材料、哪条事实、谁说的。
261
270
  - 反过来:只在这一条结论里出现一次的说法,不必造词——写进 claim 就够了。
262
271
 
263
- ## 断言是加法,不是门槛
272
+ ## 断言:登记时可省,升格时不可省
264
273
  - \`SetGoal\` 的假设可以带 \`assertions\`(主词–谓词–宾语)。**提供即严校**:引用不存在的谓词或概念、宾语形态不合值域、同一事实里自相矛盾,一律在**落账之前**被拒。
265
- - 不写断言照旧升格,只是显示为「未结构化」。
274
+ - **登记时**不写断言放行(断言在那一刻还只是「你打算怎么验」)。但**要升格成事实的那几条必须有形态**:
275
+ 结案时系统会把「已经验到门槛、却还没有断言」的命题逐条列出来挡下,在派独立评估者之前就挡——
276
+ 这一档拦下来的话,补形态的正当路径是**先用 \`RegisterTerm\` / \`RegisterPredicate\` 立词,
277
+ 再用 \`SetGoal\` 修订目标把这些命题连断言重列一遍**(主张原文一字不动就会用回原 id,验到哪一级接着算),
278
+ 然后重新结案。别为了让门放行而编一个词:词汇是约定,它将长期约束这个项目怎么写结论。
279
+ 确实不值得留下形态的结论,就如实 \`CloseGoal(outcome="abandoned")\`——缺口不许被伪装成 support。
266
280
  - 值形态五种:\`statement\` / \`quantity\`(数值 + 单位) / \`formula\` / \`code\`(指向**工作区里真有的文件**,相对路径) / \`reference\`;关系谓词的宾语是另一个概念的 \`instance\`。
267
- - 断言在**升格那一刻**随事实落地(\`fact/promoted\`),之后不回溯改写旧事实。
281
+ - 断言在**升格那一刻**随事实落地(\`fact/promoted\`),之后不回溯改写旧事实;**事实带着产出它的那条命题 id**(措辞改了也认得出)。
282
+ - 旧账本里那些没有断言的事实照旧可读,标「未结构化」——它们补不上断言,也不算你欠账。
268
283
 
269
284
  ## 冲突只暴露,不裁决
270
285
  两条**未撤回**的已确认事实落在同一单值谓词、同一主体、而客体不同时,系统给出一对冲突读数。它**不撤回任何一侧、也不判断哪条为真**——处置走人门(\`retract_fact\` / \`keep_fact\`)。别把冲突读成「系统说这条错了」。
@@ -1,8 +1,8 @@
1
1
  name: ClearAI
2
2
  # 预设名册只有这两行元数据,而且**宿主不会替我们本地化**:它只给四个内置 id 做 i18n,
3
3
  # 插件给的 name/description **一律原样显示**(见 @deepseek-ai/dsh-client-ui-agent-preset 的
4
- # presetDisplayText())。所以这里只能自己写死一句话、两种语言并排 ——
5
- # **一句中文 + 一句英文**,不再堆成两段;长度压在卡片的 clamp 之内(约 4 行)。
4
+ # presetDisplayText())。所以这里只能自己写死、两种语言并排 ——
5
+ # **一句中文 + 一句英文**,各只说一件事;长度压在卡片的 clamp 之内(约 2 行)。
6
6
  #
7
7
  # ⚠️ description **必须**是块标量(下面那个 `|-`)或者引号,不能写成普通标量:
8
8
  # 里面有「冒号 + 空格」时普通标量会被 YAML 读成嵌套映射 —— 而名册对读失败的处理是
@@ -10,4 +10,4 @@ name: ClearAI
10
10
  # 显示成目录名 + 「暂无描述」,宿主与我们两边都不报错。0.1.2–0.1.4 都带着这个 bug 发出去过;
11
11
  # `verify-package` 里那条「preset.yml 必须真能解析出 name 与 description」就是为此立的门。
12
12
  description: |-
13
- 认识论循环:命题→观测→评估→证据→事实,从答案到证据,从证据到改进。The epistemic loop: proposition → observation → evaluation → evidence → fact — from answers to evidence, from evidence to improvement.
13
+ 利用认识论循环构建可信本体。Build a trustworthy ontology through the epistemic loop.
@@ -9,7 +9,22 @@ description: Use when working inside the ClearAI preset and you need the loop's
9
9
 
10
10
  ## 术语与状态
11
11
 
12
- 六个对象是:命题 → 验证 → 观测 → 评估 → 证据 → 事实。等级 L0–L4 只决定**谁可以写裁决**与是否需要人放行。状态不存,全部由台账现算;本技能描述的规则就是当前契约的全部。
12
+ 九个对象是:目标 → 计划 → 命题 → 验证 → 观测 → 评估 → 证据 → 事实 → 世界线(等级 L0–L4 只决定**谁可以写裁决**与是否需要人放行)。状态不存,全部由台账现算;本技能描述的规则就是当前契约的全部。
13
+
14
+ ## 知识模式的启动协议(不等用户提醒)
15
+
16
+ 目标一立、命题一登记,系统就把**知识预检**送进运行态卡:当前主张文本命中的概念 / 谓词 / 可复用事实。按这个顺序走:
17
+
18
+ | 情况 | 动作 |
19
+ |---|---|
20
+ | 命中的词条能表达 | 直接用 id 引用,不重复登记 |
21
+ | 要写断言但谓词不存在 | `RegisterTerm` / `RegisterPredicate` 立最小一组(每个带依据) |
22
+ | 核心假设准备登记 | `SetGoal` 里连 `assertions` 一起写 |
23
+ | 预检摘要不够精确 | `QueryKnowledge` 按概念 / 谓词 / 主体精确取 |
24
+ | 只出现一次且不需要比较 | 保留 claim,不造词 |
25
+ | 单步、一次性、无复用 | 不建本体(普通任务连知识模式都不进) |
26
+
27
+ **别为了让门放行而编词**:词汇是约定,它将长期约束这个项目怎么写结论。确实不值得留下形态的结论,如实 `CloseGoal(outcome="abandoned")`。
13
28
 
14
29
  ## 一句话
15
30
 
@@ -75,15 +90,3 @@ description: Use when working inside the ClearAI preset and you need the loop's
75
90
  - `inconclusive` 是诚实的答案。一次如实推翻假设的步骤照样可以通过验收——不要为了让步骤通过而写 support。
76
91
  - 重评产生**新证据**,旧证据不改、不删。
77
92
  - 结算单记四列:意图(判据)/ 事实(坐标)/ 评估者 / 差额(依据或缺口)。
78
-
79
- ## 失败与不确定
80
-
81
- - 普通工具错误:同回合返回 `ok=false` 与原因,可继续,自己纠。
82
- - **效果不确定时先观察**:越过派发边界后结局未知的操作,语义只有一条——先看当前事实,再谈重试。重试的安全前提是「知道上次到底做没做」,这个前提在结局未知时恰恰不成立。
83
- - 供应商失败被归一化成类型化事实并退避,不会把一次外部抖动升格成整个运行的死亡。
84
-
85
- ## 上下文纪律(为什么提示词里没有时间)
86
-
87
- - 系统提示词**刻意不含当前时间**:它排在稳定区,任何逐调用变化的字节都会让其后的大部分提示词与整段历史失去前缀缓存。时间由每回合的**运行态卡**承载,精确到分钟;真需要精确时间就 `bash date`。
88
- - 运行态卡只在状态变化时注入,内容是系统算出来的事实,不是你的自述。
89
- - 读文件带 `limit/offset`;命中过多就缩小范围。不要把整份大文件灌进上下文。