ygl-trees 0.73.0 → 0.75.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.
- package/CHANGELOG.md +12 -0
- package/dist/apps/cli/src/bin.js +10 -1
- package/dist/bundle/bin.cjs +79 -1
- package/dist/packages/ops/tools/src/db-cmds.js +82 -1
- package/llms.txt +1 -1
- package/package.json +5 -4
- package/skills/ygl-trees-design-capture/SKILL.md +55 -0
- package/skills/ygl-trees-eval-gates/SKILL.md +83 -0
- package/skills/ygl-trees-knowledge/SKILL.md +53 -0
- package/skills/ygl-trees-lifecycle/SKILL.md +101 -0
- package/skills/ygl-trees-quickstart/SKILL.md +171 -0
- package/skills/ygl-trees-relating/SKILL.md +53 -0
- package/skills/ygl-trees-research/SKILL.md +61 -0
- package/skills/ygl-trees-techtree/SKILL.md +63 -0
- package/skills/ygl-trees-tree-building/SKILL.md +51 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.75.0(2026-10-17)npm 包修复——skills/ 进 tarball + 乱码修复 + fail-loud
|
|
4
|
+
- **修复(P0)**:npm tarball 现在包含 skills/ 目录——9 个内置技能随包分发,db init 的 "auto-imported" 承诺兑现
|
|
5
|
+
- **修复**:autoImportBuiltinSkills 找不到技能时不再静默返回 0——显式警告 "skills/ directory not found"
|
|
6
|
+
- **修复**:Linux 管道/重定向输出乱码——ANSI 颜色严格检测(isTTY===true 才开色)+ NO_COLOR/FORCE_COLOR 环境变量支持
|
|
7
|
+
- files 字段:dist + ygl.bundle.json + README + docs + CHANGELOG + llms.txt + **skills/**
|
|
8
|
+
|
|
9
|
+
## 0.74.0(2026-10-17)export-markdown + v2 测试 + 技能同步
|
|
10
|
+
- **db export-markdown**:一键生成工作区 Markdown 报告(Summary/Projects/Claims/Knowledge/Trees)
|
|
11
|
+
- **v2 功能测试**:6 项新单测覆盖 time-travel/timeline/workspace/obsidian/help-suggestions/llms-sections
|
|
12
|
+
- **技能同步 v0.73.0**:quickstart/knowledge/research/techtree 全部更新到最新命令面
|
|
13
|
+
- **141 单测**(+6)
|
|
14
|
+
|
|
3
15
|
## 0.73.0(2026-10-17)引导增强 + Token 优化 + A2A 修复
|
|
4
16
|
- **首启引导**:无数据库时显示 Welcome + 3 步入门指南
|
|
5
17
|
- **空态引导**:列表为空时显示具体命令建议(kb/project/task)
|
package/dist/apps/cli/src/bin.js
CHANGED
|
@@ -251,7 +251,16 @@ async function main(argv) {
|
|
|
251
251
|
const args = (0, types_1.parse)(argv);
|
|
252
252
|
const ctx = {
|
|
253
253
|
json: (0, types_1.bool)(args, "json"),
|
|
254
|
-
|
|
254
|
+
// Color detection (strict): colors ONLY when we're sure the terminal
|
|
255
|
+
// supports ANSI. Fixes Linux garbled output when piped/redirected.
|
|
256
|
+
// --no-color flag → always off
|
|
257
|
+
// NO_COLOR env → off (https://no-color.org convention)
|
|
258
|
+
// FORCE_COLOR env → always on (override for exotic terminals)
|
|
259
|
+
// isTTY === true → on (real terminal)
|
|
260
|
+
// isTTY === false/undefined → off (pipe, redirect, cron, etc.)
|
|
261
|
+
color: !(0, types_1.bool)(args, "no-color") &&
|
|
262
|
+
process.env.NO_COLOR === undefined &&
|
|
263
|
+
(process.env.FORCE_COLOR !== undefined || process.stdout.isTTY === true),
|
|
255
264
|
quiet: (0, types_1.bool)(args, "quiet"),
|
|
256
265
|
};
|
|
257
266
|
const group = args._[0];
|
package/dist/bundle/bin.cjs
CHANGED
|
@@ -4670,6 +4670,84 @@ function run$12(env, action, rest, args) {
|
|
|
4670
4670
|
});
|
|
4671
4671
|
return 0;
|
|
4672
4672
|
}
|
|
4673
|
+
case "export-markdown": {
|
|
4674
|
+
const lines = [];
|
|
4675
|
+
lines.push(`# ygl-trees Workspace Report`);
|
|
4676
|
+
lines.push(`> Generated: ${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
4677
|
+
lines.push("");
|
|
4678
|
+
const counts = {};
|
|
4679
|
+
for (const t of [
|
|
4680
|
+
"projects",
|
|
4681
|
+
"sources",
|
|
4682
|
+
"evidence",
|
|
4683
|
+
"claims",
|
|
4684
|
+
"kb_notes",
|
|
4685
|
+
"skills",
|
|
4686
|
+
"trees",
|
|
4687
|
+
"nodes",
|
|
4688
|
+
"artifacts",
|
|
4689
|
+
"events"
|
|
4690
|
+
]) try {
|
|
4691
|
+
counts[t] = db.get(`SELECT COUNT(*) as n FROM ${t}`)?.n || 0;
|
|
4692
|
+
} catch {
|
|
4693
|
+
counts[t] = 0;
|
|
4694
|
+
}
|
|
4695
|
+
lines.push(`## Summary`);
|
|
4696
|
+
lines.push("");
|
|
4697
|
+
lines.push(`| Entity | Count |`);
|
|
4698
|
+
lines.push(`|---|---|`);
|
|
4699
|
+
const labels = {
|
|
4700
|
+
projects: "项目",
|
|
4701
|
+
sources: "来源",
|
|
4702
|
+
evidence: "证据",
|
|
4703
|
+
claims: "断言",
|
|
4704
|
+
kb_notes: "知识",
|
|
4705
|
+
skills: "技能",
|
|
4706
|
+
trees: "树",
|
|
4707
|
+
nodes: "节点",
|
|
4708
|
+
artifacts: "产出",
|
|
4709
|
+
events: "事件"
|
|
4710
|
+
};
|
|
4711
|
+
for (const [k, v] of Object.entries(counts)) lines.push(`| ${labels[k] || k} | ${v} |`);
|
|
4712
|
+
lines.push("");
|
|
4713
|
+
lines.push(`## Projects`);
|
|
4714
|
+
const projects = db.all("SELECT name, question, created FROM projects ORDER BY created DESC LIMIT 10");
|
|
4715
|
+
for (const p of projects) lines.push(`- **${p.name}**: ${p.question || "(no question)"}`);
|
|
4716
|
+
lines.push("");
|
|
4717
|
+
lines.push(`## Recent Claims`);
|
|
4718
|
+
const claims = db.all("SELECT text, status, confidence FROM claims ORDER BY created DESC LIMIT 10");
|
|
4719
|
+
for (const c of claims) lines.push(`- [${c.status}] ${String(c.text).slice(0, 80)}... (${c.confidence})`);
|
|
4720
|
+
lines.push("");
|
|
4721
|
+
lines.push(`## Knowledge Notes`);
|
|
4722
|
+
const kb = db.all("SELECT title, tags, confidence FROM kb_notes ORDER BY created DESC LIMIT 10");
|
|
4723
|
+
for (const k of kb) lines.push(`- **${String(k.title).slice(0, 50)}** ${k.tags ? `(${k.tags})` : ""} [${k.confidence}]`);
|
|
4724
|
+
lines.push("");
|
|
4725
|
+
lines.push(`## Trees`);
|
|
4726
|
+
const trees = db.all("SELECT name FROM trees");
|
|
4727
|
+
for (const t of trees) {
|
|
4728
|
+
const nodes = db.get("SELECT COUNT(*) as n FROM nodes WHERE tree_id=(SELECT id FROM trees WHERE name=?)", [t.name])?.n || 0;
|
|
4729
|
+
lines.push(`- **${t.name}** (${nodes} nodes)`);
|
|
4730
|
+
}
|
|
4731
|
+
lines.push("");
|
|
4732
|
+
lines.push(`---`);
|
|
4733
|
+
lines.push(`*Auto-generated by ygl-trees db export-markdown*`);
|
|
4734
|
+
const md = lines.join("\n");
|
|
4735
|
+
const outPath = rest[0];
|
|
4736
|
+
if (outPath) {
|
|
4737
|
+
node_fs.writeFileSync(outPath, md);
|
|
4738
|
+
ok(ctx, `markdown report saved: ${outPath} (${md.length} chars)`);
|
|
4739
|
+
} else {
|
|
4740
|
+
if (ctx.json) {
|
|
4741
|
+
console.log(JSON.stringify({
|
|
4742
|
+
markdown: md,
|
|
4743
|
+
chars: md.length
|
|
4744
|
+
}, null, 2));
|
|
4745
|
+
return 0;
|
|
4746
|
+
}
|
|
4747
|
+
out(ctx, md);
|
|
4748
|
+
}
|
|
4749
|
+
return 0;
|
|
4750
|
+
}
|
|
4673
4751
|
case "time-travel": {
|
|
4674
4752
|
const when = rest[0];
|
|
4675
4753
|
if (!when) {
|
|
@@ -10809,7 +10887,7 @@ async function main(argv) {
|
|
|
10809
10887
|
const args = parse(argv);
|
|
10810
10888
|
const ctx = {
|
|
10811
10889
|
json: bool(args, "json"),
|
|
10812
|
-
color: !bool(args, "no-color") && process.stdout.isTTY
|
|
10890
|
+
color: !bool(args, "no-color") && process.env.NO_COLOR === void 0 && (process.env.FORCE_COLOR !== void 0 || process.stdout.isTTY === true),
|
|
10813
10891
|
quiet: bool(args, "quiet")
|
|
10814
10892
|
};
|
|
10815
10893
|
const group = args._[0];
|
|
@@ -60,8 +60,17 @@ async function autoImportBuiltinSkills(env, dbFile) {
|
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
|
-
if (!skillsDir)
|
|
63
|
+
if (!skillsDir) {
|
|
64
|
+
// Fail-loud (was silent return 0): the npm package MUST ship skills/ —
|
|
65
|
+
// if we can't find them, the user deserves to know why agent match
|
|
66
|
+
// returns nothing. This is a packaging bug indicator, not a valid state.
|
|
67
|
+
const { out: outFn, paint: paintFn, C: Cf } = require("../../../core/text/src/fmt");
|
|
68
|
+
if (env.ctx && !env.ctx.json) {
|
|
69
|
+
outFn(env.ctx, paintFn(Cf.yellow, " ! built-in skills: 0 found (skills/ not shipped or not in package)", env.ctx.color));
|
|
70
|
+
outFn(env.ctx, paintFn(Cf.dim, " this is a packaging bug — skills/ should be in the npm tarball", env.ctx.color));
|
|
71
|
+
}
|
|
64
72
|
return 0;
|
|
73
|
+
}
|
|
65
74
|
const { getOrm } = require("../../../core/database/src/orm");
|
|
66
75
|
const writes = require("../../../research/pipeline/src/writes");
|
|
67
76
|
const { parseFrontmatter } = require("../../../core/text/src/text");
|
|
@@ -108,6 +117,8 @@ function run(env, action, rest, args) {
|
|
|
108
117
|
autoImportBuiltinSkills(env, file).then(async (n) => {
|
|
109
118
|
if (n > 0)
|
|
110
119
|
(0, fmt_1.ok)(ctx, `built-in skills: ${n} imported (agent match ready)`);
|
|
120
|
+
else
|
|
121
|
+
warn(ctx, "built-in skills: 0 imported — skills/ directory not found in package");
|
|
111
122
|
// Release the ORM connection so the lock doesn't linger
|
|
112
123
|
try {
|
|
113
124
|
const { closeOrm } = require("../../../core/database/src/orm");
|
|
@@ -133,6 +144,76 @@ function run(env, action, rest, args) {
|
|
|
133
144
|
(0, fmt_1.show)(ctx, { file, size_kb: (fs.statSync(file).size / 1024).toFixed(1), ...s });
|
|
134
145
|
return 0;
|
|
135
146
|
}
|
|
147
|
+
case "export-markdown": {
|
|
148
|
+
// v2: auto-generated workspace report in markdown
|
|
149
|
+
const lines = [];
|
|
150
|
+
lines.push(`# ygl-trees Workspace Report`);
|
|
151
|
+
lines.push(`> Generated: ${new Date().toISOString()}`);
|
|
152
|
+
lines.push("");
|
|
153
|
+
// Metrics
|
|
154
|
+
const counts = {};
|
|
155
|
+
for (const t of ["projects", "sources", "evidence", "claims", "kb_notes", "skills", "trees", "nodes", "artifacts", "events"]) {
|
|
156
|
+
try {
|
|
157
|
+
counts[t] = db.get(`SELECT COUNT(*) as n FROM ${t}`)?.n || 0;
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
counts[t] = 0;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
lines.push(`## Summary`);
|
|
164
|
+
lines.push("");
|
|
165
|
+
lines.push(`| Entity | Count |`);
|
|
166
|
+
lines.push(`|---|---|`);
|
|
167
|
+
const labels = { projects: "项目", sources: "来源", evidence: "证据", claims: "断言", kb_notes: "知识", skills: "技能", trees: "树", nodes: "节点", artifacts: "产出", events: "事件" };
|
|
168
|
+
for (const [k, v] of Object.entries(counts))
|
|
169
|
+
lines.push(`| ${labels[k] || k} | ${v} |`);
|
|
170
|
+
lines.push("");
|
|
171
|
+
// Projects
|
|
172
|
+
lines.push(`## Projects`);
|
|
173
|
+
const projects = db.all("SELECT name, question, created FROM projects ORDER BY created DESC LIMIT 10");
|
|
174
|
+
for (const p of projects) {
|
|
175
|
+
lines.push(`- **${p.name}**: ${p.question || "(no question)"}`);
|
|
176
|
+
}
|
|
177
|
+
lines.push("");
|
|
178
|
+
// Recent claims
|
|
179
|
+
lines.push(`## Recent Claims`);
|
|
180
|
+
const claims = db.all("SELECT text, status, confidence FROM claims ORDER BY created DESC LIMIT 10");
|
|
181
|
+
for (const c of claims) {
|
|
182
|
+
lines.push(`- [${c.status}] ${String(c.text).slice(0, 80)}... (${c.confidence})`);
|
|
183
|
+
}
|
|
184
|
+
lines.push("");
|
|
185
|
+
// Knowledge
|
|
186
|
+
lines.push(`## Knowledge Notes`);
|
|
187
|
+
const kb = db.all("SELECT title, tags, confidence FROM kb_notes ORDER BY created DESC LIMIT 10");
|
|
188
|
+
for (const k of kb) {
|
|
189
|
+
lines.push(`- **${String(k.title).slice(0, 50)}** ${k.tags ? `(${k.tags})` : ""} [${k.confidence}]`);
|
|
190
|
+
}
|
|
191
|
+
lines.push("");
|
|
192
|
+
// Trees
|
|
193
|
+
lines.push(`## Trees`);
|
|
194
|
+
const trees = db.all("SELECT name FROM trees");
|
|
195
|
+
for (const t of trees) {
|
|
196
|
+
const nodes = db.get("SELECT COUNT(*) as n FROM nodes WHERE tree_id=(SELECT id FROM trees WHERE name=?)", [t.name])?.n || 0;
|
|
197
|
+
lines.push(`- **${t.name}** (${nodes} nodes)`);
|
|
198
|
+
}
|
|
199
|
+
lines.push("");
|
|
200
|
+
lines.push(`---`);
|
|
201
|
+
lines.push(`*Auto-generated by ygl-trees db export-markdown*`);
|
|
202
|
+
const md = lines.join("\n");
|
|
203
|
+
const outPath = rest[0];
|
|
204
|
+
if (outPath) {
|
|
205
|
+
fs.writeFileSync(outPath, md);
|
|
206
|
+
(0, fmt_1.ok)(ctx, `markdown report saved: ${outPath} (${md.length} chars)`);
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
if (ctx.json) {
|
|
210
|
+
console.log(JSON.stringify({ markdown: md, chars: md.length }, null, 2));
|
|
211
|
+
return 0;
|
|
212
|
+
}
|
|
213
|
+
(0, fmt_1.out)(ctx, md);
|
|
214
|
+
}
|
|
215
|
+
return 0;
|
|
216
|
+
}
|
|
136
217
|
case "time-travel": {
|
|
137
218
|
// v2: query workspace state at a past moment using entity created_at
|
|
138
219
|
const when = rest[0];
|
package/llms.txt
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# ygl-trees v0.
|
|
1
|
+
# ygl-trees v0.74.0 — AI Agent Research Workbench
|
|
2
2
|
|
|
3
3
|
> Single SQLite source of truth. Research → Production → Knowledge Reuse + Tech Tree.
|
|
4
4
|
> CLI for AI agents: every command supports --json (machine-readable, including errors).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ygl-trees",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.75.0",
|
|
4
4
|
"description": "猿光 · 科技树 — database-centric CLI for research → production → knowledge reuse, built for AI agents.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "dist/apps/cli/index.js",
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"README.md",
|
|
14
14
|
"docs",
|
|
15
15
|
"CHANGELOG.md",
|
|
16
|
-
"llms.txt"
|
|
16
|
+
"llms.txt",
|
|
17
|
+
"skills"
|
|
17
18
|
],
|
|
18
19
|
"engines": {
|
|
19
20
|
"node": ">=18"
|
|
@@ -26,8 +27,8 @@
|
|
|
26
27
|
"typeorm": "^1.1.2",
|
|
27
28
|
"@deepseek-ai/cordis": "~4.0.5-alpha.2",
|
|
28
29
|
"@deepseek-ai/cordis-plugin-timer": "~1.1.7-alpha.2",
|
|
29
|
-
"@deepseek-ai/
|
|
30
|
-
"@deepseek-ai/
|
|
30
|
+
"@deepseek-ai/schemastery": "~3.18.5-alpha.2",
|
|
31
|
+
"@deepseek-ai/cosmokit": "~1.8.6-alpha.2"
|
|
31
32
|
},
|
|
32
33
|
"devDependencies": {
|
|
33
34
|
"@types/node": "^20.0.0",
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ygl-trees-design-capture
|
|
3
|
+
description: >-
|
|
4
|
+
ygl-trees design 捕获方法论(spec R12/R13.2):三类 design 工件的捕获法——ui-ux 用 DESIGN.md
|
|
5
|
+
格式(google-labs-code:YAML tokens + rationale 节序 + lint 工具链);arch/decision 用 ADR
|
|
6
|
+
三段(上下文/决策/后果)。produce design 命令 + design_type 信封 + rel 挂节点 + DESIGN.md
|
|
7
|
+
单一事实源纪律。触发词:design 捕获、设计决策、ADR、DESIGN.md、ui-ux、架构决策、
|
|
8
|
+
design_type、produce design、设计沉淀。
|
|
9
|
+
version: 0.33.0
|
|
10
|
+
tags: ygl-trees,methodology,design,adr,design-md
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Design 捕获方法论:三类工件三种写法
|
|
14
|
+
|
|
15
|
+
## 1. 分型(用户裁定 2026-10-17)
|
|
16
|
+
|
|
17
|
+
| design_type | 格式 | 工具链 |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| `ui-ux` | **DESIGN.md 格式**([google-labs-code/design.md](https://github.com/google-labs-code/design.md)) | `npx -p @google/design.md designmd lint`(Windows 别名) |
|
|
20
|
+
| `arch` | ADR 三段:上下文/决策/后果 | 无(Markdown 纪律) |
|
|
21
|
+
| `decision` | ADR 三段(单点决策,比 arch 更小) | 无 |
|
|
22
|
+
|
|
23
|
+
## 2. ui-ux:DESIGN.md 格式要点
|
|
24
|
+
|
|
25
|
+
- **YAML front matter**(机器可读 tokens):colors / typography / rounded / spacing / components(`{colors.primary}` 引用)
|
|
26
|
+
- **节序固定**:Overview → Colors → Typography → Layout → Elevation → Shapes → Components → Do's and Don'ts
|
|
27
|
+
- **lint 十一规则**:broken-ref(error) / contrast-ratio(WCAG AA) / orphaned-tokens / section-order…
|
|
28
|
+
- 组件属性面有限(backgroundColor/textColor/typography/rounded/padding/size/height/width)——border 色等页级 token 会 orphan 警告,属格式 alpha 限制
|
|
29
|
+
- **仓库纪律**:repo 根 `DESIGN.md` 是 WebUI token 单一事实源;`apps/web/src/tokens.css` 镜像它——改设计先改 DESIGN.md 再同步
|
|
30
|
+
|
|
31
|
+
## 3. arch / decision:ADR 三段
|
|
32
|
+
|
|
33
|
+
```markdown
|
|
34
|
+
# <决策标题>
|
|
35
|
+
## 上下文 ( Forces:约束、备选、为什么现在决定 )
|
|
36
|
+
## 决策 ( Chosen:一句话可执行的裁定 + 触发条件 )
|
|
37
|
+
## 后果 ( Consequences:正/负/中立,含推迟项 )
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
判例参考本仓:WAL+NORMAL 双连接(docs/conventions.md §五)、Bun Windows 受阻(docs/bun-compat.md)。
|
|
41
|
+
|
|
42
|
+
## 4. 捕获命令与挂接
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
ygl-trees produce design "<标题>" --design-type ui-ux|arch|decision --body|-f <file>
|
|
46
|
+
ygl-trees rel add node:<tree>/<key> artifact:<id> --rel decided-by # 挂到树
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
design_type 存 artifacts.meta JSON 信封(schema v8);`produce list` 可按 kind 检索。
|
|
50
|
+
|
|
51
|
+
## 5. 纪律
|
|
52
|
+
|
|
53
|
+
- 设计先有 token 再有像素:ui-ux 捕获若引入新色/新字号,先改 DESIGN.md 并 lint 0 error
|
|
54
|
+
- ADR 的"后果"必须含推迟项(没有推迟项的 ADR 多半没想清代价)
|
|
55
|
+
- 每条 design 都应 `decided-by` 挂到 evolution 树的对应节点——设计孤岛 = 没捕获
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ygl-trees-eval-gates
|
|
3
|
+
description: >-
|
|
4
|
+
ygl-trees 决策模型门槛(spec R8):语义型必检条件用 eval 题型声明——布尔 --op eval 判 P(true)、
|
|
5
|
+
多选一 --op eval-choice 判期望答案概率、等级评分 --op eval-score 判 ±1 灰带;node can --eval
|
|
6
|
+
把节点上下文包(父链+祖先链+条件+解锁集+state 袋)交给评估模型(TypeSafe jev via Vercel
|
|
7
|
+
AI Gateway)。三态映射 P≥0.7 通过 / P≤0.4 未通过 / 灰带 pending。离线安全:无凭证回落 pending。
|
|
8
|
+
触发词:决策模型、布尔判定、多选、评分、evaluation、P(true)、灰带、gray band、pending、
|
|
9
|
+
eval gate、语义门槛、quest 条件、AI 判定、jev、gateway、choice、score。
|
|
10
|
+
version: 0.26.0
|
|
11
|
+
tags: ygl-trees,eval,gate,decision-model,choice,score
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Gate 决策模型评估(boolean / choice / score)
|
|
15
|
+
|
|
16
|
+
## 1. 何时用
|
|
17
|
+
|
|
18
|
+
确定性条件(等级≥3、资源≥100、flag 有无)用普通 op(>=/==/has…),本地判定。
|
|
19
|
+
**语义型门槛**——"前置任务完成了吗""情境是否满足"——状态袋里没有对应键,用 eval 题型:
|
|
20
|
+
value 写判定问题(与上下文字面对齐,模型严格按字面读、不脑补;空 value 回落用条件名)。
|
|
21
|
+
|
|
22
|
+
## 2. 三种题型
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# 布尔:P(true) 三态
|
|
26
|
+
ygl-trees node cond gate-node --name quest_done --kind quest --op eval \
|
|
27
|
+
--value "前置任务已完成且上下文满足门槛吗"
|
|
28
|
+
|
|
29
|
+
# 多选一:期望答案的概率走同一三态(0.26.0)
|
|
30
|
+
ygl-trees node cond gate-node --name team_pick --kind quest --op eval-choice \
|
|
31
|
+
--value "该派哪个团队处理" \
|
|
32
|
+
--options "proc=采购:ERP与供应商, ops=物流:账单核对" --answer proc
|
|
33
|
+
|
|
34
|
+
# 等级评分:score∈[0,N-1] 概率加权均值,±1 灰带(0.26.0)
|
|
35
|
+
ygl-trees node cond gate-node --name maturity --kind quest --op eval-score \
|
|
36
|
+
--value "实现成熟度如何" \
|
|
37
|
+
--levels "原型:仅演示, 可用:有测试, 成熟:有用户" --min-score 2
|
|
38
|
+
|
|
39
|
+
ygl-trees node can gate-node --state x=1 --eval
|
|
40
|
+
ygl-trees node can gate-node --eval --json # evaluated[] 带 type 字段 + pending[]
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
选择/评分题面纪律(材料 §3):选项/等级描述必须编码业务价值排序(裸用"轻微/中等/严重"
|
|
44
|
+
与价值观系统性分歧,实测 ρ=0.25);等级描述带 P0/节奏等语境词。
|
|
45
|
+
|
|
46
|
+
## 3. 三态语义(材料纪律)
|
|
47
|
+
|
|
48
|
+
| 题型 | 判定量 | pass | fail | pending |
|
|
49
|
+
|---|---|---|---|---|
|
|
50
|
+
| boolean | P(true) | ≥0.7 | ≤0.4 | 0.4–0.7 灰带 |
|
|
51
|
+
| choice | P(期望答案) | ≥0.7 | ≤0.4 | 0.4–0.7 灰带 |
|
|
52
|
+
| score | score | ≥min | ≤min−1 | [min−1, min) 灰带 |
|
|
53
|
+
|
|
54
|
+
聚合行为:pass 从 missing_required 移除;fail 保留并标注;**pending 独立泳道——不放行、
|
|
55
|
+
不报错、禁作终态**。
|
|
56
|
+
|
|
57
|
+
实测谱系(真实网关 1.5s/次):可证事实 P=0.97 / 未满足前提 P=0.39 / 歧义上下文 P=0.45——
|
|
58
|
+
证据不足时概率塌向灰带而非硬答。
|
|
59
|
+
|
|
60
|
+
## 4. 上下文包(模型看到什么)
|
|
61
|
+
|
|
62
|
+
节点元数据(key/name/kind/tier/cost)+ 直接父(node_edges)+ 祖先链(node_closure)+
|
|
63
|
+
该节点全部条件 + 已解锁集 + `--state` 袋。**state 必带上下文包**——裸 state 大概率落灰带。
|
|
64
|
+
|
|
65
|
+
## 5. 凭证与端点(优先级:config > 环境变量/文件 > 默认网关)
|
|
66
|
+
|
|
67
|
+
- **config 优先**:`ygl-trees config set decision_model_llm_token <t>` 与
|
|
68
|
+
`config set decision_model_llm_api_base <url>`(api_base 给主机名自动补
|
|
69
|
+
`/v4/ai/evaluation-model`,也可给完整端点——代理/自托管网关用它接管)
|
|
70
|
+
- 回落:环境变量 `AI_GATEWAY_API_KEY` → `~/.vercel.ai-gateway.env`(`token=...`,vck_ 前缀)
|
|
71
|
+
- **离线安全**:无 token/网络失败 → 该条件 pending(`no evaluator configured`)、rc=0——
|
|
72
|
+
评估器是增强不是依赖,别把 pending 当失败重试
|
|
73
|
+
- `YGL_EVAL_OFFLINE=1` 强制离线(测试/demo 确定性;优先于 config 的 eval.offline)
|
|
74
|
+
- 网关协议:v4 端点 + 四个协议头(缺一 400/404);429/529 自动退避重试≤3,400/401 不重试
|
|
75
|
+
|
|
76
|
+
## 6. 批量判定与留痕
|
|
77
|
+
|
|
78
|
+
`node ready --eval`(0.16.0):仅被 eval 门槛阻塞的节点逐节点**一次多题请求**(协议原生
|
|
79
|
+
批量,同延迟)判定——全过提升进 ready(`eval_verified` 标记),其余进 `eval_pending`
|
|
80
|
+
(逐题带 P);离线全 pending、rc=0。
|
|
81
|
+
|
|
82
|
+
每次 `--eval` 记 events:单节点 `eval.gate`(各条件 P/outcome)、批量 `eval.ready`
|
|
83
|
+
(promoted/pending 键集),`agent log` 可审计。结果不落库——现评现判,保持无状态。
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ygl-trees-knowledge
|
|
3
|
+
description: >-
|
|
4
|
+
ygl-trees 知识复用闭环:kb 笔记(add/import 批量 markdown/search 中文检索带标题加权/
|
|
5
|
+
link/ingest 报告入库)、promote 笔记提升为可复用 skill(L1/L2/L3 渐进披露)、agent match
|
|
6
|
+
技能命中。让研究报告沉淀为可被下次任务自动命中的技能。
|
|
7
|
+
触发词:知识库、knowledge、kb、笔记、note、批量导入、import、search、检索、promote、
|
|
8
|
+
技能、skill、复用、agent match。
|
|
9
|
+
version: 0.61.0
|
|
10
|
+
tags: ygl-trees,knowledge,kb,skill,reuse
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# 知识复用闭环:报告 → 笔记 → skill → 自动命中
|
|
14
|
+
|
|
15
|
+
## 1. 入库
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
ygl-trees kb ingest <reportId> # 报告入库
|
|
19
|
+
ygl-trees kb add --title "..." --body "..." [--tags a,b]
|
|
20
|
+
ygl-trees kb import <dir|file.md> # 递归批量导入 markdown(frontmatter:
|
|
21
|
+
# title/tags/confidence/url;# 标题或文件名回退;
|
|
22
|
+
# 单事务批量,2189 条 <1s;内容 hash 幂等)
|
|
23
|
+
ygl-trees kb link <a> <b> --rel related
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## 2. 检索(中文原生)
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
ygl-trees kb rm <id>
|
|
30
|
+
db obsidian [dir](导出为 Obsidian vault)(删除笔记)
|
|
31
|
+
kb search(--eval 语义重排:决策模型逐条评估相关性) "闭包表 索引"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
排序语义:命中 token 数降序 → **标题含 token 优先** → bm25。写查询词时把最独特的关键词
|
|
35
|
+
放进去;返回字段含 title_matched(标题命中数)。
|
|
36
|
+
|
|
37
|
+
## 3. 提升为 skill(L1/L2/L3 渐进披露)
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
ygl-trees kb promote <noteId> --name my-skill # L1 描述自动从正文提炼(含来源标注)
|
|
41
|
+
ygl-trees skill refs add my-skill docs/x.md --body "..." # L3 按需引用
|
|
42
|
+
ygl-trees agent match "我要做 X 任务" # Agent 靠 L1 命中技能
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
- L1 description 常驻上下文(做什么+何时触发+同义词),L2 body 激活才加载
|
|
46
|
+
- 好的 L1 决定命中率:写清触发词;promote 的自动提炼已含来源标注
|
|
47
|
+
- `skill import <SKILL.md>` 可直接导入带 frontmatter 的技能文件
|
|
48
|
+
|
|
49
|
+
## 4. 闭环使用姿势
|
|
50
|
+
|
|
51
|
+
任务开始 → `agent brief` + `agent match "<任务描述>"` → 命中则 `skill show <name>` 读 L2 →
|
|
52
|
+
按 L2 干活;任务结束 → 产出报告 → `kb ingest` → 有复用价值就 `kb promote`。
|
|
53
|
+
每命中一次 `hits+1`,热门技能自然浮出。
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ygl-trees-lifecycle
|
|
3
|
+
description: >-
|
|
4
|
+
ygl-trees 更新时机与维护节奏(何时动数据,而非怎么动):事件→动作触发表——树侧(何时
|
|
5
|
+
node unlock / add / link / cond / index)、研究资产侧(何时 source / evidence / claim /
|
|
6
|
+
resolve / kb / promote / task / produce / rel);节奏纪律(改结构后必 index+validate、
|
|
7
|
+
解锁后必挂证据、会话收口前必 brief+doctor、周期性 export 备份);该不该动的判定口诀;
|
|
8
|
+
漂移反模式。机制细节见各专职技能,本技能只回答「什么时候」。
|
|
9
|
+
触发词:更新时机、什么时候更新、何时解锁、何时该动、何时挂、维护、节奏、cadence、
|
|
10
|
+
keep in sync、同步、漂移、stale、脏索引、收口、会话结束、下次做什么、lifecycle。
|
|
11
|
+
version: 0.42.0
|
|
12
|
+
tags: ygl-trees,lifecycle,cadence,maintenance,timing
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# ygl-trees 更新时机与维护节奏
|
|
16
|
+
|
|
17
|
+
专职技能回答「**怎么动**」(命令机制)与「**动成什么样**」(方法论)。本技能只回答
|
|
18
|
+
「**什么时候动**」——这是 agent 用这套工具时最容易漏的一层:机制全会,却在该动的时刻不动,
|
|
19
|
+
于是树和研究变成两条各自漂移的流水线。
|
|
20
|
+
|
|
21
|
+
## 0. 核心判据:什么时候必须动
|
|
22
|
+
|
|
23
|
+
三条铁律,任何一条成立就必须落库(不是"记在心里"):
|
|
24
|
+
|
|
25
|
+
1. **状态变了** → 落库。能力达成、判断成形、任务推进、结构改变,都必须在**同一轮工作内**写下去;
|
|
26
|
+
下一轮会话只读得到库里的东西,脑内状态等于不存在。
|
|
27
|
+
2. **结构变了** → 立刻重建索引。任何 `node add/link/rm` 之后:`node index`(闭包+拓扑)+ `node validate`(环/悬空/脏索引)。
|
|
28
|
+
3. **写了判断** → 同轮挂证据。`node`/`claim`/`design` 出来后,`rel` 把依据挂上;没有证据的节点是待验证假设,
|
|
29
|
+
不是结论。
|
|
30
|
+
|
|
31
|
+
## 1. 事件 → 动作触发表(树侧)
|
|
32
|
+
|
|
33
|
+
| 触发事件 | 立刻做 | 别做 |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| 一个里程碑/能力**真正达成**(有可指认的证据) | `node unlock <key>` + `rel add node:<key> evidence:<id>` | 只在心里认为"算做到了",不 unlock |
|
|
36
|
+
| 发现领域里还有**未建模的子能力** | `node add <child>` + `node link <parent> <child>`(先判定必检 or `--optional`) | 用 payload/备注描述层级关系(关系必须进 `node_edges`) |
|
|
37
|
+
| 发现两条路线**互斥** | `node link a b --rel excludes --mode NOT`(或同父 XOR) | 靠文档写"这两个不能同时选" |
|
|
38
|
+
| 出现**语义门槛**("做到 X 才算过",无客观阈值) | `node cond <key> --op eval --value "<判定问题>"`(判据题型见 eval-gates 技能) | 把主观判断硬凑成 `kind=level --value 3` |
|
|
39
|
+
| 依赖**方向变了** | 先加新边 → `node index` → `node validate` → 再 `node unlink <parent> <child>`;**两次索引之间不要跨会话** | 直接删旧边(中间态可能成环,validate 会拦但状态已脏) |
|
|
40
|
+
| 同一能力被**重新拆解** | 新节点先建、挂到父、`node index` 通过后再 `node unlink` 下线旧边 | 先删后建(树上出现空洞,后续会话看到断裂) |
|
|
41
|
+
| 重复 `node link` 同一 (父,子,rel) | 幂等:原地更新并回执 `duplicate: true`(rc=0),**改 `--mode/--optional` 重连即改属性** | 以为重复 link 会新建边而反复确认(不会) |
|
|
42
|
+
| 改了 tier/cost/kind 等**定义** | 一般无需重建索引;改的是 `node_state` 才需要留意 | 把定义改动误当结构改动而反复 index(无害但浪费) |
|
|
43
|
+
| **解锁判定被质疑** | `node can <key> --state ...` 复算,看 `missing_required` / `blocked_by` / `cost_multiplier` | 凭印象回答"应该能解锁了" |
|
|
44
|
+
|
|
45
|
+
## 2. 事件 → 动作触发表(研究资产侧)
|
|
46
|
+
|
|
47
|
+
| 触发事件 | 立刻做 |
|
|
48
|
+
|---|---|
|
|
49
|
+
| 开始一个新研究问题 | `project new <name> -q "<question>"`(选定);拆出的检索式 `research query add "<text>"` |
|
|
50
|
+
| 找到外部依据(网页/文档/记录) | `source add`(`--url`/`--title`);由检索式找回的额外 `query attach <qid> <sid>` |
|
|
51
|
+
| 从原文里看到**关键句** | `evidence add <sid> --quote "..."`(**quote 必须是原文**,`--note` 放你的解读) |
|
|
52
|
+
| 形成**一个可被反驳的判断** | `claim add "<text>"` + `claim link <cid> <eid> [--stance supports|refutes]` |
|
|
53
|
+
| 判断**稳定下来**(支持/反对够了) | `claim resolve <cid> --as supported|refuted|contested`(人工仲裁,模型不代替) |
|
|
54
|
+
| 需要**可交付结论** | `produce outline` → `section` → `report`(或 `export` 落盘) |
|
|
55
|
+
| 需要**固化需求/设计** | `produce spec`;设计用 `produce design --design-type ui-ux|arch|decision` |
|
|
56
|
+
| 出现**可复用知识**(换任务还有用) | `kb add` / `kb ingest <artifactId>`;成模式再 `promote <note>` 成 skill |
|
|
57
|
+
| 出现**行动项** | `task add "<title>"` → `task doing <id>` → `task done <id>`(不建 task 的"待办"只活在本轮上下文) |
|
|
58
|
+
| 资产与树节点**有关系了** | `rel add node:<key> <type>:<ref> --rel <词>`(词汇表与"何时挂"决策树见 relating 技能) |
|
|
59
|
+
|
|
60
|
+
## 3. 节奏纪律(cadence)
|
|
61
|
+
|
|
62
|
+
| 时机 | 必做 |
|
|
63
|
+
|---|---|
|
|
64
|
+
| **每轮会话开场** | `agent brief`(我在哪、有什么、下一步)→ 有 `next_actions` 就照做 |
|
|
65
|
+
| **每轮会话收口前** | `doctor`(9 项);有脏索引/重复/孤儿按其提示修;`node index` 若结构动过 |
|
|
66
|
+
| **改树结构后**(同一轮内) | `node index` + `node validate`——**绝不留到下一轮** |
|
|
67
|
+
| **解锁任何节点后** | 同轮 `rel` 挂依据;否则验尸时无法回答"凭什么解锁" |
|
|
68
|
+
| **claim 状态变化后** | 建议 `produce report` 重生成(报告是状态的投影,不是手写副本) |
|
|
69
|
+
| **一轮工作有结论** | `kb add` 沉淀;结论若与树相关,`rel` 双向挂上 |
|
|
70
|
+
| **长时间未动(跨多轮)** | `agent brief` + `doctor` 先对账,再决定动不动树(避免基于过期认知改结构) |
|
|
71
|
+
| **周期性**(可选) | `job add ... --every <min>` + `job tick`(cron 驱动);`db export <file.sql>` 做完整备份 |
|
|
72
|
+
| **结构性风险动作前**(批量 import、大批节点改写) | 先 `db export` 备份——`db import` 的替换语义会整表重写 |
|
|
73
|
+
|
|
74
|
+
## 4. 该不该动:五个自问
|
|
75
|
+
|
|
76
|
+
1. **这个状态换一个会话还成立吗?** 只有库里写着的成立 → 要落库。
|
|
77
|
+
2. **这条边/条件是必检还是可选?** 答不出就别建——必检会阻塞解锁,可选只影响提示与折扣,语义不同。
|
|
78
|
+
3. **这个节点有依据吗?** 没有就先补 `rel` 或把它降级为假设(不 unlock)。
|
|
79
|
+
4. **改完索引还干净吗?** 不确定就跑 `node index && node validate`。
|
|
80
|
+
5. **这属于"我在想"还是"已发生"?** 前者写 `--note`/`payload`,后者才 `unlock`/`resolve`/`done`。
|
|
81
|
+
|
|
82
|
+
## 5. 反模式(漂移的六种长相)
|
|
83
|
+
|
|
84
|
+
1. **心内解锁**:能力做完了但不 `node unlock`,下次会话看树仍以为没做到 → 重复劳动或误判前置。
|
|
85
|
+
2. **裸节点**:`node add` 后从不挂 `evidence`/`claim` → 树退化成一厢情愿的愿望清单。
|
|
86
|
+
3. **结构改了不重建**:`node link` 后跳过 `node index` → `can/ready/path` 按旧闭包回答,结论全错。
|
|
87
|
+
4. **先删后建**:重构节点时不保留中间态 → 树出现断裂空洞,漂移期内的判断无法回溯。
|
|
88
|
+
5. **状态在正文里**:把"已完成/已验证"写进 `--note` 而不是 `unlock`/`resolve` → 机器读不到,投影(报告/仪表盘)全失真。
|
|
89
|
+
6. **研究资产不挂树**:evidence/claim/kb 散落在研究侧,树侧只有节点名 → 两条流水线各自演进,`rel` 就是为了防这个。
|
|
90
|
+
|
|
91
|
+
## 6. 与专职技能的分工
|
|
92
|
+
|
|
93
|
+
| 要回答的问题 | 去哪 |
|
|
94
|
+
|---|---|
|
|
95
|
+
| 什么时候动(本技能) | 这里——触发表 §1/§2、节奏 §3、判定 §4、反模式 §5 |
|
|
96
|
+
| 命令怎么用 | `ygl-trees-techtree`(树机制)、`ygl-trees-research`(研究管线)、`ygl-trees-knowledge`(kb/skill) |
|
|
97
|
+
| 树该长成什么样 | `ygl-trees-tree-building`(分解法、tier 语义、≥30 节点自检) |
|
|
98
|
+
| 该挂什么关系 | `ygl-trees-relating`(rel 词汇表 + 何时挂决策树) |
|
|
99
|
+
| 语义门槛怎么判 | `ygl-trees-eval-gates`(三题型、灰带、离线安全) |
|
|
100
|
+
| 设计怎么沉淀 | `ygl-trees-design-capture`(DESIGN.md / ADR) |
|
|
101
|
+
| 全局契约与入口 | `ygl-trees-quickstart` |
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ygl-trees-quickstart
|
|
3
|
+
description: >-
|
|
4
|
+
ygl-trees 工作台快速上手与全局契约:初始化(db init)、研究项目(project new)、
|
|
5
|
+
agent brief 定位下一步、--json 纯净纪律与退出码契约、健康检查(doctor)、
|
|
6
|
+
任务清单(task)、关联织物(rel)、用户插件(plugin install)、WebUI(webui serve)、
|
|
7
|
+
备份导出(db export)、PG 镜像/迁移、调度(job add/tick)、性能基准(pnpm bench)、
|
|
8
|
+
bundle 构建(build:bundle)、临时文件 tmp/ 规范。
|
|
9
|
+
触发词:ygl-trees 入门、初始化、quickstart、工作区、退出码、exit code、备份、export、
|
|
10
|
+
PostgreSQL、sync、migrate、镜像、job、tick、定时、doctor、健康检查、任务、task、
|
|
11
|
+
关联、rel、插件、plugin、WebUI、webui、仪表盘、dashboard、bench、基准、bundle。
|
|
12
|
+
version: 0.73.0
|
|
13
|
+
tags: ygl-trees,quickstart,backup,postgres,schedule,doctor,plugin,webui,bench
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# ygl-trees 快速上手与全局契约
|
|
17
|
+
|
|
18
|
+
单一 SQLite 文件是唯一事实源;PostgreSQL 只做单向导出。所有命令支持 `--json`。
|
|
19
|
+
|
|
20
|
+
## 1. 冷启动三步
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
node dist/apps/cli/src/bin.js db init # 建工作区($YGL_HOME,默认 ~/.ygl-trees)
|
|
24
|
+
node dist/apps/cli/src/bin.js project new <name> -q "<研究问题>"
|
|
25
|
+
node dist/apps/cli/src/bin.js agent brief # 我在哪、有什么、下一步做什么(从这里开始)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 2. Agent API 契约(冻结,勿靠猜)
|
|
29
|
+
|
|
30
|
+
- **退出码**:`0` 成功(含"重复已存在")、`1` 运行错误/404、`2` 用法错误
|
|
31
|
+
- **--json 纯净**:写 = 单个 JSON 对象(重复带 `duplicate: true`);读 = 数组/对象
|
|
32
|
+
- **写入幂等**:claim/evidence/query/kb/job 按内容 hash 判重;`--force` 显式保留重复
|
|
33
|
+
- 输出字段名是冻结契约,snake_case
|
|
34
|
+
|
|
35
|
+
## 2a. 技能发现(Agent 一键连接)
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
ygl-trees skill link # 连接到 ./.agents/skills/(DSH/Claude 等 Agent 即刻可发现)
|
|
39
|
+
ygl-trees skill link --global # 连接到 ~/.agents/skills/(全局)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## 2a. v2 新命令(0.72+)
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
ygl-trees agent ask "<问题>" # 自然语言路由(projects/search/tree/health/...)
|
|
46
|
+
ygl-trees db time-travel "日期" # 查询过去某时刻的工作区状态
|
|
47
|
+
ygl-trees db timeline # 增长时间线(events/天+里程碑)
|
|
48
|
+
ygl-trees db workspace list # 多工作区管理
|
|
49
|
+
ygl-trees db obsidian [dir] # 导出 Obsidian markdown vault
|
|
50
|
+
ygl-trees skill init <name> # 模板创建技能
|
|
51
|
+
ygl-trees skill refs <name> # 技能引用展示
|
|
52
|
+
ygl-trees llms --section <name> # 能力清单节选
|
|
53
|
+
ygl-trees llms --json # 结构化 JSON
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## 2b. 运维命令速查
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
ygl-trees doctor # 9 项健康检查
|
|
60
|
+
ygl-trees doctor --fix # 一键安全修复(脏索引标记 + 陈旧锁清理)
|
|
61
|
+
ygl-trees kb rm <id> # 删除笔记(与 skill rm 对称)
|
|
62
|
+
ygl-trees webui serve --host 0.0.0.0 --port 8642 # LAN/WSL 可访问
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
PG 迁移预检:`db migrate pg` 前自动逐表扫描 INTEGER 列类型——非整数值会**点名 table.column rowid=N** 而非迁移时才爆。
|
|
66
|
+
|
|
67
|
+
## 2c. DB 锁行为(0.68)
|
|
68
|
+
|
|
69
|
+
- CLI 短命令:进程级锁(保护写窗口,退出自动释放)
|
|
70
|
+
- webui serve:无锁(WAL 并发安全,与 CLI 共存)
|
|
71
|
+
- 死锁自愈:下一进程探测持有者 PID,死则即时清(无需手动 rmdir)
|
|
72
|
+
- YGL_TEST=1:全旁路(测试并行)
|
|
73
|
+
|
|
74
|
+
## 3. 健康检查与任务
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
ygl-trees doctor # 9 项健康检查(schema/索引/FTS/重复/孤儿/config/锁/eval信封/插件)
|
|
78
|
+
ygl-trees task add "<标题>" # 建任务(todo 起步)
|
|
79
|
+
ygl-trees task list # 按状态分组
|
|
80
|
+
ygl-trees task done <id> # 勾销(全程留痕)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## 4. 关联织物(把研究资产挂到树节点)
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
ygl-trees rel add node:<tree>/<key> claim:<id> --rel decided-by
|
|
87
|
+
ygl-trees rel add node:<key> kb:<id> --rel evidenced-by
|
|
88
|
+
ygl-trees rel add node:<key> artifact:<id> --rel tracked-by
|
|
89
|
+
ygl-trees rel list --src node:<key> # 双向过滤 + 对端显示名
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
八类引用:`node claim evidence source query kb artifact skill`。
|
|
93
|
+
详细方法论见 `ygl-trees-relating` 技能。
|
|
94
|
+
|
|
95
|
+
## 5. 用户插件(业务域扩展位)
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
ygl-trees plugin init # 建 $YGL_HOME/plugins profile
|
|
99
|
+
ygl-trees plugin install <npm包|git地址|本地目录> # 本地目录自动 link:
|
|
100
|
+
ygl-trees plugin list # 版本 / spec / 可解析态
|
|
101
|
+
ygl-trees plugin remove <name>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
插件契约:`export apply(ctx, config)` + 可选 `export commands = { <组名>: { run } }`
|
|
105
|
+
贡献 CLI 命令组。编写指南:`docs/plugin-authoring.md`。示例:`examples/plugin-demo/`。
|
|
106
|
+
|
|
107
|
+
## 6. WebUI
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
ygl-trees webui serve --port 8642 # React 客户端(xyflow 树 + 时间轴 + 产出渲染)
|
|
111
|
+
ygl-trees webui export --out d.html # 零依赖单文件导出(离线可开)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
构建 React 客户端:`pnpm --filter @ygl/web build`。设计系统:仓库根 `DESIGN.md`。
|
|
115
|
+
|
|
116
|
+
## 7. 备份 / PostgreSQL / 调度
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
ygl-trees db export <file.sql> # 全部数据表 + 行数摘要
|
|
120
|
+
ygl-trees db sync pg <url> # 可刷新 PK-only 镜像
|
|
121
|
+
ygl-trees db migrate pg <url> [--fresh] # 一次性完整保真迁移
|
|
122
|
+
ygl-trees job add <title> -- "<cmd>" --every 60 # cron 每分钟跑 job tick
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
禁止双后端同写:ygl-trees 永远只写 SQLite,PG 是单向出口。
|
|
126
|
+
|
|
127
|
+
## 8. 性能基准
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
pnpm bench # 7 席(startup/写入/检索/索引),结果 tmp/bench-results.json
|
|
131
|
+
pnpm build:bundle # tsdown 单文件 bundle(~323KB,10% 启动提速)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## 9. 临时文件铁律
|
|
135
|
+
|
|
136
|
+
一切临时脚本/数据放 `<repo>/tmp/`(已 gitignore),**禁止**写系统临时目录。
|
|
137
|
+
|
|
138
|
+
## 10. 用户配置(config 表)
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
ygl-trees config list # 全部键
|
|
142
|
+
ygl-trees config set eval.pass_threshold 0.8 # 写入即校验 + 留痕
|
|
143
|
+
ygl-trees config reset eval.pass_threshold
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## 10b. 什么时候动数据(时机问题)
|
|
147
|
+
|
|
148
|
+
本技能讲"怎么动";**"什么时候动"看 `ygl-trees-lifecycle` 技能**——事件→动作触发表
|
|
149
|
+
(树侧何时 unlock/add/link、研究侧何时写 source/evidence/claim/kb/task)、节奏纪律
|
|
150
|
+
(改结构后必 `node index`+`validate`、解锁后必挂证据、会话收口前必 `agent brief`+`doctor`)、
|
|
151
|
+
以及漂移反模式。开场先 `agent brief`,收口前 `doctor`。
|
|
152
|
+
|
|
153
|
+
## 10c. 冻结契约五项(0.55 定契)
|
|
154
|
+
|
|
155
|
+
| 契约 | 含义 | 门禁 |
|
|
156
|
+
|---|---|---|
|
|
157
|
+
| ① 退出码 | 0 成功 / 1 运行错误 / 2 用法错误 | contract 4 |
|
|
158
|
+
| ② 写入幂等 | 同内容重跑 rc=0 + `duplicate:true` | contract 1+2 |
|
|
159
|
+
| ③ 坏引用点名 | `xxx not found: <id>`(不裸异常) | contract 3 |
|
|
160
|
+
| ④ --json 通道 | 成功+失败都可整体 `JSON.parse` | contract 5 |
|
|
161
|
+
| ⑤ flag 校验 | 裸 flag / 枚举非法 → rc=2,不落库 | contract 6 |
|
|
162
|
+
|
|
163
|
+
`pnpm contract:audit` 84 项机械化断言以上全部。
|
|
164
|
+
|
|
165
|
+
## 11. 常见坑
|
|
166
|
+
|
|
167
|
+
- 数据库打不开报 "database is locked":可能残留 `<db>.lock` 目录
|
|
168
|
+
- `job --force` 必须在 `--` 之前(之后都算命令内容)
|
|
169
|
+
- 测试工作区:`YGL_HOME=<dir>` 指到临时位置
|
|
170
|
+
- Windows 下 `.cmd` shim 需 shell 解析(plugin install 已处理)
|
|
171
|
+
- CLI 入口是 `dist/apps/cli/src/bin.js`(自执行,rc 契约在此);`dist/bundle/bin.cjs` 是 bundle 版(更快)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ygl-trees-relating
|
|
3
|
+
description: >-
|
|
4
|
+
ygl-trees 关联织物方法论(spec R9/R13.2):用 rel 命令把研究资产挂到树节点——何时挂什么:
|
|
5
|
+
节点↔evidence(依据)、节点↔claim(决策)、节点↔kb(沉淀知识)、节点↔task(跟踪)、
|
|
6
|
+
节点↔artifact/spec/design(产出)。rel 词汇表与方向语义;防止"两条平行流水线"反模式。
|
|
7
|
+
触发词:关联、挂接、rel、evidenced-by、decided-by、tracked-by、关联织物、
|
|
8
|
+
relation fabric、cross-linking、研究资产挂节点。
|
|
9
|
+
version: 0.33.0
|
|
10
|
+
tags: ygl-trees,methodology,relations,fabric
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# 关联方法论:让科技树不再孤岛
|
|
14
|
+
|
|
15
|
+
## 1. 反模式先立靶
|
|
16
|
+
|
|
17
|
+
**两条平行流水线**:研究管线(source→evidence→claim→report)和科技树各自发育、零缝合——
|
|
18
|
+
检阅者看不出"这个节点依据什么证据、由什么断言决定、跟踪着什么任务"。`relations` 表就是缝合线。
|
|
19
|
+
|
|
20
|
+
## 2. rel 词汇表(方向:src --rel→ dst)
|
|
21
|
+
|
|
22
|
+
| rel | 语义 | 典型挂法 |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| `evidenced-by` | 节点的实现/成立有据可查 | node → evidence / kb |
|
|
25
|
+
| `decided-by` | 节点由某个决策(claim/design)裁定 | node → claim / design artifact |
|
|
26
|
+
| `tracked-by` | 节点的进展由任务跟踪 | node → task artifact |
|
|
27
|
+
| `derived-from` | 节点从某知识/报告衍生 | node → kb / report artifact |
|
|
28
|
+
| `related` | 兜底弱关联(能起更specific的rel就别用) | 任意 |
|
|
29
|
+
|
|
30
|
+
## 3. 引用语法与验证
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
ygl-trees rel add node:<tree>/<key> kb:<id|title> --rel evidenced-by
|
|
34
|
+
ygl-trees rel add node:<key> claim:<id> --rel decided-by
|
|
35
|
+
ygl-trees rel add node:<key> artifact:<id|title> --rel tracked-by
|
|
36
|
+
ygl-trees rel list --src node:<key> # 双向过滤 + 对端显示名
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
八类引用:`node claim evidence source query kb artifact skill`(node 裸 key 在当前树解析)。
|
|
40
|
+
404→rc1、坏类型→rc2、幂等 duplicate→rc0。
|
|
41
|
+
|
|
42
|
+
## 4. 何时挂(决策树)
|
|
43
|
+
|
|
44
|
+
- 写完一条 claim 并 resolve supported,且它解释了树上某节点为何这样设计 → `decided-by`
|
|
45
|
+
- kb ingest 了报告且节点是该报告的主体 → `derived-from`
|
|
46
|
+
- 建了 task(R12)且任务属于该节点的未来工作 → `tracked-by`
|
|
47
|
+
- evidence 的原文直接支撑节点可行性 → `evidenced-by`
|
|
48
|
+
- 拿不准 → 先 `related`,语义清楚了再 rm/add 换精确 rel
|
|
49
|
+
|
|
50
|
+
## 5. 消费面
|
|
51
|
+
|
|
52
|
+
WebUI 节点详情面板 related 区(React 与内嵌 dashboard 都有);`rel list` CLI;未解锁节点挂上
|
|
53
|
+
evidenced-by 后,检阅者能直接看到"它还差什么证据"。
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ygl-trees-research
|
|
3
|
+
description: >-
|
|
4
|
+
用 ygl-trees 研究管线做证据先行的研究:检索式登记(query add)、来源采集(source add(--relevance 只收整数 0-5,非整数 rc=2) /
|
|
5
|
+
source fetch 网页抓取)、原文摘录(evidence add)、断言与证据链接(claim add / link /
|
|
6
|
+
resolve 人工仲裁)、query↔source 召回关联(query attach)、产出证据支撑的报告
|
|
7
|
+
(produce outline/section/report/export)。
|
|
8
|
+
触发词:研究、调研、evidence、证据、claim、断言、来源、source、引文、仲裁、resolve、
|
|
9
|
+
报告、report、文献、抓取、fetch。
|
|
10
|
+
version: 0.73.0
|
|
11
|
+
tags: ygl-trees,research,evidence,claim,report
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# 证据先行的研究管线
|
|
15
|
+
|
|
16
|
+
核心循环:**来源 → 证据 → 断言 → 报告**。断言状态由证据链接自动推导
|
|
17
|
+
(supported/refuted/contested/unverified),报告只把已证实的写成结论。
|
|
18
|
+
|
|
19
|
+
> **什么时候动这些命令**(时机而非机制)见 `ygl-trees-lifecycle` 技能。
|
|
20
|
+
|
|
21
|
+
## 1. 采集
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
ygl-trees research query add "邻接表 vs 闭包表" # 登记检索式
|
|
25
|
+
ygl-trees research source add --title t --url https://... \
|
|
26
|
+
--abstract "..." --relevance 4 --query "邻接表 vs 闭包表" # 手工入库+关联检索式
|
|
27
|
+
ygl-trees research source fetch https://example.com/page # 自动抓取(title/meta/正文;
|
|
28
|
+
# 非 html 拒收零副作用;薄内容打 needs_review)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
来源按 `(project, url_hash)` 去重;`--query` 顺带建立召回关联(query list 显示召回数)。
|
|
32
|
+
|
|
33
|
+
## 2. 摘录与断言
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
ygl-trees research evidence add <sourceId> --quote "原文片段" --locator "§2"
|
|
37
|
+
ygl-trees research claim add "断言文本" --confidence A
|
|
38
|
+
ygl-trees research claim link <claimId> <evidenceId> [--stance refutes] # 状态自动推导
|
|
39
|
+
ygl-trees research claim resolve <claimId> --as contested --note "矛盾待查" # 人工仲裁出口
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
- 有支持有反对 → `contested`;仲裁后新证据链接会重新推导
|
|
43
|
+
- 全部命令幂等:同内容重跑返回已存在行(`duplicate: true`,rc=0)
|
|
44
|
+
|
|
45
|
+
## 3. 产出
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
ygl-trees produce outline # 按 claim 状态生成骨架
|
|
49
|
+
ygl-trees produce section "结论" --body "..."
|
|
50
|
+
ygl-trees produce report # 摘要→结论(带证据编号)→章节→证据清单→来源→局限
|
|
51
|
+
ygl-trees produce export <reportId> report.md
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
未证实断言自动进"局限与未决问题",不伪装成结论。
|
|
55
|
+
|
|
56
|
+
## 4. Agent 检查清单
|
|
57
|
+
|
|
58
|
+
- 先 `agent brief --json` 看 next_actions(它会告诉你缺来源/缺证据/缺断言)
|
|
59
|
+
- 每条断言至少一条证据再进报告;contested 的要么补证据要么 resolve
|
|
60
|
+
- `--json` 输出可直接 parse 拿 id;列表命令字段:source(id,title,url,relevance,…)、
|
|
61
|
+
claim(id,text,status,confidence,sup,ref)、evidence(id,source,locator,quote,used_by)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ygl-trees-techtree
|
|
3
|
+
description: >-
|
|
4
|
+
用 ygl-trees 科技树数据模型建依赖图:节点与父子关联(node add/link,rel=prereq|unlock|
|
|
5
|
+
excludes|enables × mode=AND|OR|XOR|NOT)、必检与可选条件(node cond)、闭包与拓扑索引
|
|
6
|
+
(node index/validate 环检测)、解锁判定(can/ready/path)、状态与导入导出 round-trip
|
|
7
|
+
(node unlock/export/import)。
|
|
8
|
+
触发词:科技树、技能树、依赖图、DAG、tech tree、skill tree、解锁、unlock、前置、
|
|
9
|
+
prerequisite、拓扑、闭包、closure、互斥、节点、node。
|
|
10
|
+
version: 0.73.0
|
|
11
|
+
tags: ygl-trees,techtree,dag,unlock,gate
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# 科技树节点模型
|
|
15
|
+
|
|
16
|
+
`node_edges` 邻接表是唯一事实源;`node_closure`(祖先/后代闭包)与 `node_index`(拓扑序)
|
|
17
|
+
是派生索引——`node index` 重建,遇到必检环直接报错。
|
|
18
|
+
|
|
19
|
+
> **什么时候动这些命令**(时机而非机制)见 `ygl-trees-lifecycle` 技能。
|
|
20
|
+
|
|
21
|
+
## 1. 建树
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
ygl-trees tree new demo
|
|
25
|
+
ygl-trees node add mining --cost 5 --tier 0
|
|
26
|
+
ygl-trees node link mining smelting # prereq/AND/必检(默认)
|
|
27
|
+
ygl-trees node link bronze steel --optional # 可选:不阻塞,进 missing_optional
|
|
28
|
+
ygl-trees node link bronze iron --mode OR # 任一父解锁即可
|
|
29
|
+
ygl-trees node link a b --rel excludes # 互斥分支:父解锁则子被封死
|
|
30
|
+
ygl-trees node cond steel --name lab_level --kind level --value 3 # 必检门槛
|
|
31
|
+
ygl-trees node cond steel --name has_eng --kind flag --op has --optional \
|
|
32
|
+
--effect cost_mul --weight 0.8 # 可选打折
|
|
33
|
+
# 语义型门槛(决策模型三题型 boolean/choice/score)→ 见 ygl-trees-eval-gates 技能
|
|
34
|
+
ygl-trees node index && ygl-trees node validate
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## 2. 判定与导航
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
ygl-trees node can steel --state "lab_level=3,has_eng=1" # 三类失败分开列+成本系数
|
|
41
|
+
ygl-trees node ready # 当前可解锁集合
|
|
42
|
+
ygl-trees node path steel # 解锁顺序+累计成本(目标恒最后)
|
|
43
|
+
ygl-trees node ancestors bronze # 闭包索引直查,depth=1 是直接父
|
|
44
|
+
ygl-trees node unlock mining && ygl-trees node lock mining
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
- 写入时环守卫:门控边(prereq/unlock)成环当场拒绝
|
|
48
|
+
- `node can` 输出:missing_required(阻塞)/ blocked_by(被排斥)/ missing_optional(不阻塞)
|
|
49
|
+
|
|
50
|
+
## 3. 迁移与保真
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
ygl-trees node export --out tree.json
|
|
54
|
+
ygl-trees node import --file tree.json --tree copy # 严格校验+事务,坏文件零副作用
|
|
55
|
+
ygl-trees node import --file tree.json --merge # 按 (tree,key) upsert 进已有树
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
round-trip 语义等价有 demo 断言守护;同名树默认拒绝(防覆盖)。
|
|
59
|
+
|
|
60
|
+
## 4. Agent 姿势
|
|
61
|
+
|
|
62
|
+
改结构后必跑 `node index` + `node validate`(`agent brief` 也会提示脏索引);
|
|
63
|
+
解锁判定用 `can` 而不是自己遍历 parents——mode 组合语义(AND/OR/XOR/NOT)在 canUnlock 里。
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ygl-trees-tree-building
|
|
3
|
+
description: >-
|
|
4
|
+
ygl-trees 树构建方法论(spec R13.2):把一个领域分解成丰富科技树的实践法——能力域识别、
|
|
5
|
+
层级设定(tier=成熟度/里程碑轴)、必检 vs 可选边(prereq/unlock/enables/excludes × AND/OR/XOR/NOT)、
|
|
6
|
+
语义门槛挂 eval 三题型、未来节点用真实条件锁定而非空缺。防止"三节点演示链"反模式。
|
|
7
|
+
触发词:树构建、领域分解、节点设计、tier 规划、衍生、子节点、反模式、演示链、
|
|
8
|
+
tree building、decomposition、node design、derivation。
|
|
9
|
+
version: 0.33.0
|
|
10
|
+
tags: ygl-trees,methodology,tree,decomposition
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# 树构建方法论:从领域到 30+ 节点的丰富树
|
|
14
|
+
|
|
15
|
+
## 1. 反模式先立靶
|
|
16
|
+
|
|
17
|
+
**三节点演示链**(a→b→c)是最常见的失败形态——它验证不了分叉、互斥、可选路径与门语义。
|
|
18
|
+
真实领域树的特征:**多根或多干、每层 2-6 分叉、20% 可选边、未来节点带真实条件锁定**。
|
|
19
|
+
|
|
20
|
+
## 2. 分解四步
|
|
21
|
+
|
|
22
|
+
1. **能力域识别**:领域切成 5-8 个能力域(ygl-trees 例:架构/研究/知识/模型/运维/Web)——每个域一条生长干
|
|
23
|
+
2. **tier = 里程碑轴**:t0 地基(不依赖别人的)、t1 架构、t2 模型层……tier 语义要在建树前说清楚(成熟度?时间?依赖深度?)
|
|
24
|
+
3. **边三问**:必检吗(required prereq)?可选吗(--optional,只影响 cost)?互斥吗(excludes,分支死锁)?
|
|
25
|
+
4. **未来节点锁定**:还没发生的里程碑也进树(npm-publish/bun-runtime),**用真实 eval 条件锁**——registry 选型用 `--op eval-choice`、就绪度用 `--op eval-score`,让门可以随现实翻转
|
|
26
|
+
|
|
27
|
+
## 3. 节点命名与粒度
|
|
28
|
+
|
|
29
|
+
- key 用短横线小写复合(`eval-mixed`、`webui-serve`),name 放中文全称
|
|
30
|
+
- 一个节点 = 一个可独立陈述的里程碑("采用 TypeORM" ✓;"数据库" ✗ 太泛)
|
|
31
|
+
- 粒度判据:**这个节点的完成日期能被唯一回答吗**?
|
|
32
|
+
|
|
33
|
+
## 4. 建树命令节奏
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
ygl-trees tree new <name>
|
|
37
|
+
ygl-trees node add <key> --tier <t> --name "<全称>" # 批量加完再连边
|
|
38
|
+
ygl-trees node link <parent> <child> [--optional|--rel excludes]
|
|
39
|
+
ygl-trees node cond <future-key> --op eval-choice ... # 未来门
|
|
40
|
+
ygl-trees node unlock <done-key> # 已完成里程碑
|
|
41
|
+
ygl-trees node index && ygl-trees node validate
|
|
42
|
+
ygl-trees tree use <name> && ygl-trees webui serve # React 客户端 pan/zoom 检阅
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## 5. 自检清单
|
|
46
|
+
|
|
47
|
+
- [ ] ≥30 节点、≥5 层、≥4 条生长干
|
|
48
|
+
- [ ] 可选边 ≥15%(--optional / effect cost_mul)
|
|
49
|
+
- [ ] 未来节点 ≥2 个带真实 eval 条件(不是空缺)
|
|
50
|
+
- [ ] doctor 全绿(信封有效);node validate 无环
|
|
51
|
+
- [ ] 已完成里程碑 unlock(时间轴布局能看到解锁弧)
|