@namewta/speculo 1.0.11 → 1.0.12

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.
Files changed (22) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/template/workflows/learning/G-goal/G-goal.md +13 -8
  4. package/template/workflows/learning/G-goal/chain-template.md +14 -6
  5. package/template/workflows/learning/G-goal/goal-plan-template.md +72 -43
  6. package/template/workflows/learning/G-goal/orchestration-protocol.md +7 -6
  7. package/template/workflows/learning/G-goal/planning-modes.md +3 -3
  8. package/template/workflows/learning/G-goal/probe-template.md +28 -9
  9. package/template/workflows/learning/G-goal/progress-template.md +9 -5
  10. package/template/workflows/learning/G-goal/references/coverage-bar.md +7 -2
  11. package/template/workflows/learning/G-goal/references/external-goal-runner.md +13 -6
  12. package/template/workflows/learning/G-goal/references/lead-orchestration.md +52 -0
  13. package/template/workflows/learning/G-goal/references/mine-unit.md +49 -0
  14. package/template/workflows/learning/G-goal/references/split-rules.md +54 -0
  15. package/template/workflows/learning/G-goal/references/stop-rules.md +8 -3
  16. package/template/workflows/learning/INDEX.md +1 -1
  17. package/template/workflows/learning/README.md +5 -5
  18. package/template/workflows/learning/common/rules/artifact-contract.md +3 -3
  19. package/template/workflows/learning/common/rules/questioning-policy.md +5 -0
  20. package/template/workflows/learning/common/rules/teaching-policy.md +1 -1
  21. package/template/workflows/learning/common/skills/socratic-questioning/SKILL.md +4 -2
  22. package/template/workflows/learning/common/tools/validate-learning.mjs +213 -4
package/README.md CHANGED
@@ -72,7 +72,7 @@ After initialization, the target project gains the following AI agent-callable a
72
72
 
73
73
  | Workflow | Work Entries | Description |
74
74
  |---|---:|---|
75
- | **learning** | 9 | Evidence-aware learning for projects, products, subjects, languages, and skills: complete 30–40 minute plain-language lessons, Socratic inquiry lessons, Goal-Plan compilation for later external /goal execution, single-file homework review, optional retention review, and provenance-preserving topic synthesis |
75
+ | **learning** | 9 | Evidence-aware learning for projects, products, subjects, languages, and skills: complete 30–40 minute plain-language lessons, Socratic inquiry lessons, Goal-Plan compilation for later external /goal execution (teach a mine-unit of ≤15 lessons, then fan out miners), single-file homework review, optional retention review, and provenance-preserving topic synthesis |
76
76
  | **specdev** | 14 | Local-first specification-driven development: archive, code review, diagnosis, grilling, implementation, setup, learning, goal planning, prototyping, architecture review, specs, tickets, triage, and wayfinding |
77
77
  | **ops** | 3 | Host inventory and project deployment: initialize, host manage, and APP/shared-service deploy with dual documentation |
78
78
  | **person** | 2 | Persona-methodology and rigorous deliberation workflows (Mao Zedong Cognitive OS; Bidirectional Steelman Deliberation) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@namewta/speculo",
3
- "version": "1.0.11",
3
+ "version": "1.0.12",
4
4
  "description": "Workflow-packaged AI collaboration assets with state-safe refresh tooling.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -3,15 +3,15 @@ id: learning/goal
3
3
  type: workflow-entry
4
4
  workflow: learning
5
5
  name: 目标学习(目标模式)
6
- description: 为选定编程项目编译一份可被外部 /goal 执行的完整 Goal-Plan;计划会话可跟随 A 写出课程地图,但不写 Lesson、不向学习者提问、不自动串联 H/R/C。
7
- keywords: [goal, 目标模式, 目标学习, chain, coverage, mine, plan]
6
+ description: 为选定编程项目编译一份可被外部 /goal 执行的完整 Goal-Plan;计划会话可跟随 A 写出课程地图并按库存切 ≤15 的 mine unit,但不写 Lesson、不向学习者提问、不自动串联 H/R/C。
7
+ keywords: [goal, 目标模式, 目标学习, chain, coverage, mine, plan, mine-unit]
8
8
  ---
9
9
 
10
10
  # 目标学习(目标模式)
11
11
 
12
12
  > 激活本 Work 后,先读取 `<Path>{roots.workflows}/learning/README.md</Path>`。
13
13
 
14
- G-goal 是计划编译器,不是授课编排器。激活后写出完整 Goal-Plan 并停止。授课、挖掘与验收由用户在 AI CLI 中 `/goal` 读取该计划执行。
14
+ G-goal 是计划编译器,不是授课编排器。激活后写出完整 Goal-Plan 并停止。授课、挖掘与验收由用户在 AI CLI 中 `/goal` 读取该计划执行:按项目规模划出一个不超过 15 节的教学单元 → 先把这个单元的课全部写完 → 再为每一课派 miner。
15
15
 
16
16
  ## 读取范围
17
17
 
@@ -23,7 +23,7 @@ G-goal 是计划编译器,不是授课编排器。激活后写出完整 Goal-P
23
23
 
24
24
  | 用户意图 | 模式 | 谁执行 | 必须按需读取 |
25
25
  | --- | --- | --- | --- |
26
- | 选定项目并生成完整计划(默认) | `plan` | 本 Work | `<Path>{roots.workflows}/learning/G-goal/planning-modes.md</Path>`、`<Path>{roots.workflows}/learning/G-goal/goal-plan-template.md</Path>`、`<Path>{roots.workflows}/learning/A-assess-and-plan/A-assess-and-plan.md</Path>` |
26
+ | 选定项目并生成完整计划(默认) | `plan` | 本 Work | `<Path>{roots.workflows}/learning/G-goal/planning-modes.md</Path>`、`<Path>{roots.workflows}/learning/G-goal/goal-plan-template.md</Path>`、`<Path>{roots.workflows}/learning/A-assess-and-plan/A-assess-and-plan.md</Path>`、`<Path>{roots.workflows}/learning/G-goal/references/mine-unit.md</Path>` |
27
27
  | 源码或范围变化,重编译计划 | `replan` | 本 Work | 同上,加 `<Path>{roots.workflows}/learning/G-goal/orchestration-protocol.md</Path>`;旧 `goal-plan.md` 只读归档到 `goal/revisions/` |
28
28
  | 按计划写课与挖掘 | `run` / `resume` | 外部 `/goal` | 计划正文 §5 / §8;本 Work 不在激活会话内执行 |
29
29
  | 对照矩阵验收 | `verify` | 外部 `/goal`,或以后再激活本 Work 只读验收 | 计划正文 §6 与 `<Path>{roots.workflows}/learning/G-goal/references/stop-rules.md</Path>` |
@@ -35,9 +35,9 @@ G-goal 是计划编译器,不是授课编排器。激活后写出完整 Goal-P
35
35
  1. 解析 roots 与 Learning v2 状态。roots 必须来自已打开的 `<Path>{roots.state}/workspace.json</Path>`。若尚无 `status.json` / `locations.json`,先按 `<Path>{roots.workflows}/learning/I-init-setup/I-init-setup.md</Path>` 写入空状态骨架,不创建知识条目。
36
36
  2. 范围访谈一次只问一个缺口:项目路径、in-scope 模块/公开 API、out-of-scope、`expression_level`、`coverage_depth`。用户已答过不重复问。
37
37
  3. 创建或恢复 `YYYY-MM-DD-<kebab-topic>[-NN]` Change,生成 `.status.json`,设置 `phase=planning`、`current_work=learning/goal`。
38
- 4. 读取目标仓库,做编程库存:C4 Context + Container、公开入口、主数据存储、一条主路径与一条失败路径、按模块归组的范围内函数。琐碎 helper、生成代码、测试夹具标 `covered-by-parent` 或 `deferred`,不为它们开课。
38
+ 4. 读取目标仓库,做编程库存:C4 Context + Container、公开入口、主数据存储、一条主路径与一条失败路径、按模块归组的范围内函数。琐碎 helper、生成代码、测试夹具标 `covered-by-parent` 或 `deferred`,不为它们开课。Lead 可派只读库存员。
39
39
  5. 若本 Change 尚无可用 `course.md`,跟随 `<Path>{roots.workflows}/learning/A-assess-and-plan/A-assess-and-plan.md</Path>` 写出 `course.md`、`background/foundation.md`、`baseline.md`、`sources.md` 和 Change `INDEX.md`。这些文件仍归 A 所有;本 Work 只在编译计划时走 A 合同。已有课程地图则核对,不默默重写。
40
- 6. 按模板写出完整计划,禁止留下「待调研」章节:
40
+ 6. 按模板写出完整计划,禁止留下「待调研」章节,禁止预写探针题目或深化讲义;按库存把课装进 mine unit(每个 ≤15 节,写入 `chain.md` Units 表):
41
41
  - `<Path>{roots.state}/learning/changes/{change}/goal/goal-plan.md</Path>`
42
42
  - `<Path>{roots.state}/learning/changes/{change}/goal/chain.md</Path>`
43
43
  - `<Path>{roots.state}/learning/changes/{change}/goal/coverage-matrix.md</Path>`
@@ -48,10 +48,12 @@ G-goal 是计划编译器,不是授课编排器。激活后写出完整 Goal-P
48
48
 
49
49
  ## 完成标准
50
50
 
51
- - `goal/goal-plan.md` 按模板填实 Outcome、范围、四轴合同、Chain、Wave、派单菜谱、授权矩阵、DoD、HARD NO、Resume 和最小读取清单;
51
+ - `goal/goal-plan.md` 按模板填实 Outcome、范围、四轴合同、Chain、Wave、mine unit、派单菜谱、授权矩阵、DoD、HARD NO、Resume 和最小读取清单;
52
52
  - `course.md` 有可观察 OBJ;`chain.md` 与矩阵使用库存得到的真实模块名,不是占位符;
53
+ - `chain.md` 有 mine unit 列表,每个 unit ≤15(`mine_unit_cap`);小项目允许远小于 15;不得为凑满 15 而灌水开课;
54
+ - 计划不得预写探针题目或深化讲义;
53
55
  - 计划会话结束时 `lessons/` 仍无讲义正文(只允许空 INDEX);
54
- - 粘贴块含 Outcome、verification surface、constraints、boundaries、iteration policy、blocked-stop;
56
+ - 粘贴块含 Outcome、verification surface、constraints、boundaries、iteration policy(teach-then-mine10 问上限、`split-lesson`、Lead 扇出、unit ≤15)、blocked-stop;
55
57
  - 文档中的「允许」不构成 `/goal` 之外的额外授权;
56
58
  - 不复活 `Q-quiz`,不把 mine 写成学习者问答课。
57
59
 
@@ -64,6 +66,9 @@ G-goal 是计划编译器,不是授课编排器。激活后写出完整 Goal-P
64
66
  - 进度模板:`<Path>{roots.workflows}/learning/G-goal/progress-template.md</Path>`
65
67
  - 模式:`<Path>{roots.workflows}/learning/G-goal/planning-modes.md</Path>`
66
68
  - 编排:`<Path>{roots.workflows}/learning/G-goal/orchestration-protocol.md</Path>`
69
+ - Lead 编排:`<Path>{roots.workflows}/learning/G-goal/references/lead-orchestration.md</Path>`
70
+ - 挖掘单元:`<Path>{roots.workflows}/learning/G-goal/references/mine-unit.md</Path>`
71
+ - 拆课规则:`<Path>{roots.workflows}/learning/G-goal/references/split-rules.md</Path>`
67
72
  - 覆盖尺:`<Path>{roots.workflows}/learning/G-goal/references/coverage-bar.md</Path>`
68
73
  - 停止规则:`<Path>{roots.workflows}/learning/G-goal/references/stop-rules.md</Path>`
69
74
  - 外部执行器:`<Path>{roots.workflows}/learning/G-goal/references/external-goal-runner.md</Path>`
@@ -1,6 +1,6 @@
1
1
  # Chain: <主题>
2
2
 
3
- 本文件是 Goal 的课程序列投影,不是 C-consolidate,不搬迁 Change。权威 OBJ 仍在 `course.md`。
3
+ 本文件是 Goal 的课程序列投影,不是 C-consolidate,不搬迁 Change。权威 OBJ 仍在 `course.md`。mine unit 切分写在本文件 Units 表,不要另造权威文件。
4
4
 
5
5
  ## DAG
6
6
 
@@ -14,13 +14,21 @@ L-001 → L-002
14
14
  | --- | --- | --- | --- | --- | --- | --- | --- |
15
15
  | L-001 | <系统地图> | OBJ-01 | C:context, D:happy-path | — | 35 | `lessons/L-001-<slug>.md` | planned |
16
16
 
17
- 状态:`planned` | `writing` | `written` | `mined` | `revised` | `deferred`。
17
+ 状态:`planned` | `writing` | `written` | `mined` | `split_proposed` | `split` | `revised` | `deferred`。
18
+
19
+ ## Units
20
+
21
+ | unit_id | Lessons | 上限 | 状态 |
22
+ | --- | --- | --- | --- |
23
+ | U1 | L-001 | ≤15 | planned |
24
+
25
+ 任何 unit 课数不得超过 15。15 是上界,不是配额。小项目允许远小于 15。一课不得同时属于两个 unit。前置课尚未 `written` / `mined` / `revised` 的课,不得提前塞进当前单元。
18
26
 
19
27
  ## Waves
20
28
 
21
- | Wave | 焦点 | Lessons | 上限 | Gate |
29
+ | Wave | 焦点 | Lessons | 分组尺 | Gate |
22
30
  | --- | --- | --- | --- | --- |
23
- | 1 | C4 Context + Container + 主数据流 | L-001 | ≤8 或 1 个容器 | G2/G3 |
24
- | 2 | 范围内公开 API 与方法性状 | | ≤8 1 个容器 | G2/G3 |
31
+ | 1 | C4 Context + Container + 主数据流 | L-001 | 架构与数据流 | G1/G2/G3 |
32
+ | 2 | 范围内公开 API 与方法性状 | | 公开 API | G1/G2/G3 |
25
33
 
26
- 不改变任何覆盖格子的节点不要写入本表。
34
+ Wave 是覆盖尺上的内容分组,不是挖掘门,也不是并发授权。不改变任何覆盖格子的节点不要写入本表。
@@ -7,10 +7,13 @@ project_path: <path-to-source-repo>
7
7
  status: draft
8
8
  modes: [plan]
9
9
  orchestration: external-goal
10
+ run_orchestration: lead-directed
10
11
  coverage_domain: programming
11
12
  expression_level: plain
12
13
  coverage_depth: standard
13
- wave_cap: 8
14
+ mine_unit: project
15
+ mine_unit_cap: 15
16
+ learning_agent_limit: 4
14
17
  max_waves: 2
15
18
  ready_for_execution: false
16
19
  ---
@@ -44,20 +47,21 @@ ready_for_execution: false
44
47
  Outcome:
45
48
  范围内覆盖矩阵格子全部变为 covered | deferred(reason) | covered-by-parent;
46
49
  每个 chain 节点有一份 L 合同 Lesson;
47
- 每节新课或改写课有对应 goal/probes/GP-*.md
50
+ 每节新课或改写课至多两份 goal/probes/GP-*-b0N.md(每课 ≤10 问);
48
51
  写好 goal/verify.md。
49
52
 
50
53
  Verification surface:
51
54
  goal/coverage-matrix.md 状态列;
52
55
  lessons/L-*.md 存在且满足 L-lesson 合同;
53
- goal/probes/GP-*.md 关闭态且至少改变过一个矩阵格子,或显式 defer;
56
+ goal/probes/GP-*.md 关闭态且至少改变过一个矩阵格子,或显式 defer / split-proposal
54
57
  goal/verify.md 对照 stop-rules 逐条勾选。
55
58
 
56
59
  Constraints:
57
60
  roots 只来自已打开的 workspace.json;
58
61
  只写当前 Change 的 lessons/ 与 goal/;
59
62
  派单跟随 L-lesson 与 socratic-questioning audience=mine;
60
- 不发明库存里没有的函数。
63
+ 不发明库存里没有的函数;
64
+ mine unit ≤15;每课最多 10 问。
61
65
 
62
66
  Boundaries:
63
67
  不写 inquiry/、homework/、review/、synthesis/、archive/;
@@ -67,12 +71,17 @@ Boundaries:
67
71
  不自动激活 H/R/C/A-archive。
68
72
 
69
73
  Iteration policy:
70
- 同一时间只推进一个 chain 节点;
71
- L 写课,再 mine 探针,再更新矩阵与 progress;
72
- 缺口可 re-dispatch-L defer(reason),不得用新问句代替矩阵变化。
74
+ 先按 goal/chain.md 的当前 mine unit 写完该单元全部 L(阶段 T),再按课扇出 mine(阶段 M);
75
+ 禁止 L mine L → mine 交错;
76
+ 并行只发生在写集不相交的子代理之间;矩阵、chain、progress、verify、lessons/INDEX 只有 Lead 可写;
77
+ 处置:re-dispatch-L | split-lesson | defer(reason);
78
+ mine-more 只允许在该课尚未满 10 问、且 batch-01 已证明存在真实矩阵缺口时,用来打开 b02;
79
+ 满 10 问后 mine-more 非法;
80
+ 当前 mine unit 课数不得超过 15。超过则停止,回到 G-goal replan 重新切单元。
73
81
 
74
82
  Blocked-stop:
75
- 本波矩阵无变化;达到 wave_capmax_waves;用户暂停;
83
+ 本单元矩阵无变化;出现第 11 问;交错 L/mine;miner 写入 lessons/ matrix;
84
+ 同一 unit 规划超过 15 节;用户暂停;
76
85
  缺源码 / 缺 baseline / 目标无法核对;所有权冲突或路径越界。
77
86
  ```
78
87
 
@@ -105,6 +114,7 @@ Blocked-stop:
105
114
  - 不复活 `Q-quiz`
106
115
  - 不使用 `Q-question` 的 `inquiry/` Response 协议
107
116
  - 不为琐碎 helper、生成代码、测试夹具单独开课
117
+ - 不在本计划里预写探针题目或深化讲义
108
118
 
109
119
  ### Authoritative Inputs
110
120
 
@@ -113,7 +123,7 @@ Blocked-stop:
113
123
  | 1 | 用户当场决定 | 覆盖范围、暂停、defer |
114
124
  | 2 | `course.md` OBJ | 权威学习目标;本文件不改写 OBJ |
115
125
  | 3 | `goal/coverage-matrix.md` | 格子状态是覆盖权威 |
116
- | 4 | `goal/chain.md` | 课程序列投影 |
126
+ | 4 | `goal/chain.md` | 课程序列投影与 mine unit 切分 |
117
127
  | 5 | 本计划派单菜谱 | `/goal` 只按 §5 / §7 行动 |
118
128
  | 6 | 源码事实 | 库存与证据;不得发明符号 |
119
129
 
@@ -173,28 +183,32 @@ Extended Abstract 不是完成条件。
173
183
 
174
184
  ---
175
185
 
176
- ## §4 Chain and Waves
186
+ ## §4 Chain, Waves, and Mine Units
177
187
 
178
- 权威序列在 `goal/chain.md`。本计划只记录波次与门。
188
+ 权威序列与 mine unit 切分在 `goal/chain.md`。本计划只记录覆盖波与挖掘单元门。Wave 是内容分组,不是挖掘门,也不是并发授权。
179
189
 
180
- | Wave | 焦点 | Lessons | 上限 | Gate |
190
+ | Wave | 焦点 | Lessons | 分组尺 | Gate |
181
191
  | --- | --- | --- | --- | --- |
182
- | 1 | C4 Context + Container + 主数据流 | | ≤8 或 1 个容器 | G2/G3 |
183
- | 2 | 范围内公开 API 与方法性状 | | ≤8 1 个容器 | G2/G3 |
192
+ | 1 | C4 Context + Container + 主数据流 | | 架构与数据流 | G1/G2/G3 |
193
+ | 2 | 范围内公开 API 与方法性状 | | 公开 API | G1/G2/G3 |
184
194
 
185
- 默认最多 `max_waves: 2`。同一时间只推进一个 chain 节点。Wave 不是并发授权。
195
+ | Mine unit | Lessons | 上限 | 状态 |
196
+ | --- | --- | --- | --- |
197
+ | U1 | | ≤15(上界,不是配额) | planned |
198
+
199
+ 默认最多 `max_waves: 2`。`mine_unit: project`,`mine_unit_cap: 15`。小项目允许远小于 15。切分规则读 `<Path>{roots.workflows}/learning/G-goal/references/mine-unit.md</Path>`。
186
200
 
187
201
  ---
188
202
 
189
203
  ## §5 Dispatch Recipe
190
204
 
191
- `/goal` 的 `run` / `resume` 只读本节与 §7 / §8。计划会话不得执行本节。
205
+ `/goal` 的 `run` / `resume` 只读本节与 §7 / §8。计划会话不得执行本节。编排读 `<Path>{roots.workflows}/learning/G-goal/references/lead-orchestration.md</Path>`。拆课读 `<Path>{roots.workflows}/learning/G-goal/references/split-rules.md</Path>`。
192
206
 
193
- 对每个 ready 的 chain 节点,严格按这次序:
207
+ 运行形状是 **teach-then-mine**,不是一课一挖交错:
194
208
 
195
- 1. 跟随 `<Path>{roots.workflows}/learning/L-lesson/L-lesson.md</Path>` 与 `<Path>{roots.workflows}/learning/common/rules/teaching-policy.md</Path>`,写入 `lessons/L-<NNN>-<slug>.md`。
196
- 2. 跟随 `<Path>{roots.workflows}/learning/common/skills/socratic-questioning/SKILL.md</Path>` 且 `audience=mine`,按 `<Path>{roots.workflows}/learning/G-goal/probe-template.md</Path>` 写入 `goal/probes/GP-<NNN>-wave-<NN>.md`。审问对象是已写成的 Lesson 与源码,不是学习者。
197
- 3. 按探针缺口更新 `goal/coverage-matrix.md` 与 `goal/progress.md`。需要补丁时 `re-dispatch-L`;无法核对时 `defer(reason)`。
209
+ 1. **阶段 T**:按当前 mine unit 为每课派一个 L 子代理。跟随 `<Path>{roots.workflows}/learning/L-lesson/L-lesson.md</Path>` 与 `<Path>{roots.workflows}/learning/common/rules/teaching-policy.md</Path>`,写入各自的 `lessons/L-<NNN>-<slug>.md`。`lessons/INDEX.md` 只有 Lead 可写。本单元全部 planned 节点都有合格 L 合同讲义后,才进入阶段 M。
210
+ 2. **阶段 M**:为每课派一个 miner。跟随 `<Path>{roots.workflows}/learning/common/skills/socratic-questioning/SKILL.md</Path>` 且 `audience=mine`,按 `<Path>{roots.workflows}/learning/G-goal/probe-template.md</Path>` 写入 `goal/probes/GP-<lesson>-b01.md`(Q1–Q5);若仍有真实矩阵缺口且预算未满 10,再开 `b02.md`(Q6–Q10)。审问对象是已写成的 Lesson 与源码,不是学习者。miner 不得写 `lessons/` 或 matrix。
211
+ 3. **阶段 D**:仅 Lead 根据各课缺口表与 `split-proposal` 处置:`re-dispatch-L` | `split-lesson` | `defer(reason)`。Lead 提交矩阵 / progress。满 10 问后禁止 `mine-more`。
198
212
 
199
213
  禁止:
200
214
 
@@ -202,6 +216,10 @@ Extended Abstract 不是完成条件。
202
216
  - 自动激活 H / R / C / A-archive
203
217
  - 把 mine 写成学习者问答课
204
218
  - 在计划会话里写 `lessons/` 或 `goal/probes/`
219
+ - 在 Goal-Plan 里预写探针题目或深化讲义
220
+ - 阶段 T 完成前启动 miner
221
+ - `L → mine → L → mine` 交错
222
+ - 同一 `lesson_id` 第 11 问,或 `b03`
205
223
 
206
224
  ---
207
225
 
@@ -211,26 +229,28 @@ Extended Abstract 不是完成条件。
211
229
 
212
230
  | Gate | 含义 | 证据 |
213
231
  | --- | --- | --- |
214
- | G0 | 计划已编译 | `goal/goal-plan.md`、`chain.md`、`coverage-matrix.md`、`progress.md` 存在;`course.md` 有可观察 OBJ |
215
- | G2 | 本波至少改变一个矩阵格子 | 矩阵 diff + `progress.md` 一行事实 |
216
- | G3 | 本波新课或改写课有对应 probe | `goal/probes/GP-*.md` |
232
+ | G0 | 计划已编译 | `goal/goal-plan.md`、`chain.md`、`coverage-matrix.md`、`progress.md` 存在;`course.md` 有可观察 OBJ;每个 mine unit ≤15 节 |
233
+ | G1 | 本单元全部 planned 节点已写成合格 L 合同讲义 | `lessons/L-*.md`;progress 显示本 unit 先进入 `written` |
234
+ | G2 | 本单元至少改变一个矩阵格子 | 矩阵 diff + `progress.md` 一行事实 |
235
+ | G3 | 本单元新课或改写课有对应 probe,且每课 Q 数 ≤10 | `goal/probes/GP-*-b0N.md` |
217
236
  | G-final | 范围内格子全部闭合且已验收 | 矩阵无 uncovered;`goal/verify.md` 已写 |
218
237
 
219
- Overall Definition of Done = G-final。课写完不是 DoD
238
+ Overall Definition of Done = G-final。课写完不是 DoD。阶段 T 结束之前禁止 miner。阶段 M 结束之前,Lead 不得把「课写完」写成 Goal 完成。
220
239
 
221
240
  ---
222
241
 
223
242
  ## §7 Authorization Matrix
224
243
 
225
- | 动作 | 计划会话(G-goal) | `/goal` 会话 |
226
- | --- | --- | --- |
227
- | 跟随 I-init-setup 写空骨架 | 允许 | 不授权 |
228
- | 跟随 A 写或核对 course 地图 | 允许 | 不授权 |
229
- | 写 goal-plan / chain / matrix / progress 骨架 | 允许 | 只更新 matrix / progress |
230
- | `lessons/` | 不授权 | 允许,且必须走 L 合同 |
231
- | 写 `goal/probes/` | 不授权 | 允许,且必须 `audience=mine` |
232
- | 写 `goal/verify.md` | 不授权 | 允许,在停止规则触发后 |
233
- | 写 `inquiry/` / `homework/` / `review/` / `synthesis/` / `archive/` / mastered | 不授权 | 不授权 |
244
+ | 动作 | plan 会话 | `/goal` Lead | L 子代理 | Miner 子代理 |
245
+ | --- | --- | --- | --- | --- |
246
+ | 跟随 I 写空骨架 | 允许 | 不授权 | 不授权 | 不授权 |
247
+ | 跟随 A 写/核 course 地图 | 允许 | 不授权 | 不授权 | 不授权 |
248
+ | 写 goal-plan / chain 骨架 | 允许 | run 中可追加 split 子节点、切 unit | 不授权 | 不授权 |
249
+ | 更新 matrix / progress | 只写骨架 | 允许(独占) | 不授权 | 不授权 |
250
+ | 写 `lessons/L-*.md` | 不授权 | 不代替 writer | 仅自己的文件 | 不授权 |
251
+ | 写 `goal/probes/` | 不授权 | 不代替 miner | 不授权 | 仅自己的 `GP-b0N` |
252
+ | 写 `goal/verify.md` | 不授权 | 停止规则触发后允许 | 不授权 | 不授权 |
253
+ | `inquiry/` `homework/` `review/` `synthesis/` `archive/` mastered | 禁止 | 禁止 | 禁止 | 禁止 |
234
254
 
235
255
  文档中的「允许」不构成 `/goal` 之外的额外授权。
236
256
 
@@ -243,9 +263,17 @@ Overall Definition of Done = G-final。课写完不是 DoD。
243
263
  - 不自动串联 H / R / C / A-archive
244
264
  - 路径只用 `<Path>{roots.*}/...</Path>`;roots 必须来自已打开的 `workspace.json`
245
265
  - 不发明库存里没有的函数、容器或数据流
246
- - 不把 Wave 当成并发授权
266
+ - 不把 Wave 当成挖掘门或并发授权
247
267
  - 不把 Extended Abstract 当成完成条件
248
268
  - 不把计划会话里的「已批准 / 允许」当成执行授权
269
+ - 不预写探针题目,不在 Goal-Plan 里写深化讲义
270
+ - 不在阶段 T 完成前启动 miner
271
+ - 不交错 L 与 mine
272
+ - 同一 `lesson_id` 第 11 问非法;满 10 问禁止 mine-more
273
+ - miner 不得选择处置,只填表
274
+ - `split-lesson` 不得调用 `C-consolidate` 或 `relocate-learning.mjs`
275
+ - 同一 mine unit 不得超过 15 节;不得把 16+ 节一次扇出
276
+ - 宿主没有 team 时,不得借机退回旧交错循环
249
277
 
250
278
  ---
251
279
 
@@ -254,17 +282,17 @@ Overall Definition of Done = G-final。课写完不是 DoD。
254
282
  恢复时只读:
255
283
 
256
284
  1. 本文件 §0、§5、§7、§8
257
- 2. `goal/progress.md` 当前行
285
+ 2. `goal/progress.md` 当前 `mine_unit` 与阶段
258
286
  3. 最近一份 `goal/probes/GP-*.md`
259
- 4. `goal/chain.md` 中状态为 `writing` / `written` / 下一个 `planned` 的节点
287
+ 4. `goal/chain.md` Units 表与当前 unit 内状态为 `writing` / `written` / `mined` / 下一个 `planned` 的节点
260
288
 
261
- 从最后一个改变了矩阵格子的检查点继续。仍覆盖有效格子的 Lesson 不删除、不重写。源码或范围变了先回到 G-goal `replan`,不要在 `/goal` 里偷偷扩 scope。
289
+ 从最后一个改变了矩阵格子的检查点继续。仍覆盖有效格子的 Lesson 不删除、不重写。源码或范围变了先回到 G-goal `replan`,不要在 `/goal` 里偷偷扩 scope。当前 unit 若尚未全部 `written`,先完成阶段 T,禁止提前 mine
262
290
 
263
291
  ---
264
292
 
265
293
  ## §10 Progress
266
294
 
267
- 只记录可核对事实,权威表在 `goal/progress.md`。不准写主观百分比。
295
+ 只记录可核对事实,权威表在 `goal/progress.md`。不准写主观百分比。progress 必须能看出当前 unit 全部 L 先进入 `written`,再进入 `mined`。
268
296
 
269
297
  ---
270
298
 
@@ -272,10 +300,11 @@ Overall Definition of Done = G-final。课写完不是 DoD。
272
300
 
273
301
  1. 已打开的 `<Path>{roots.state}/workspace.json</Path>`
274
302
  2. 本计划 §0、§5、§7、§8
275
- 3. `goal/chain.md` 当前节点
276
- 4. `<Path>{roots.workflows}/learning/L-lesson/L-lesson.md</Path>` 与 teaching-policy
277
- 5. 写完 Lesson 后:`probe-template.md` + socratic-questioning 且 `audience=mine`
278
- 6. `goal/coverage-matrix.md` + `<Path>{roots.workflows}/learning/G-goal/references/stop-rules.md</Path>`
279
- 7. 停止规则触发后写 `goal/verify.md`
303
+ 3. `goal/chain.md` 当前 mine unit
304
+ 4. `<Path>{roots.workflows}/learning/G-goal/references/lead-orchestration.md</Path>` 与 mine-unit / split-rules
305
+ 5. 阶段 T:`<Path>{roots.workflows}/learning/L-lesson/L-lesson.md</Path>` teaching-policy
306
+ 6. 本单元全部 L 写成后:`probe-template.md` + `<Path>{roots.workflows}/learning/common/skills/socratic-questioning/SKILL.md</Path>` 且 `audience=mine`
307
+ 7. `goal/coverage-matrix.md` + `<Path>{roots.workflows}/learning/G-goal/references/stop-rules.md</Path>`
308
+ 8. 停止规则触发后写 `goal/verify.md`
280
309
 
281
310
  不要整读 archive、其他 Change,或 `inquiry/`。
@@ -1,25 +1,26 @@
1
1
  # G-goal 编排协议
2
2
 
3
- G-goal 只在 `plan` / `replan` 中编排上游地图。授课与挖掘由外部 `/goal` 按 Goal-Plan 派单。
3
+ G-goal 只在 `plan` / `replan` 中编排上游地图。授课与挖掘由外部 `/goal` 按 Goal-Plan 派单。`/goal` 采用 lead-directed:Lead 扇出、写集隔离、mine unit ≤15。细节读 `<Path>{roots.workflows}/learning/G-goal/references/lead-orchestration.md</Path>` 与 `<Path>{roots.workflows}/learning/G-goal/references/mine-unit.md</Path>`。
4
4
 
5
5
  ## 计划会话允许跟随的合同
6
6
 
7
7
  - `I-init-setup`:没有状态时写空骨架
8
8
  - `A-assess-and-plan`:写出或核对 `course.md`、background、baseline、sources、Change INDEX
9
+ - 可选只读库存员:产出 C4 与函数分组,不写状态
9
10
 
10
- 不得跟随:`L-lesson`、`Q-question`、`H-homework`、`R-review`、`C-consolidate`、`A-archive`。
11
+ 不得跟随:`L-lesson`、`Q-question`、`H-homework`、`R-review`、`C-consolidate`、`A-archive`。不得写 `lessons/` 或 `goal/probes/`。
11
12
 
12
13
  ## `/goal` 会话允许跟随的合同
13
14
 
14
- - `L-lesson`:写 `lessons/L-*.md`
15
- - `socratic-questioning` 且 `audience=mine`:写 `goal/probes/GP-*.md`
15
+ - `L-lesson`:写 `lessons/L-*.md`(阶段 T,每课一个 writer)
16
+ - `socratic-questioning` 且 `audience=mine`:写 `goal/probes/GP-*.md`(阶段 M,每课一个 miner)
16
17
 
17
18
  不得跟随:`Q-question` 的 `inquiry/` Response 协议、H/R/C/A-archive。
18
19
 
19
20
  ## 所有权
20
21
 
21
- 派单不是改写所有权。A 仍拥有 course 地图,L 仍拥有 lessons,G 拥有 `goal/`,Q 拥有 `inquiry/`。
22
+ 派单不是改写所有权。A 仍拥有 course 地图,L 仍拥有 lessons,G 拥有 `goal/`,Q 拥有 `inquiry/`。矩阵、chain、progress、verify、`lessons/INDEX` 只有 Lead 可写。
22
23
 
23
24
  ## 并发
24
25
 
25
- 默认 current、严格串行。同一时间只推进一个 chain 节点。Wave 不是并发授权。
26
+ Lead 扇出 + 写集隔离。只有写集不相交才允许并行。同一 mine unit ≤15 节。Wave 不是并发授权,也不是挖掘门。禁止「同一时间只推进一个 chain 节点」的旧交错循环;禁止因为宿主没有 team 就退回 `L → mine → L → mine`。
@@ -4,15 +4,15 @@
4
4
 
5
5
  ## plan(默认)
6
6
 
7
- 写出完整 Goal-Plan、Chain、覆盖矩阵和进度骨架,并可跟随 A 合同写 `course.md` / background / baseline / sources。不写 `lessons/`,不写 `goal/probes/`,不提问学习者。`ready_for_execution: false`。计划正文不授权执行。
7
+ 写出完整 Goal-Plan、Chain、覆盖矩阵和进度骨架,并可跟随 A 合同写 `course.md` / background / baseline / sources。按库存切出 mine unit 列表(每个 ≤15 节,小项目允许远小于 15)。不写 `lessons/`,不写 `goal/probes/`,不提问学习者,不预写探针题目或深化讲义。`ready_for_execution: false`。计划正文不授权执行。
8
8
 
9
9
  ## replan
10
10
 
11
- 源码、范围或用户目标变化时重编译。旧 `goal-plan.md` 移入 `goal/revisions/REV-<NNN>.md` 只读。已写成且仍覆盖有效格子的 Lesson 不删除;失效格子改 `deferred` 或重新入链。
11
+ 源码、范围或用户目标变化时重编译。旧 `goal-plan.md` 移入 `goal/revisions/REV-<NNN>.md` 只读。已写成且仍覆盖有效格子的 Lesson 不删除;失效格子改 `deferred` 或重新入链。mine unit 按新库存重切,仍硬顶 15。
12
12
 
13
13
  ## run / resume
14
14
 
15
- 不由本 Work 在激活会话内执行。执行器是用户的外部 `/goal`,合同见 `<Path>{roots.workflows}/learning/G-goal/references/external-goal-runner.md</Path>` 与当前 `goal-plan.md` §5 / §8。
15
+ 不由本 Work 在激活会话内执行。执行器是用户的外部 `/goal`,合同见 `<Path>{roots.workflows}/learning/G-goal/references/external-goal-runner.md</Path>` 与当前 `goal-plan.md` §5 / §8。运行形状是四段:P 已由计划会话完成;`/goal` 对每个 mine unit 走 T(写完全部课)→ M(按课提问)→ D(Lead 深化或拆课),再进入下一 unit。按项目切 unit,硬顶 15
16
16
 
17
17
  ## verify
18
18
 
@@ -1,15 +1,19 @@
1
1
  ---
2
2
  artifact: learning-goal-probe
3
- probe_id: GP-<NNN>-wave-<NN>
3
+ probe_id: GP-<lesson>-b0N
4
4
  lesson_id: L-<NNN>-<slug>
5
5
  audience: mine
6
- wave: <NN>
6
+ batch: b01
7
+ question_budget_used: 0
8
+ split_depth: 0
7
9
  status: open
8
10
  ---
9
11
 
10
- # Probe GP-<NNN> / Wave <NN>
12
+ # Probe GP-<lesson> / b0N
11
13
 
12
- 审问对象是已写成的 Lesson 与源码,不是学习者。禁止 `inquiry/`、`Response:`、`Submission:`、`verdict`、mastered
14
+ 审问对象是已写成的 Lesson 与源码,不是学习者。禁止 `inquiry/`、`Response:`、`Submission:`、`verdict`、mastered。`b01` 承载 Q1–Q5;`b02` 承载 Q6–Q10。禁止 `b03`。miner 不得写 `lessons/`。
15
+
16
+ `question_budget_used` 按 `lesson_id` 累计。`re-dispatch-L` 不重置该课预算。
13
17
 
14
18
  ## Q1 澄清
15
19
 
@@ -29,15 +33,30 @@ status: open
29
33
 
30
34
  ## Q5 元问题
31
35
 
32
- `mine-more` | `defer` | `re-dispatch-L`,原因:
36
+ `mine-more` | `defer` | `re-dispatch-L` | `split-lesson`,原因:
37
+
38
+ (`b02` 的元问题是 Q10,选项相同。满 10 问后 `mine-more` 非法,只允许 `split-lesson` / `defer` / `re-dispatch-L`。)
33
39
 
34
40
  ## 缺口清单
35
41
 
36
- | 格子 | 缺口 | 建议补丁 | 处置 |
42
+ | 格子 | 缺口 | 建议补丁 | 处置建议 |
43
+ | --- | --- | --- | --- |
44
+ | | | | mine-more / defer / re-dispatch-L / split-lesson |
45
+
46
+ Miner 只填表,不选择最终处置。
47
+
48
+ ## split-proposal
49
+
50
+ 10 问后仍不清,或操作定义触发时填写;否则写 `none`。
51
+
52
+ | 原 lesson_id | 建议新课标题 | 从原课拿走的格子 | 为什么 10 问不够 |
37
53
  | --- | --- | --- | --- |
38
- | | | | mine-more / defer / re-dispatch-L |
54
+ | | | | |
55
+
56
+ Lead 裁决:新课进当前或下一 mine unit;当前 unit 加进去会超过 15 则开新 unit。不得调用 `C-consolidate` 或 `relocate-learning.mjs`。
39
57
 
40
58
  ## 关闭
41
59
 
42
- - 状态:`open` | `patched` | `deferred` | `closed`
43
- - 是否改变了至少一个矩阵格子:
60
+ - 状态:`open` | `patched` | `deferred` | `split_proposed` | `closed`
61
+ - `question_budget_used`:
62
+ - 是否改变了至少一个矩阵格子,或显式 `defer` / `split-proposal`:
@@ -1,14 +1,18 @@
1
1
  # Progress: <主题>
2
2
 
3
- 只记录可核对事实。不准写主观百分比。
3
+ 只记录可核对事实。不准写主观百分比。当前 mine unit 必须全部 L 先进入 `written`,再进入 `mined`。
4
4
 
5
- | 时间 | Wave | Gate | 写入文件 | 矩阵变化 | Blocker |
6
- | --- | --- | --- | --- | --- | --- |
7
- | | 0 | G0 | goal/goal-plan.md | 骨架已建 | |
5
+ | 时间 | mine_unit | Wave | Gate | 写入文件 | 矩阵变化 | Blocker |
6
+ | --- | --- | --- | --- | --- | --- | --- |
7
+ | | U1 | 0 | G0 | goal/goal-plan.md | 骨架已建 | |
8
8
 
9
9
  ## 当前
10
10
 
11
- - current_wave:
11
+ - current_mine_unit:
12
+ - current_phase: `T` | `M` | `D` | `idle`
13
+ - 本单元课数:
14
+ - question_budget_used:(按 lesson_id 累计,re-dispatch-L 不重置)
15
+ - active_subagents:
12
16
  - 仍 uncovered:
13
17
  - 下一 ready 节点:
14
18
  - 最近 probe:
@@ -19,9 +19,14 @@ v1 只实现编程四轴。其他领域可同构为 units / properties / structu
19
19
  - Extended Abstract 不是完成条件
20
20
  - 不改变矩阵格子的 Lesson 或 probe 不要写
21
21
 
22
- ## Wave
22
+ ## Wave(内容分组,不是挖掘门)
23
23
 
24
24
  - Wave 1:Context + Container + 主数据流
25
25
  - Wave 2:范围内 Code / 方法性状
26
- - 每波 ≤8 课或 1 个容器/模块,取小
26
+ - Wave 只按覆盖轴分组,不决定何时挖,也不是并发授权
27
27
  - 默认最多 2 波
28
+ - 3 课的波不必撑到 15;单波若超过 15,只把挖掘切开,不把覆盖波扩成配额
29
+
30
+ ## 挖掘扇出
31
+
32
+ 挖掘扇出上界是 `mine_unit_cap=15`,不是必须凑满。4 节的小项目按 4 节挖。切分规则读 `<Path>{roots.workflows}/learning/G-goal/references/mine-unit.md</Path>`。
@@ -6,7 +6,7 @@
6
6
 
7
7
  执行器是用户自己的 AI CLI `/goal`,不是 Learning Work 会话内的自动编排。`plan` 写出的文档不构成执行授权。`ready_for_execution: false` 一直保持到用户把计划交给 `/goal`。计划正文里的「允许」不构成 `/goal` 之外的额外授权。
8
8
 
9
- 详细操作手册是当前 Change 的 `goal/goal-plan.md`。本文件只固定执行器边界。
9
+ 详细操作手册是当前 Change 的 `goal/goal-plan.md`。本文件只固定执行器边界。Lead 编排读 `<Path>{roots.workflows}/learning/G-goal/references/lead-orchestration.md</Path>`。
10
10
 
11
11
  ## 启动
12
12
 
@@ -17,10 +17,12 @@
17
17
 
18
18
  ## 允许跟随
19
19
 
20
- - `<Path>{roots.workflows}/learning/L-lesson/L-lesson.md</Path>`:写 `lessons/L-*.md`
21
- - `<Path>{roots.workflows}/learning/common/skills/socratic-questioning/SKILL.md</Path>` 且 `audience=mine`:写 `goal/probes/GP-*.md`
20
+ - `<Path>{roots.workflows}/learning/L-lesson/L-lesson.md</Path>`:写 `lessons/L-*.md`(阶段 T,每课一个 L 子代理)
21
+ - `<Path>{roots.workflows}/learning/common/skills/socratic-questioning/SKILL.md</Path>` 且 `audience=mine`:写 `goal/probes/GP-*.md`(阶段 M,每课一个 miner)
22
22
 
23
- 派单不改所有权。A 仍拥有 course 地图,L 仍拥有 lessons,G 拥有 `goal/`,Q 拥有 `inquiry/`。
23
+ 必须加载 socratic-questioning SKILL,路径固定为 `{roots.workflows}/learning/common/skills/socratic-questioning/SKILL.md`。不得另造配方。
24
+
25
+ 派单不改所有权。A 仍拥有 course 地图,L 仍拥有 lessons,G 拥有 `goal/`,Q 拥有 `inquiry/`。矩阵、chain、progress、verify、`lessons/INDEX` 只有 Lead 可写。
24
26
 
25
27
  ## 禁止
26
28
 
@@ -31,12 +33,17 @@
31
33
  - 把 mine 写成学习者问答课
32
34
  - 为不改变矩阵格子的主题另开课
33
35
  - 发明库存里不存在的函数、容器或数据流
36
+ - 阶段 T 完成前启动 miner
37
+ - `L → mine → L → mine` 交错
38
+ - 同一 `lesson_id` 第 11 问或 `b03`
39
+ - miner 写入 `lessons/` 或 matrix
40
+ - 同一 mine unit 超过 15 节
34
41
 
35
42
  ## 循环
36
43
 
37
- 一节课 一次 mine 更新矩阵与 `progress.md`。默认 current、严格串行。同一时间只推进一个 chain 节点。Wave 不是并发授权。
44
+ 当前 mine unit 全部 L(阶段 T)→ 按课 mine(阶段 M)→ Lead 处置(阶段 D)→ 下一 unit。禁止交错。并行只发生在写集不相交的子代理之间。宿主没有 team 时按相同派单包顺序执行,合同不降级。
38
45
 
39
- 处置只允许:`mine-more` | `defer(reason)` | `re-dispatch-L`。
46
+ 处置只允许:`re-dispatch-L` | `split-lesson` | `defer(reason)`。`mine-more` 只允许在该课尚未满 10 问、且 batch-01 已证明存在真实矩阵缺口时打开 `b02`。满 10 问后 `mine-more` 非法。
40
47
 
41
48
  ## 停止
42
49
 
@@ -0,0 +1,52 @@
1
+ # Lead 编排与派单(Learning `/goal`)
2
+
3
+ 本文件只在外部 `/goal` 执行 Goal-Plan,或 G-goal `plan` / `replan` 需要派只读库存员 / A 子代理时读取。不引入 SpecDev 的 Ticket / worktree / Evidence 协议。并发单位是「一个 Lesson 文件」或「一份 probe 文件」。
4
+
5
+ 抄 SpecDev `P-goal-plan` 的 lead-directed:主会话是唯一 Lead。Learning 不把 Lead 写进 Ticket;Lead 是当前 `/goal`(或 plan)主会话。
6
+
7
+ ## 角色与写集
8
+
9
+ | 角色 | 何时出现 | 可以写 | 禁止写 | 必须读取 |
10
+ | --- | --- | --- | --- | --- |
11
+ | Lead(`/goal` 主会话,或 plan 主会话) | 始终 | `goal-plan` / `chain` / `matrix` / `progress` / `verify` / `lessons/INDEX` / `learning-log` / `.status.json` | 不得代替 L 写讲义正文,不得代替 miner 编造探针 | 已打开的 `workspace.json`、Goal-Plan §0/§5/§7/§8、`<Path>{roots.workflows}/learning/G-goal/references/stop-rules.md</Path>`、本文件、`<Path>{roots.workflows}/learning/G-goal/references/mine-unit.md</Path>`、`<Path>{roots.workflows}/learning/G-goal/references/split-rules.md</Path>` |
12
+ | 库存员(只读) | plan 可选 | 无 | 任何状态与知识文件 | 目标仓库源码,产出 C4 与函数分组给 Lead |
13
+ | A 子代理 | plan 且尚无 `course.md` | `course.md`、background、baseline、sources、Change INDEX | `goal/`、`lessons/`、`probes/` | `A-assess-and-plan` 合同 |
14
+ | L 子代理 | `/goal` 阶段 T,每课一个 | 仅被分配的 `lessons/L-<NNN>-<slug>.md` | 其他 L 文件、INDEX、`goal/`、`inquiry/` | `L-lesson.md` + teaching-policy + 该课 OBJ 与矩阵行 |
15
+ | Miner 子代理 | `/goal` 阶段 M,每课一个 | 仅 `GP-<lesson>-b01.md` 与(若仍有预算)`b02.md` | `lessons/`、chain、matrix、`inquiry/`、其他课的 probe | 下面这条 skill 路径 + questioning-policy + 该课 Lesson + 源码锚点 + 该课声称覆盖的矩阵行 |
16
+
17
+ Miner 必须加载的 skill 路径(不得另造配方):
18
+
19
+ ```text
20
+ {roots.workflows}/learning/common/skills/socratic-questioning/SKILL.md
21
+ ```
22
+
23
+ ## 派单包最低字段
24
+
25
+ 每次派单必须包含:`lesson_id` 或 change 范围、只读路径、唯一可写路径、允许动作、停止条件、返回格式。Miner 包还必须包含该课声称覆盖的矩阵行,以及上述 skill 合同路径。`audience` 固定为 `mine`。
26
+
27
+ L 返回至少:`lesson_id`、写入路径、是否满足 L 合同章节、声称覆盖的格子。Miner 返回至少:`lesson_id`、`probe_id`、`question_budget_used`、缺口表、Q5/Q10 建议(`mine-more` | `defer` | `re-dispatch-L` | `split-lesson`)、可选 `split-proposal`。Miner 只填表,不选择处置。
28
+
29
+ ## 并发边界
30
+
31
+ - `learning_agent_limit` 默认 4,硬顶为 `min(本单元课数, 15, 宿主能力)`。Lead 不计入。
32
+ - 只有写集不相交才允许并行。两个 L 子代理不得写同一文件。两个 miner 不得写同一 GP 文件。
33
+ - 跨 mine unit 不得同时写课或同时挖掘。
34
+ - 矩阵、chain、progress、verify、`lessons/INDEX` 只有 Lead 可写。
35
+ - 矩阵冲突只暂停受影响格子;其他课的 T/M 继续。不得抢锁、清空或覆盖他人写集。
36
+
37
+ ## 无宿主 team 时的顺序回退
38
+
39
+ 宿主没有 subagent / agent team:Lead 按相同派单包顺序执行,合同不变。禁止因为不能并行就退回 `L → mine → L → mine` 交错。阶段 T 结束之前禁止任何 miner 启动。
40
+
41
+ ## 计划会话
42
+
43
+ G-goal `plan` / `replan` 允许 Lead 派只读库存员,以及在没有 `course.md` 时派 A 子代理。Lead 自己写 `goal/*` 骨架,并按 mine-unit 规则切单元。计划会话仍然禁止写 `lessons/` 与 `goal/probes/`。
44
+
45
+ ## HARD NO
46
+
47
+ - 不在阶段 T 完成前启动 miner
48
+ - 不交错 L 与 mine
49
+ - miner 不得写 `lessons/` 或 matrix
50
+ - miner 不得选择处置
51
+ - 同一 mine unit 不得超过 15 节
52
+ - 宿主没有 team 时,不得借机退回旧交错循环
@@ -0,0 +1,49 @@
1
+ # 挖掘单元(mine unit)
2
+
3
+ 覆盖波次(Wave 1 架构 / Wave 2 API)描述「学什么」,不单独决定「何时挖」。真正启动挖掘的单位叫 **mine unit**。
4
+
5
+ ## Frontmatter
6
+
7
+ ```yaml
8
+ mine_unit: project # 按项目切分,不再写死 wave | goal
9
+ mine_unit_cap: 15 # 硬顶
10
+ learning_agent_limit: 4 # 默认并行度;硬顶 min(4, 本单元课数, 宿主能力)
11
+ ```
12
+
13
+ `wave_cap` 不再当作挖掘门。Wave 只保留为覆盖尺上的内容分组。
14
+
15
+ ## 怎么切
16
+
17
+ plan 阶段做编程库存之后,用下面规则切 mine unit(写入 `goal/chain.md` 的 Units 表,不要另造权威文件):
18
+
19
+ 1. 先按覆盖波分组:Wave 1 = C4 Context + Container + 主/失败数据流;Wave 2 = 范围内公开 API 与方法性状。
20
+ 2. 再按模块 / 容器切课。琐碎 helper、生成代码、测试夹具标 `covered-by-parent` 或 `deferred`,不为它们开课。
21
+ 3. 把课按依赖序装进 mine unit:
22
+ - 同一单元内课数 ≤ 15。
23
+ - 尽量不把有前置依赖的课和它的前置拆到两个尚未挖掘的单元里;若必须拆,后一单元的 T 要等前一单元 D 结束(或至少等前置课 `mined` / `revised`)。
24
+ - 一课不得同时属于两个 unit。
25
+ - 前置课尚未 `written` / `mined` / `revised` 的课,不得提前塞进当前单元。
26
+ 4. 估算课数在 plan 时写进 chain。`/goal` 不得为了凑单元而加灌水课,也不得把已规划的第 16 节塞进当前单元。
27
+
28
+ 规则:
29
+
30
+ 1. 若估算课数 **≤ 15**:整个 Goal 就是一个 mine unit。先写完全部课,再按课扇出 miner。
31
+ 2. 若估算课数 **> 15**:按模块 / 容器切成连续单元,**每个单元 ≤ 15 节**。写完一个单元的全部课 → 挖这一个单元 → 再写下一个单元。
32
+ 3. 任何 mine unit 不得超过 15 节。校验器对「同一 mine unit 规划超过 15 节」报错。
33
+ 4. **15 是扇出上界,不是配额,不是必须凑满。** 4 节的小项目按 4 节挖,不要为了填满 15 而灌水开课。
34
+ 5. 3 课的波不必撑到 15;单波若超过 15,只把挖掘切开,不把覆盖波扩成配额。
35
+
36
+ ## 与 Wave 的关系
37
+
38
+ Wave 1 / Wave 2 仍按覆盖轴分组。挖掘扇出上界是 `mine_unit_cap=15`,不是必须凑满,也不是 Wave 配额。若某一覆盖波本身超过 15 节,必须先拆成多个 mine unit,不得整波一次扇出超过 15 个 miner。
39
+
40
+ ## 示例
41
+
42
+ - 6 节的小库:1 个 unit,写完 6 节再 6 路挖掘(受 `learning_agent_limit` 限制,默认同时 4 个,其余排队)。
43
+ - 11 节:1 个 unit。
44
+ - 18 节:例如 Unit A 10 节 + Unit B 8 节,不得 18 节一次挖。
45
+ - 某容器公开 API 单独就超过 15 节:说明课图过粗或范围过大。plan 阶段就应拆课或把一部分标 `deferred`,而不是把 16+ 节装进一个 unit。
46
+
47
+ ## `/goal` 循环
48
+
49
+ 当前 mine unit:阶段 T 写完全部 L → 阶段 M 按课 mine → 阶段 D Lead 处置 → 下一 unit。禁止 `L → mine → L → mine` 交错。当前 mine unit 课数不得超过 15。超过则停止,回到 G-goal `replan` 重新切单元。
@@ -0,0 +1,54 @@
1
+ # 拆课规则(split-lesson)
2
+
3
+ 每课最多 10 问。10 问仍说不清,说明课拆得不够,应继续拆课而不是继续问。`split-lesson` 不是 `C-consolidate`。
4
+
5
+ ## 计数
6
+
7
+ - 计数键是 `lesson_id`,不是 wave,不是 Change,不是 mine unit。
8
+ - 文件:`goal/probes/GP-<lesson>-b01.md` 承载 Q1–Q5;`GP-<lesson>-b02.md` 承载 Q6–Q10。
9
+ - 两批都必须走 socratic-questioning skill 的 5 槽(澄清 / 证据 / 反例 / 视角 / 元问题)。`b02` 只把审问对象挖深,不改槽位,不改 `audience=mine` 协议。
10
+ - 第 11 问非法。出现 `b03` 非法。满 10 后 `mine-more` 非法,只走 `split-lesson`。
11
+ - `re-dispatch-L` 改写同一课,**不重置**该课 `question_budget_used`。已经问过的题仍算在原课上。
12
+ - split 出的新课各自获得新的 10 问预算。原课收窄覆盖后,剩余预算冻结,不再追加问题。
13
+ - `validate-learning` 按 `lesson_id` 统计 Q 数,`>10` 报错;出现 `b03` 报错。
14
+
15
+ ## 「10 问仍解释不清」的操作定义
16
+
17
+ 满足任一即可由 miner 填写 `split-proposal`(Lead 裁决):
18
+
19
+ 1. 该课声称覆盖的矩阵格子仍有 `uncovered`,且 `b02` 的 Q5 / Q10 仍建议 `mine-more`。
20
+ 2. 合格读者读完 Lesson + 两批 probe,仍说不出该课声称覆盖的函数目的、主路径或失败路径、或方法副作用。
21
+ 3. 若把缺口继续写进同一课,`time_budget` 将超过 40 分钟(违反 L 合同)。
22
+
23
+ 不触发 split:
24
+
25
+ - 只是缺 L 合同章节(直觉 / 机制 / 边界 / 例子 / 文字等价物)→ `re-dispatch-L`,同一课补丁。
26
+ - 格子可以诚实标 `deferred(reason)` → `defer`,不再提问。
27
+ - 格子本就该 `covered-by-parent` → 改矩阵,不开新课。
28
+
29
+ ## `re-dispatch-L` 与 split 的分界
30
+
31
+ | 证据 | Lead 处置 | 谁执笔 |
32
+ | --- | --- | --- |
33
+ | 同一 OBJ,缺动机 / 机制 / 边界 / 例子 / 文字等价物 | `re-dispatch-L` | 原课的 L 子代理补丁 `lessons/L-*.md` |
34
+ | 一课声称覆盖超过 1 个容器,或 10 问后仍有解不开的函数簇 | `split-lesson` | Lead 改 chain;新 L 子代理写新课 |
35
+ | 源码里没有该符号,或超出 Scope | `defer(reason)` | Lead 改矩阵 |
36
+ | 需要改 `course.md` OBJ | 停止并 `replan` | 用户激活 G-goal + A |
37
+ | 当前 unit 加新课后将超过 15 节 | 新课进下一 unit,本 unit 先收束 | Lead |
38
+
39
+ Miner 只填表,不选择处置。
40
+
41
+ ## `split-lesson` 不是整合
42
+
43
+ - Miner 不得改 `course.md`,不得改 `chain.md`,不得写 `lessons/`。它只在 probe 里填写 `split-proposal`:建议新课标题、从原课拿走哪些格子、为什么 10 问不够。
44
+ - Lead 若判断 OBJ 不变、只是课粒度粗:可在 `chain.md` 追加子节点。新课优先进入**当前或下一个 mine unit**;若当前 unit 加进去会超过 15 节,必须开新 unit,不得把第 16 节塞进当前扇出。
45
+ - Lead 若判断必须改 `course.md` 的 OBJ:停止,要求用户激活 G-goal `replan`(跟随 A)。`/goal` 不得偷偷改写 A 的目标。
46
+ - max split depth = 2。超过则 `defer(reason=needs-replan)`,写 verify 草稿,回到 G-goal `replan`。
47
+ - 禁止把 split 做成 `C-consolidate`,禁止搬到 `children/`,禁止写 `synthesis/` 或 `context/`,禁止调用 `relocate-learning.mjs`。
48
+
49
+ ## HARD NO
50
+
51
+ - 同一 `lesson_id` 第 11 问非法
52
+ - 满 10 问后禁止 `mine-more`
53
+ - `split-lesson` 不得调用 `C-consolidate` 或 `relocate-learning.mjs`
54
+ - 当前 unit 加新课会超过 15 时,新课进入下一 unit
@@ -4,15 +4,19 @@
4
4
 
5
5
  ## 完成即停
6
6
 
7
- 当范围内格子全部为 `covered`、`deferred(reason)` 或 `covered-by-parent`,且每个 chain 节点有 L 合同 Lesson,且每节新课或改写课有对应 probe,且 `goal/verify.md` 已写,则 Goal 完成。
7
+ 当范围内格子全部为 `covered`、`deferred(reason)` 或 `covered-by-parent`,且每个 chain 节点有 L 合同 Lesson,且每节新课或改写课有对应 probe(每课 ≤10 问),且 `goal/verify.md` 已写,则 Goal 完成。
8
8
 
9
9
  ## 过程即停(任一即停)
10
10
 
11
- - 本波没有矩阵格子发生变化
12
- - 已达到 `wave_cap` `max_waves`
11
+ - 本 mine unit 没有矩阵格子发生变化
12
+ - 出现第 11 问,或 `GP-*-b03.md`
13
+ - 交错 L/mine(阶段 T 完成前启动 miner,或 `L → mine → L`)
14
+ - miner 写入 `lessons/` 或 matrix
15
+ - 同一 mine unit 规划或扇出超过 15 节
13
16
  - 用户暂停
14
17
  - 缺源码、缺 baseline,或目标主要是无法核对的生成物
15
18
  - 所有权冲突或路径越界
19
+ - split depth=2 后仍不清:`defer(needs-replan)`,写 verify 草稿,回到 G-goal `replan`
16
20
 
17
21
  ## 不算完成
18
22
 
@@ -20,3 +24,4 @@
20
24
  - 文件巡览结束
21
25
  - 模型表示「已经理解」
22
26
  - 主观百分比
27
+ - 阶段 T 结束但尚未 mine
@@ -3,7 +3,7 @@ id: learning
3
3
  type: workflow
4
4
  workflow: learning
5
5
  name: Learning Workflow
6
- description: 以完整、通俗、多表示的课程,苏格拉底问答课、目标模式 Goal-Plan 编译、单文件作业评审、可选延迟复习和带溯源的主题综合,持续建立个人 Markdown 知识库。
6
+ description: 以完整、通俗、多表示的课程,苏格拉底问答课、目标模式 Goal-Plan 编译(先教后挖)、单文件作业评审、可选延迟复习和带溯源的主题综合,持续建立个人 Markdown 知识库。
7
7
  keywords: [learning, 学习, 教学, 苏格拉底, 反问, goal, 目标模式, 作业, 复习, 综合, 知识, eli5]
8
8
  ---
9
9
 
@@ -11,7 +11,7 @@
11
11
  - **A-archive** — 冷归档:用户明确关闭后移动整个 Change 树到日期目录;不做知识综合或掌握判断。
12
12
  - **A-assess-and-plan** — 评估背景并设计课程:以目标和证据为起点建立课程、背景、基线、来源和可变 Lesson 地图。
13
13
  - **C-consolidate** — 主题整合:将选定 Change 物理嵌入父 Change,生成带 claim 级 provenance 的可迭代主题综合。
14
- - **G-goal** — 目标学习(目标模式):为选定编程项目编译一份可被外部 /goal 执行的完整 Goal-Plan;计划会话可跟随 A 写出课程地图,但不写 Lesson、不向学习者提问、不自动串联 H/R/C。
14
+ - **G-goal** — 目标学习(目标模式):为选定编程项目编译一份可被外部 /goal 执行的完整 Goal-Plan;按项目切 ≤15 节的 mine unit,先写齐再按课派 miner。计划会话可跟随 A 写出课程地图,但不写 Lesson、不向学习者提问、不自动串联 H/R/C。
15
15
  - **H-homework** — 课程作业与评审:以单一 Markdown 文件生成题目、接收显式提交并追加逐题评审;不与 Lesson 混写。
16
16
  - **I-init-setup** — 初始化学习系统:初始化 Learning v2 的教学偏好、空索引、位置登记和可验证状态。
17
17
  - **L-lesson** — 完整课程讲解:一次输出 30–40 分钟、通俗但完整的 Lesson;不生成作业、不评分、不宣称掌握。
@@ -38,13 +38,13 @@ I-init-setup -> A-assess-and-plan -> (user chooses) L-lesson
38
38
  |\
39
39
  +-> Q-question (inquiry/)
40
40
  |\
41
- +-> G-goal (goal/ Goal-Plan; later /goal may L then mine)
41
+ +-> G-goal (goal/ Goal-Plan; later /goal: mine-unit T then M then D)
42
42
 
43
43
  Any active or closed changes --(user chooses C)--> consolidation parent
44
44
  Any closed root tree --(user chooses A)--> archive/YYYY-MM/<change>
45
45
  ```
46
46
 
47
- `L` 完成后只报告已生成的 Lesson 和可选的下一步,不自动激活 `H`;`H` 评审后可结束本轮,不自动激活 `R`、`C` 或 `A`。`Q-question` 不自动激活 L/H/R,也不写入 `lessons/` 或 `homework/`。`G-goal` 激活只编译 Goal-Plan 并停止,不写 `lessons/` 或 `goal/probes/`;用户稍后用外部 `/goal` 才可派单 L 然后 socratic-questioning `audience=mine`。`C` 和 `A` 都需要用户明确确认,未确认的 dry-run 不得移动或写入 context。
47
+ `L` 完成后只报告已生成的 Lesson 和可选的下一步,不自动激活 `H`;`H` 评审后可结束本轮,不自动激活 `R`、`C` 或 `A`。`Q-question` 不自动激活 L/H/R,也不写入 `lessons/` 或 `homework/`。`G-goal` 激活只编译 Goal-Plan 并停止,不写 `lessons/` 或 `goal/probes/`;用户稍后用外部 `/goal` 按项目切出不超过 15 节的 mine unit,先写齐该单元全部课,再按课派 miner(socratic-questioning `audience=mine`)。不自动串联 H/R/C/A。`C` 和 `A` 都需要用户明确确认,未确认的 dry-run 不得移动或写入 context。
48
48
 
49
49
  ## 启动协议
50
50
 
@@ -77,7 +77,7 @@ changes/<change-id>/
77
77
  .status.json
78
78
  ```
79
79
 
80
- `inquiry/` 由 `Q-question` 拥有;`goal/` 由 `G-goal` 拥有。计划会话只写 Goal-Plan 骨架(`goal-plan.md` / `chain.md` / `coverage-matrix.md` / `progress.md`),不写 `goal/probes/` 或 `lessons/` 正文。`/goal` 可更新矩阵与 progress 并写入 probes/verify;不得写入 `inquiry/`。
80
+ `inquiry/` 由 `Q-question` 拥有;`goal/` 由 `G-goal` 拥有。计划会话只写 Goal-Plan 骨架(`goal-plan.md` / `chain.md` / `coverage-matrix.md` / `progress.md` 与 mine unit 切分),不写 `goal/probes/` 或 `lessons/` 正文。`/goal` Lead 可更新矩阵与 progress 并写入 probes/verify;L 子代理只写被分配的 Lesson;miner 只写自己的 `GP-*-b0N`;不得写入 `inquiry/`。
81
81
 
82
82
  综合父 Change 使用:
83
83
 
@@ -119,7 +119,7 @@ Workflow 自身只读模板;Change 内容只写当前 Change 或其 `children/
119
119
 
120
120
  `expression_level=eli5|plain` 只控制词汇、句法、脚手架和类比比例;`coverage_depth=overview|standard|deep` 控制覆盖强度。Lesson 可放非评分的 pause/self-check,但不得生成 Q/A、答案、分数、verdict 或 mastered 字段。外部图片只是可选增强,必须有 alt、caption、source、访问日期和文字等价物,课程不能依赖链接可用性。
121
121
 
122
- 学习者苏格拉底批次只允许出现在 `Q-question` 拥有的 `inquiry/` 内。`G-goal` 的 `audience=mine` probes 写入 `goal/probes/`,不是 Lesson Q/A,且不占用 30–40 分钟 Lesson 预算。
122
+ 学习者苏格拉底批次只允许出现在 `Q-question` 拥有的 `inquiry/` 内。`G-goal` 的 `audience=mine` probes 写入 `goal/probes/`,不是 Lesson Q/A,且不占用 30–40 分钟 Lesson 预算。每课最多 10 问(两批 5 槽);满 10 后只拆课,不续问。
123
123
 
124
124
  ## Homework 合同
125
125
 
@@ -3,13 +3,13 @@
3
3
  | 工件 | Owner | 规则 |
4
4
  | --- | --- | --- |
5
5
  | `course.md`、`background/`、`baseline.md`、`sources.md`、Change `INDEX.md` | A-assess-and-plan | 目标、宏观基础、原始基线、来源和课程地图;实质修改留 revision |
6
- | `lessons/` | L-lesson | 完整 Lesson 和 Lesson INDEX;不写作业答案或掌握结论 |
6
+ | `lessons/` | L-lesson | 完整 Lesson 和 Lesson INDEX;不写作业答案或掌握结论。`/goal` 阶段 T 每课一个 L 子代理只写被分配的 `L-*.md`;`lessons/INDEX` 只有 Lead 可写 |
7
7
  | `homework/` | H-homework + learner | H 写问题/评审,learner 写 A;提交后只追加 Review,旧 attempt 不可改写 |
8
8
  | `inquiry/` | Q-question + learner | Q 写问题/Teaching/Inquiry Lesson,learner 写 A;`Response: ready` 后只追加讲解,旧 batch 不可改写;不写入 `lessons/` 或 `homework/` |
9
- | `goal/` | G-goal | goal-plan.md, chain.md, coverage-matrix.md, progress.md, probes/, verify.md, revisions/; plan session writes plan skeleton only, not probes; /goal may update matrix/progress and write probes/verify; must not write inquiry/, lessons/, homework/, review/, synthesis/, archive/, mastered |
9
+ | `goal/` | G-goal | goal-plan.md, chain.md, coverage-matrix.md, progress.md, probes/, verify.md, revisions/; plan session writes plan skeleton and mine units only, not probes or deepening lessons; `/goal` Lead 独占 matrix/progress/verify/chain 追加;miner 只写自己的 `GP-*-b0N`;must not write inquiry/, lessons/, homework/, review/, synthesis/, archive/, mastered |
10
10
  | `review/` | R-review | 延迟保持题、原始回答、证据和复习日期 |
11
11
  | `children/<id>/` | child Work | 子 Change 原有工件;父只负责根锁、路由、位置登记 |
12
- | `synthesis/`、topic context | C-consolidate | claim 级综合、冲突、空白、provenance 和版本;不得覆盖原料 |
12
+ | `synthesis/`、topic context | C-consolidate | claim 级综合、冲突、空白、provenance 和版本;不得覆盖原料。`split-lesson` 不得调用 C 或 relocate |
13
13
  | `archive/`、locations projection | A-archive | 用户关闭后的物理移动和只读登记;不判断掌握、不写 context |
14
14
 
15
15
  状态 JSON 只是 projection;真实内容、学习者原始回答、评审和 source manifest 是证据权威。
@@ -18,6 +18,10 @@
18
18
 
19
19
  每题标注 `objective_id`、`bloom_level`、`socratic_move`、`expected_evidence`、`difficulty`。数量默认 5,可由用户改,但必须 Q/A 成对连续。
20
20
 
21
+ ## mine 数量上限
22
+
23
+ `audience=mine` 每课最多两批 10 问:`b01` = Q1–Q5,`b02` = Q6–Q10。第 11 问非法。第三批必须返回 `split-proposal` 而不是再出题。满 10 后禁止 `mine-more`。`re-dispatch-L` 不重置该课 `question_budget_used`。tutor 的 `inquiry/` 仍是一批约 5 题,Response 协议不变。
24
+
21
25
  ## 文件协议
22
26
 
23
27
  - 出题文件含空 A 与精确行 `Response: pending`。
@@ -26,6 +30,7 @@
26
30
  - 闭环后 `Response: closed`。重答新建 `IQ-…-batch-NN`,旧文件只读。
27
31
  - 禁止 `Submission:`、禁止 H 的 `verdict: correct|partial|incorrect` 字段名。教学判定用 `aligned|partial|off|uncertain`。
28
32
  - 禁止写入 `lessons/` 或 `homework/`。
33
+ - `audience=mine` 禁止上述 learner 协议字段,也禁止 miner 写 `lessons/`。
29
34
 
30
35
  ## 讲解与 keep-alive
31
36
 
@@ -14,4 +14,4 @@ ASCII、Markdown table、公式或可选外链图片可以组合使用;每个
14
14
 
15
15
  Lesson 可以有非评分 pause/self-check,但不含 Q/A、答案、分数、verdict 或 mastered 字段。
16
16
 
17
- 学习者苏格拉底批次只允许出现在 `Q-question` 拥有的 `inquiry/` 内。`G-goal` 的 `audience=mine` probes 写入 `goal/probes/`,不是 Lesson Q/A,且不占用 30–40 分钟 Lesson 预算。`L-lesson` 仍不得写 Q/A。
17
+ 学习者苏格拉底批次只允许出现在 `Q-question` 拥有的 `inquiry/` 内。`G-goal` 的 `audience=mine` probes 写入 `goal/probes/`,不是 Lesson Q/A,且不占用 30–40 分钟 Lesson 预算。每课最多 10 问。`L-lesson` 仍不得写 Q/A。
@@ -12,7 +12,7 @@ description: 苏格拉底问答、反问授课或用提问教;当学习者卡
12
12
  3. 先判断能否提问:真新手或高元素交互时返回降级探针或「先走 L-lesson」建议,而不是硬出机制题。
13
13
  4. 默认 `socratic` 五槽:澄清、机制证据、假设反例、迁移视角、元问题/下一步。用户指定 `5e-recipe` / `feynman` / `productive-failure` / custom 时只改题目,不改 inquiry 文件协议。
14
14
  5. `audience=tutor` 写入 `inquiry/IQ-*.md`:Q1–Q5、空 A、`Response: pending`。`Response: ready` 后只追加 Teaching 与 Inquiry Lesson;不改写答案,不写 `lessons/`、`homework/`、mastered、`Submission`、`verdict`。
15
- 6. `audience=mine` 写入 `goal/probes/GP-*.md`:只审问已写 Lesson 与源码,不写学习者问答,不写 `Response:`、`Submission:`、verdict、mastered,不写 `inquiry/`。
15
+ 6. `audience=mine` 写入 `goal/probes/GP-*.md`:只审问已写 Lesson 与源码,不写学习者问答,不写 `Response:`、`Submission:`、verdict、mastered,不写 `inquiry/`,不得写 `lessons/`。默认一批 5 问(`b01` = Q1–Q5);每课最多两批 10 问(`b02` = Q6–Q10)。第三批必须返回 `split-proposal` 路由而不是再出题。禁止 `b03`。满 10 后只走 `split-lesson`。
16
16
  7. Teaching 每题含思路复原、`aligned|partial|off|uncertain`、中文详解、`Explain (English)`、纠错、未见知识、来源。Inquiry Lesson 把 5 题收成一课并给 keep-alive 钩子。需要完整 30–40 分钟讲义或正式测评时只返回路由,不自动激活 L/H/R。
17
17
 
18
18
  ## 题槽
@@ -25,7 +25,9 @@ description: 苏格拉底问答、反问授课或用提问教;当学习者卡
25
25
  | Q4 | 迁移/视角 | viewpoints |
26
26
  | Q5 | 元问题/下一步 | question-the-question |
27
27
 
28
+ `audience=mine` 的 `b02` 使用同一 5 槽,编号为 Q6–Q10。不改槽位,不改 `audience=mine` 协议。
29
+
28
30
  ## 完成标准
29
31
 
30
32
  - `audience=tutor`:一批 Q/A 成对;讲解在作答之后;指定教法只改变题目,不改变 Response 协议;
31
- - `audience=mine`:输出可被 `/goal` 直接写入 `goal/probes/GP-*.md`,且不触发 learner 协议字段。
33
+ - `audience=mine`:输出可被 `/goal` 直接写入 `goal/probes/GP-*-b0N.md`,且不触发 learner 协议字段;每课 Q 数 ≤10;第三批只返回 `split-proposal`。
@@ -14,6 +14,13 @@ const OLD_WORKS = new Set(["A-archive-and-consolidate", "E-eli5", "P-practice",
14
14
  const PHASE = new Set(["planning", "teaching", "homework", "review", "consolidating", "closed", "archived"]);
15
15
  const LIFECYCLE = new Set(["active", "blocked", "closed", "archived"]);
16
16
  const EVIDENCE = new Set(["not_attempted", "passed", "needs_review"]);
17
+ const MINE_UNIT_CAP = 15;
18
+ const MAX_LESSON_QUESTIONS = 10;
19
+ const PROBE_BATCH = /^GP-(L-\d{3}(?:-[a-z0-9]+)*)-b0(\d+)\.md$/;
20
+ const QUESTION_HEADING = /^##\s+Q(\d+)\b/gm;
21
+ const OLD_INTERLEAVE = /同一时间只推进一个 chain 节点|L\s*→\s*mine\s*→\s*L|先 L 写课,再 mine 探针/;
22
+ const LEARNER_PROBE = /(?:^|\n)\s*Response:|(?:^|\n)\s*Submission:|\binquiry\//;
23
+ const SOCRATIC_SKILL_PATH = "{roots.workflows}/learning/common/skills/socratic-questioning/SKILL.md";
17
24
 
18
25
  function options(argv) {
19
26
  const result = { workflowRoot: null, stateRoot: null, stage: null, change: null, selfCheck: false };
@@ -98,11 +105,22 @@ function validateWorkflowRoot(root, errors) {
98
105
  ["H-homework/homework-template.md", ["Q1", "A1", "Submission: pending"]],
99
106
  ["Q-question/Q-question.md", ["Response: pending", "Inquiry Lesson", "inquiry/"]],
100
107
  ["Q-question/inquiry-template.md", ["Response: pending", "Q1", "A1"]],
101
- ["G-goal/G-goal.md", ["ready_for_execution", "goal/goal-plan.md", "covered-by-parent"]],
102
- ["G-goal/goal-plan-template.md", ["ready_for_execution: false", "covered-by-parent", "goal/probes"]],
103
- ["G-goal/references/external-goal-runner.md", ["audience=mine", "Q-quiz", "inquiry/"]],
108
+ ["G-goal/G-goal.md", ["ready_for_execution", "goal/goal-plan.md", "covered-by-parent", "mine unit", "mine_unit_cap"]],
109
+ ["G-goal/goal-plan-template.md", ["ready_for_execution: false", "covered-by-parent", "goal/probes", "mine_unit: project", "mine_unit_cap: 15", "learning_agent_limit: 4", "split-lesson", "teach-then-mine"]],
110
+ ["G-goal/chain-template.md", ["Units", "mined", "split_proposed", "split"]],
111
+ ["G-goal/probe-template.md", ["b01", "b02", "question_budget_used", "split-proposal", "split-lesson"]],
112
+ ["G-goal/progress-template.md", ["mine_unit", "question_budget_used", "active_subagents"]],
113
+ ["G-goal/orchestration-protocol.md", ["lead-orchestration.md", "mine-unit.md", "audience=mine"]],
114
+ ["G-goal/planning-modes.md", ["mine unit", "T(写完全部课)"]],
115
+ ["G-goal/references/external-goal-runner.md", ["audience=mine", "Q-quiz", "inquiry/", "socratic-questioning/SKILL.md", "mine unit"]],
116
+ ["G-goal/references/lead-orchestration.md", ["learning_agent_limit", "写集", "只有 Lead 可写", SOCRATIC_SKILL_PATH]],
117
+ ["G-goal/references/mine-unit.md", ["mine_unit_cap", "不是配额", "15"]],
118
+ ["G-goal/references/split-rules.md", ["split-lesson", "question_budget", "C-consolidate", "relocate-learning.mjs"]],
119
+ ["G-goal/references/stop-rules.md", ["第 11 问", "交错 L/mine", "15 节"]],
120
+ ["G-goal/references/coverage-bar.md", ["mine_unit_cap=15", "不是必须凑满"]],
104
121
  ["common/rules/teaching-policy.md", ["coverage_depth", "不以字符数"]],
105
- ["common/rules/questioning-policy.md", ["audience=mine", "goal/probes"]],
122
+ ["common/rules/questioning-policy.md", ["audience=mine", "goal/probes", "最多两批 10 问"]],
123
+ ["common/skills/socratic-questioning/SKILL.md", ["audience=mine", "b02", "split-proposal"]],
106
124
  ];
107
125
  for (const [file, markers] of contracts) {
108
126
  const path = join(root, file);
@@ -110,6 +128,196 @@ function validateWorkflowRoot(root, errors) {
110
128
  const text = readFileSync(path, "utf8");
111
129
  for (const marker of markers) if (!text.includes(marker)) errors.push(`${file}: missing '${marker}'`);
112
130
  }
131
+ const goalPlan = readFileSync(join(root, "G-goal/goal-plan-template.md"), "utf8");
132
+ if (OLD_INTERLEAVE.test(goalPlan) && !goalPlan.includes("禁止 L → mine → L → mine")) {
133
+ errors.push("G-goal/goal-plan-template.md: old interleave iteration policy remains");
134
+ }
135
+ const orch = readFileSync(join(root, "G-goal/orchestration-protocol.md"), "utf8");
136
+ if (orch.includes("默认 current、严格串行") || (orch.includes("同一时间只推进一个 chain 节点") && !orch.includes("禁止"))) {
137
+ errors.push("G-goal/orchestration-protocol.md: old serial one-node recipe remains");
138
+ }
139
+ }
140
+
141
+ function markdownTables(text) {
142
+ const tables = [];
143
+ const lines = String(text).split(/\r?\n/);
144
+ let i = 0;
145
+ while (i < lines.length) {
146
+ if (/^\|.+\|$/.test(lines[i]) && i + 1 < lines.length && /^\|\s*[-:| ]+\|$/.test(lines[i + 1])) {
147
+ const header = lines[i].split("|").slice(1, -1).map((cell) => cell.trim());
148
+ i += 2;
149
+ const rows = [];
150
+ while (i < lines.length && /^\|.+\|$/.test(lines[i]) && !/^\|\s*[-:| ]+\|$/.test(lines[i])) {
151
+ rows.push(lines[i].split("|").slice(1, -1).map((cell) => cell.trim()));
152
+ i += 1;
153
+ }
154
+ tables.push({ header, rows });
155
+ continue;
156
+ }
157
+ i += 1;
158
+ }
159
+ return tables;
160
+ }
161
+
162
+ function tableColumn(table, names) {
163
+ const index = table.header.findIndex((cell) => names.some((name) => cell.toLowerCase() === name.toLowerCase()));
164
+ return index;
165
+ }
166
+
167
+ function lessonIdsIn(text) {
168
+ return [...String(text).matchAll(/\bL-\d{3}(?:-[a-z0-9]+)*\b/g)].map((match) => match[0]);
169
+ }
170
+
171
+ function filledSplitProposal(text) {
172
+ const section = /##\s+split-proposal\b([\s\S]*?)(?:\n##\s+|$)/i.exec(text);
173
+ if (!section) return false;
174
+ const body = section[1];
175
+ if (/^\s*none\s*$/im.test(body.trim())) return false;
176
+ const tables = markdownTables(body);
177
+ for (const table of tables) {
178
+ for (const row of table.rows) {
179
+ if (row.some((cell) => cell && cell !== "—" && cell !== "-" && cell !== "none")) return true;
180
+ }
181
+ }
182
+ return /L-\d{3}/.test(body);
183
+ }
184
+
185
+ function parseChainLessons(chainText) {
186
+ const lessons = new Map();
187
+ for (const table of markdownTables(chainText)) {
188
+ const idCol = tableColumn(table, ["ID", "id", "lesson_id"]);
189
+ const statusCol = tableColumn(table, ["状态", "status"]);
190
+ if (idCol < 0 || statusCol < 0) continue;
191
+ for (const row of table.rows) {
192
+ const id = lessonIdsIn(row[idCol] ?? "")[0];
193
+ if (id) lessons.set(id, { status: (row[statusCol] ?? "").trim(), row });
194
+ }
195
+ }
196
+ return lessons;
197
+ }
198
+
199
+ function parseChainUnits(chainText) {
200
+ const units = [];
201
+ for (const table of markdownTables(chainText)) {
202
+ const unitCol = tableColumn(table, ["unit_id", "unit"]);
203
+ const lessonsCol = tableColumn(table, ["Lessons", "lessons", "课列表"]);
204
+ if (unitCol < 0 || lessonsCol < 0) continue;
205
+ for (const row of table.rows) {
206
+ const unitId = (row[unitCol] ?? "").trim();
207
+ if (!unitId || unitId === "unit_id") continue;
208
+ units.push({ unitId, lessons: lessonIdsIn(row[lessonsCol] ?? "") });
209
+ }
210
+ }
211
+ return units;
212
+ }
213
+
214
+ function validateGoal(changeRoot, errors) {
215
+ const goalRoot = join(changeRoot, "goal");
216
+ if (!isDir(goalRoot)) return;
217
+ const rel = (path) => relative(changeRoot, path).split(sep).join("/");
218
+ const chainPath = join(goalRoot, "chain.md");
219
+ const planPath = join(goalRoot, "goal-plan.md");
220
+ const progressPath = join(goalRoot, "progress.md");
221
+ const matrixPath = join(goalRoot, "coverage-matrix.md");
222
+ const probesRoot = join(goalRoot, "probes");
223
+ const verifyPath = join(goalRoot, "verify.md");
224
+ const chainText = isFile(chainPath) ? readFileSync(chainPath, "utf8") : "";
225
+ const planText = isFile(planPath) ? readFileSync(planPath, "utf8") : "";
226
+ const progressText = isFile(progressPath) ? readFileSync(progressPath, "utf8") : "";
227
+ const matrixText = isFile(matrixPath) ? readFileSync(matrixPath, "utf8") : "";
228
+ const verifyText = isFile(verifyPath) ? readFileSync(verifyPath, "utf8") : "";
229
+
230
+ if (planText && OLD_INTERLEAVE.test(planText) && !planText.includes("禁止 L → mine → L → mine")) {
231
+ errors.push(`${rel(planPath)}: interleaved L/mine iteration policy is forbidden`);
232
+ }
233
+ if (progressText && OLD_INTERLEAVE.test(progressText)) {
234
+ errors.push(`${rel(progressPath)}: interleaved L/mine progress is forbidden`);
235
+ }
236
+
237
+ const units = parseChainUnits(chainText);
238
+ const seenLesson = new Set();
239
+ for (const unit of units) {
240
+ if (unit.lessons.length > MINE_UNIT_CAP) {
241
+ errors.push(`${rel(chainPath)}: mine unit ${unit.unitId} has ${unit.lessons.length} lessons; mine_unit_cap=${MINE_UNIT_CAP}`);
242
+ }
243
+ for (const id of unit.lessons) {
244
+ if (seenLesson.has(id)) errors.push(`${rel(chainPath)}: lesson ${id} belongs to more than one mine unit`);
245
+ seenLesson.add(id);
246
+ }
247
+ }
248
+
249
+ const lessons = parseChainLessons(chainText);
250
+ const probeFiles = isDir(probesRoot)
251
+ ? files(probesRoot).filter((path) => path.endsWith(".md") && !path.endsWith("INDEX.md"))
252
+ : [];
253
+ const byLesson = new Map();
254
+ for (const path of probeFiles) {
255
+ const name = path.split(sep).pop() ?? "";
256
+ const label = rel(path);
257
+ const text = readFileSync(path, "utf8");
258
+ const meta = frontmatter(text);
259
+ const match = PROBE_BATCH.exec(name);
260
+ if (!match) {
261
+ errors.push(`${label}: probe filename must be GP-<lesson>-b0N.md`);
262
+ continue;
263
+ }
264
+ const fileLesson = match[1];
265
+ const batch = Number(match[2]);
266
+ if (batch > 2) errors.push(`${label}: b03+ is forbidden; max two batches per lesson`);
267
+ if (meta.audience && meta.audience !== "mine") errors.push(`${label}: audience must be mine`);
268
+ if (meta.lesson_id && meta.lesson_id !== fileLesson && !String(meta.lesson_id).startsWith(fileLesson) && !fileLesson.startsWith(String(meta.lesson_id))) {
269
+ errors.push(`${label}: miner write-set isolation; lesson_id ${meta.lesson_id} does not match filename ${fileLesson}`);
270
+ }
271
+ if (LEARNER_PROBE.test(text)) errors.push(`${label}: mine probe must not contain Response:, Submission:, or inquiry/`);
272
+ const questions = [...text.matchAll(QUESTION_HEADING)].map((item) => Number(item[1]));
273
+ const record = byLesson.get(fileLesson) ?? { questions: [], batches: new Set(), files: [], texts: [], budgets: [], split: false, mineMore: false, splitDepth: 0 };
274
+ record.questions.push(...questions);
275
+ record.batches.add(batch);
276
+ record.files.push(label);
277
+ record.texts.push(text);
278
+ if (Number.isInteger(meta.question_budget_used)) record.budgets.push(meta.question_budget_used);
279
+ if (filledSplitProposal(text)) record.split = true;
280
+ if (/\bmine-more\b/.test(text)) record.mineMore = true;
281
+ if (Number.isInteger(meta.split_depth)) record.splitDepth = Math.max(record.splitDepth, meta.split_depth);
282
+ byLesson.set(fileLesson, record);
283
+ }
284
+
285
+ const minedLessons = new Set(byLesson.keys());
286
+ for (const unit of units) {
287
+ const statuses = unit.lessons.map((id) => lessons.get(id)?.status ?? "");
288
+ const unitHasProbe = unit.lessons.some((id) => minedLessons.has(id));
289
+ const unitStillWriting = unit.lessons.some((id) => {
290
+ const status = lessons.get(id)?.status ?? "";
291
+ return status === "planned" || status === "writing";
292
+ });
293
+ if (unitHasProbe && unitStillWriting) {
294
+ errors.push(`${rel(chainPath)}: mine unit ${unit.unitId} interleaved L/mine (probes exist while a lesson is still planned/writing)`);
295
+ }
296
+ void statuses;
297
+ }
298
+
299
+ const matrixUncovered = /\buncovered\b/.test(matrixText);
300
+ for (const [lessonId, record] of byLesson) {
301
+ const uniqueQs = new Set(record.questions);
302
+ if (uniqueQs.size > MAX_LESSON_QUESTIONS || record.questions.some((number) => number > MAX_LESSON_QUESTIONS)) {
303
+ errors.push(`${record.files[0]}: lesson ${lessonId} has more than ${MAX_LESSON_QUESTIONS} questions`);
304
+ }
305
+ const budget = record.budgets.length ? Math.max(...record.budgets) : uniqueQs.size;
306
+ const status = lessons.get(lessonId)?.status ?? "";
307
+ if (status === "revised" && uniqueQs.size > 0 && record.budgets.some((value) => value === 0)) {
308
+ errors.push(`${record.files[0]}: re-dispatch-L must not reset question_budget_used for ${lessonId}`);
309
+ }
310
+ if (uniqueQs.size >= MAX_LESSON_QUESTIONS && record.mineMore && !record.split) {
311
+ errors.push(`${record.files[0]}: 10 questions with remaining uncovered/mine-more requires split-proposal, not b03`);
312
+ }
313
+ if (uniqueQs.size >= MAX_LESSON_QUESTIONS && matrixUncovered && !record.split && record.mineMore) {
314
+ errors.push(`${record.files[0]}: 10 questions still uncovered requires split-proposal`);
315
+ }
316
+ if (record.splitDepth >= 2 && matrixUncovered && !/needs-replan/.test(`${progressText}\n${verifyText}\n${record.texts.join("\n")}`)) {
317
+ errors.push(`${record.files[0]}: split depth=2 still unclear must defer(needs-replan)`);
318
+ }
319
+ void budget;
320
+ }
113
321
  }
114
322
 
115
323
  function validateEntry(entry, label, errors, archived) {
@@ -229,6 +437,7 @@ function validateStateRoot(root, opts, errors) {
229
437
  const changeRoot = join(root, ...String(entry.locator).split("/"));
230
438
  validateLessons(changeRoot, errors);
231
439
  validateHomework(changeRoot, errors);
440
+ validateGoal(changeRoot, errors);
232
441
  if (opts.stage === "pre-archive" && (!opts.change || opts.change === entry.change_id) && value?.lifecycle !== "closed") errors.push(`${entry.change_id}: pre-archive requires lifecycle closed`);
233
442
  if (opts.stage === "complete" && (!opts.change || opts.change === entry.change_id) && !String(entry.locator).startsWith("archive/")) errors.push(`${entry.change_id}: complete stage requires archived locator`);
234
443
  }