@namewta/speculo 0.8.13 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/README.md +11 -7
  2. package/dist/src/cli.js +12 -1
  3. package/dist/src/cli.js.map +1 -1
  4. package/dist/src/doctor.d.ts +10 -0
  5. package/dist/src/doctor.js +70 -0
  6. package/dist/src/doctor.js.map +1 -0
  7. package/dist/src/index.js +105 -56
  8. package/dist/src/index.js.map +1 -1
  9. package/dist/src/kernel.d.ts +98 -0
  10. package/dist/src/kernel.js +29 -0
  11. package/dist/src/kernel.js.map +1 -0
  12. package/dist/src/refresh.js +55 -24
  13. package/dist/src/refresh.js.map +1 -1
  14. package/dist/src/structured.d.ts +2 -2
  15. package/dist/src/structured.js +114 -241
  16. package/dist/src/structured.js.map +1 -1
  17. package/package.json +4 -3
  18. package/template/.speculo/README.md +1 -1
  19. package/template/.speculo/capabilities.json +14 -0
  20. package/template/.speculo/kernel/README.md +10 -0
  21. package/template/.speculo/kernel/capability-profile.schema.json +14 -0
  22. package/template/.speculo/kernel/checkpoint.schema.json +7 -0
  23. package/template/.speculo/kernel/trace-event.schema.json +8 -0
  24. package/template/.speculo/kernel/workflow-manifest.schema.json +13 -0
  25. package/template/.speculo/kernel.json +10 -0
  26. package/template/.speculo/refresh-contract.json +4 -1
  27. package/template/AGENTS.md +11 -3
  28. package/template/canonical/canonical-specdev-goal-plan.md +10 -1
  29. package/template/canonical/canonical-specdev-grill-with-docs.md +7 -0
  30. package/template/canonical/canonical-specdev-orchestrate-implementation.md +18 -2
  31. package/template/canonical/canonical-specdev-spec.md +7 -0
  32. package/template/canonical/canonical-specdev-tickets.md +13 -6
  33. package/template/canonical/canonical-specdev-wayfinder.md +7 -0
  34. package/template/commands/archive-and-consolidate.md +13 -41
  35. package/template/commands/docs-sync.md +1 -1
  36. package/template/commands/git-repository-audit.md +1 -1
  37. package/template/commands/handoff.md +1 -1
  38. package/template/commands/retro.md +1 -1
  39. package/template/commands/status.md +4 -4
  40. package/template/skills/archive-and-consolidate/SKILL.md +9 -168
  41. package/template/skills/archive-and-consolidate/references/entry-procedure.md +170 -0
  42. package/template/skills/docs-sync/SKILL.md +9 -11
  43. package/template/skills/docs-sync/references/entry-procedure.md +18 -0
  44. package/template/skills/engineering-standards-builder/SKILL.md +9 -148
  45. package/template/skills/engineering-standards-builder/references/entry-procedure.md +154 -0
  46. package/template/skills/git-history-squash/SKILL.md +9 -88
  47. package/template/skills/git-history-squash/references/entry-procedure.md +94 -0
  48. package/template/skills/github-npm-ops/SKILL.md +9 -18
  49. package/template/skills/github-npm-ops/references/entry-procedure.md +25 -0
  50. package/template/skills/optimize-codex-config/SKILL.md +12 -72
  51. package/template/skills/optimize-codex-config/references/entry-procedure.md +78 -0
  52. package/template/skills/source-code-zip/SKILL.md +10 -559
  53. package/template/skills/source-code-zip/references/entry-procedure.md +565 -0
  54. package/template/skills/speculo-retro/SKILL.md +9 -16
  55. package/template/skills/speculo-retro/references/entry-procedure.md +23 -0
  56. package/template/skills/upstream-fork-sync/SKILL.md +10 -72
  57. package/template/skills/upstream-fork-sync/references/entry-procedure.md +78 -0
  58. package/template/skills/writing-great-skills/SKILL.md +9 -73
  59. package/template/skills/writing-great-skills/references/entry-procedure.md +79 -0
  60. package/template/workflows/learning/A-archive/A-archive.md +32 -0
  61. package/template/workflows/learning/A-assess-and-plan/A-assess-and-plan.md +23 -22
  62. package/template/workflows/learning/A-assess-and-plan/background-template.md +13 -0
  63. package/template/workflows/learning/A-assess-and-plan/change-status-template.json +21 -14
  64. package/template/workflows/learning/A-assess-and-plan/course-template.md +28 -0
  65. package/template/workflows/learning/C-consolidate/C-consolidate.md +40 -0
  66. package/template/workflows/learning/H-homework/H-homework.md +38 -0
  67. package/template/workflows/learning/H-homework/homework-template.md +63 -0
  68. package/template/workflows/learning/I-init-setup/I-init-setup.md +20 -18
  69. package/template/workflows/learning/I-init-setup/context-index-template.md +3 -3
  70. package/template/workflows/learning/I-init-setup/learner-profile-template.md +12 -10
  71. package/template/workflows/learning/I-init-setup/review-index-template.md +1 -1
  72. package/template/workflows/learning/INDEX.md +8 -8
  73. package/template/workflows/learning/L-lesson/L-lesson.md +39 -0
  74. package/template/workflows/learning/L-lesson/lesson-template.md +51 -0
  75. package/template/workflows/learning/R-review/R-review.md +19 -17
  76. package/template/workflows/learning/R-review/review-template.md +13 -6
  77. package/template/workflows/learning/README.md +88 -81
  78. package/template/workflows/learning/_state/status.json +1 -1
  79. package/template/workflows/learning/common/rules/activation-and-memory.md +20 -0
  80. package/template/workflows/learning/common/rules/artifact-contract.md +11 -25
  81. package/template/workflows/learning/common/rules/assessment-policy.md +9 -4
  82. package/template/workflows/learning/common/rules/knowledge-organization.md +4 -6
  83. package/template/workflows/learning/common/rules/mastery-policy.md +3 -19
  84. package/template/workflows/learning/common/rules/path-reference-contract.md +3 -5
  85. package/template/workflows/learning/common/rules/teaching-policy.md +9 -9
  86. package/template/workflows/learning/common/schemas/change-status.schema.json +46 -18
  87. package/template/workflows/learning/common/schemas/status.schema.json +25 -24
  88. package/template/workflows/learning/common/skills/topic-synthesis/SKILL.md +17 -0
  89. package/template/workflows/learning/common/skills/topic-synthesis/claim-template.md +15 -0
  90. package/template/workflows/learning/common/tools/relocate-learning.mjs +208 -0
  91. package/template/workflows/learning/common/tools/validate-learning.mjs +195 -297
  92. package/template/workflows/learning/manifest.json +1 -0
  93. package/template/workflows/learning/runtime-contract.json +3 -2
  94. package/template/workflows/ops/A-archive-and-learn/A-archive-and-learn.md +7 -0
  95. package/template/workflows/ops/E-execute-and-stabilize/E-execute-and-stabilize.md +8 -1
  96. package/template/workflows/ops/I-intake-and-assess/I-intake-and-assess.md +7 -0
  97. package/template/workflows/ops/INDEX.md +2 -0
  98. package/template/workflows/ops/P-plan-and-approve/P-plan-and-approve.md +8 -1
  99. package/template/workflows/ops/README.md +3 -0
  100. package/template/workflows/ops/common/rules/activation-and-memory.md +20 -0
  101. package/template/workflows/ops/manifest.json +1 -0
  102. package/template/workflows/person/INDEX.md +2 -0
  103. package/template/workflows/person/M-mao-zedong-cognitive-os/M-mao-zedong-cognitive-os.md +9 -0
  104. package/template/workflows/person/M-mao-zedong-cognitive-os/books/README.md +1 -1
  105. package/template/workflows/person/S-steelman-deliberation/S-steelman-deliberation.md +7 -0
  106. package/template/workflows/person/common/rules/activation-and-memory.md +20 -0
  107. package/template/workflows/person/manifest.json +1 -0
  108. package/template/workflows/specdev/A-archive-and-consolidate/A-archive-and-consolidate.md +7 -0
  109. package/template/workflows/specdev/C-code-review/C-code-review.md +7 -0
  110. package/template/workflows/specdev/D-diagnose-bugs/D-diagnose-bugs.md +7 -0
  111. package/template/workflows/specdev/G-grill-with-docs/G-grill-with-docs.md +7 -0
  112. package/template/workflows/specdev/I-implement/I-implement.md +7 -0
  113. package/template/workflows/specdev/I-implement/execution-preflight.md +1 -1
  114. package/template/workflows/specdev/I-init-setup/I-init-setup.md +7 -0
  115. package/template/workflows/specdev/INDEX.md +2 -0
  116. package/template/workflows/specdev/L-learn-change/L-learn-change.md +7 -0
  117. package/template/workflows/specdev/O-orchestrate-implementation/O-orchestrate-implementation.md +10 -1
  118. package/template/workflows/specdev/P-goal-plan/P-goal-plan.md +10 -1
  119. package/template/workflows/specdev/P-prototype/P-prototype.md +7 -0
  120. package/template/workflows/specdev/R-review-architecture/R-review-architecture.md +7 -0
  121. package/template/workflows/specdev/README.md +3 -0
  122. package/template/workflows/specdev/S-spec/S-spec.md +7 -0
  123. package/template/workflows/specdev/T-tickets/T-tickets.md +10 -3
  124. package/template/workflows/specdev/T-tickets/ticket-template.md +2 -2
  125. package/template/workflows/specdev/T-tickets/tickets-map-template.md +1 -1
  126. package/template/workflows/specdev/T-triage/T-triage.md +7 -0
  127. package/template/workflows/specdev/W-wayfinder/W-wayfinder.md +7 -0
  128. package/template/workflows/specdev/common/README.md +1 -0
  129. package/template/workflows/specdev/common/rules/activation-and-memory.md +20 -0
  130. package/template/workflows/specdev/manifest.json +1 -0
  131. package/template/workflows/workflow-manifest.schema.json +14 -0
  132. package/template/workflows/learning/A-archive-and-consolidate/A-archive-and-consolidate.md +0 -38
  133. package/template/workflows/learning/A-archive-and-consolidate/promotion-plan-template.md +0 -23
  134. package/template/workflows/learning/A-assess-and-plan/learning-plan-template.md +0 -28
  135. package/template/workflows/learning/E-eli5/E-eli5.md +0 -37
  136. package/template/workflows/learning/E-eli5/lesson-template.md +0 -31
  137. package/template/workflows/learning/P-practice/P-practice.md +0 -34
  138. package/template/workflows/learning/P-practice/practice-template.md +0 -16
  139. package/template/workflows/learning/Q-quiz/Q-quiz.md +0 -34
  140. package/template/workflows/learning/Q-quiz/quiz-artifact-template.md +0 -16
  141. package/template/workflows/learning/common/skills/knowledge-promotion/SKILL.md +0 -46
  142. package/template/workflows/learning/common/skills/knowledge-promotion/domain-index-template.md +0 -6
  143. package/template/workflows/learning/common/skills/knowledge-promotion/domain-overview-template.md +0 -19
  144. package/template/workflows/learning/common/skills/knowledge-promotion/knowledge-template.md +0 -25
@@ -1,5 +1,5 @@
1
1
  {
2
- "schema_version": 1,
2
+ "schema_version": 2,
3
3
  "workflow": "learning",
4
4
  "active": [],
5
5
  "archived": []
@@ -0,0 +1,20 @@
1
+ # Activation and memory retrieval protocol
2
+
3
+ 本规则只在用户明确激活当前 workflow 或某个 Work 后读取。INDEX 只用于被动发现,不初始化状态、不读取 active change、不写入知识。
4
+
5
+ ## Locate before read
6
+
7
+ 1. 先解析当前 workflow 的 roots、状态索引和稳定 ID;不存在时静默跳过,不能凭旧路径猜测。
8
+ 2. 根据当前请求、Work 分支、关键词、状态和 provenance 定位最小相关 entry。
9
+ 3. 只回读命中的 entry 和直接 provenance;需要恢复、冲突裁决、归档、迁移或执行安全证明时,才读取该阶段声明的完整证据集合。
10
+ 4. 没有匹配证据时返回缺失证据并停止依赖该结论的分支,不补造事实。
11
+
12
+ ## Memory writes
13
+
14
+ 正式知识、永久 context、synthesis 或 archive 写入前,先解析唯一 owner 与 gateway,检查 pending transaction、lock、未完成 promotion 和 recovery evidence。gateway 不明或事务未闭合时,只阻塞记忆写入,继续独立的只读审计、定位和验证。
15
+
16
+ 每次写入必须记录 source IDs、证据定位、验证时间或 digest;写入后重新读取索引和目标 entry,确认 owner、locator、内容和状态投影一致。原始证据不可被派生视图覆盖。
17
+
18
+ ## Read budget
19
+
20
+ 当前 Work 的权威状态、schema、Map/Plan、当前输入和直接所有权合同可以完整读取;非当前分支的知识树、历史 change、研究库、项目 Skill 和示例只按索引与关键词读取。执行、冲突、恢复和归档 Work 需要完整证据时,以该 Work 的显式合同为准。
@@ -1,27 +1,13 @@
1
- # Learning 工件合同
1
+ # Learning v2 工件合同
2
2
 
3
- ## 权威与所有权
4
-
5
- | 工件 | Owner | 权威内容 |
3
+ | 工件 | Owner | 规则 |
6
4
  | --- | --- | --- |
7
- | `intake.md` | A-assess | 学习对象、动机、范围和约束 |
8
- | `baseline.md` | A-assess | 未教学前的已有能力证据 |
9
- | `learning-plan.md` | A-assess | 目标、关键目标、前置知识、完成证据和深度 |
10
- | `sources.md` | A-assess / E-eli5 | 来源、定位、可信度和已验证范围 |
11
- | `lessons/` | E-eli5 | 教学解释、图解、类比及边界 |
12
- | `practice/` | P-practice | 原始练习作答、提示层级和反馈 |
13
- | `quiz/*-response.md` | 学习者 | 不经改写的原始答案 |
14
- | `quiz/*-result.md` | Q-quiz / R-review | 按锁定 rubric 产生的评分和差距 |
15
- | `promotion-plan.md` | A-archive | 待创建、合并、取代、索引更新和归档动作 |
16
- | `context/` | A-archive;R 仅限复习状态 | 当前已掌握知识 |
17
- | `archive/` | A-archive | 不可变学习历史 |
18
-
19
- 冲突时按实际事实、学习者原始作答、锁定目标/rubric、测验 result、promotion plan、永久知识、课程解释和索引摘要的顺序裁决。状态 JSON 只是投影,必须与上述工件一致。
20
-
21
- ## 不变量
22
-
23
- - `baseline.md` 必须在首份 lesson 前形成;缺失时不能宣称教学适配了背景。
24
- - 目标内容可以澄清但不能在看到测验结果后静默降低;实质变化创建修订记录并重新测验。
25
- - AI 不得改写学习者答案后再评分。
26
- - 未同时通过即时和保持门的内容只能留在 change,不能进入 context。
27
- - 归档内容只读;纠正通过新 change 和 supersedes 关系完成。
5
+ | `course.md`、`background/`、`baseline.md`、`sources.md`、Change `INDEX.md` | A-assess-and-plan | 目标、宏观基础、原始基线、来源和课程地图;实质修改留 revision |
6
+ | `lessons/` | L-lesson | 完整 Lesson 和 Lesson INDEX;不写作业答案或掌握结论 |
7
+ | `homework/` | H-homework + learner | H 写问题/评审,learner 写 A;提交后只追加 Review,旧 attempt 不可改写 |
8
+ | `review/` | R-review | 延迟保持题、原始回答、证据和复习日期 |
9
+ | `children/<id>/` | child Work | Change 原有工件;父只负责根锁、路由、位置登记 |
10
+ | `synthesis/`、topic context | C-consolidate | claim 级综合、冲突、空白、provenance 和版本;不得覆盖原料 |
11
+ | `archive/`、locations projection | A-archive | 用户关闭后的物理移动和只读登记;不判断掌握、不写 context |
12
+
13
+ 状态 JSON 只是 projection;真实内容、学习者原始回答、评审和 source manifest 是证据权威。
@@ -1,7 +1,12 @@
1
- # 评估与评分政策
1
+ # 作业与评审政策
2
2
 
3
- 学习计划在教学前锁定 objective id、关键性和验收证据。最终题目不得简单复述课程示例;评分 rubric 必须在作答前固定,但答案或评分结论只在学习者提交原始 response 后读取或形成。
3
+ H 将教学和评估分开。生成作业时只提供问题和必要的作答边界,不泄露答案;用户必须在同一文件加入 `Submission: ready` 才会进入评审。
4
4
 
5
- 每次测验覆盖主动回忆、因果/结构解释、新情境迁移和常见误区辨析。题目、原始 response、result 分文件保存;重试使用新变式和递增 attempt 编号,不覆盖旧证据。
5
+ 默认五题分别覆盖回忆/定义、机制/为什么、变式应用、全新情境迁移和误区辨析。每题评审必须写:
6
6
 
7
- Result 对每个 objective 列出:得分、引用的 response、正确处、差距、证据来源和置信度。主观或来源冲突项使用 `needs_review`。失败 result 必须形成最小补救范围,路由回 E 或 P,不扩大整个课程。
7
+ - `verdict`: `correct | partial | incorrect | uncertain`;
8
+ - 回答覆盖了什么、缺了什么和引用的 Lesson/source anchor;
9
+ - 中文详细讲解和 `Explain (English)`;
10
+ - 误区、修正路径和下一步。
11
+
12
+ H 不要求固定百分比,也不把 immediate 评审自动称为 mastered。题目/回答/评审在单文件内按段落追加;重答创建新 attempt。评分无法裁决时使用 `uncertain` 并保留 blocker。
@@ -1,9 +1,7 @@
1
- # 图书式知识组织
1
+ # 主题知识组织与溯源
2
2
 
3
- Learning 只使用 Markdown 目录和精确链接:`context/INDEX.md` 指向领域 `INDEX.md`,领域索引指向具体知识文件。允许 `rg` 修复索引,但不建设向量、Embedding、chunk、相似度或 rerank 层。
3
+ 被动入口是 `context/INDEX.md` -> `domains/<domain>/topics/<topic-id>/INDEX.md` -> 精确主题文件。主题视图是 C 生成的派生物,不是原始课程或作业的替代品。
4
4
 
5
- 领域目录使用稳定 kebab id;领域类型为 projectproduct、subject、language skill。每个领域包含 `INDEX.md`、`overview.md`,并按真实需要创建 `concepts/`、`methods/`、`adr/`,不为空架构预建无用途目录。
5
+ 每个 claim 记录稳定 `claim_id`、陈述、`evidence_status`(`draft|supported|contested|unresolved`)、source Change IDLesson/Homework/Review anchor、外部 source ID、验证时间和冲突/空白。所有链接用 `{roots.*}` aliases;物理 relocation 后由 `locations.json` 按 Change ID 解析当前路径。
6
6
 
7
- 知识文件必须包含元数据表:Knowledge ID、状态、前置知识、相关知识、掌握证据、最近验证、下次复习;正文包含当前理解、心智模型、示例与应用、常见误区、来源与证据。状态只允许 `mastered | review_due | needs_refresh | superseded`。
8
-
9
- 同主题存在时合并当前真相;新事实推翻旧结论时改写当前文件并记录 `supersedes` 归档证据;仅相关时互链,不复制正文。更新顺序为知识叶子、领域 INDEX、context/INDEX、REVIEW。索引只保存导航摘要,不保存课程全文或会话历史。
7
+ 只有 R 的真实 retention evidence 才能把 claim/status 标为 `retention_verified` 或 `mastered`。C 可以综合未掌握材料,但必须保留 evidence status,不能从综合文本推断掌握。
@@ -1,21 +1,5 @@
1
- # 掌握与复习政策
1
+ # 掌握与复习政策 v2
2
2
 
3
- ## 双重掌握门
3
+ 即时理解来自 H 的逐题评审,字段为 `mastery.immediate` 和 objective/transfer projection;H 评审后可以结束当前轮次。没有固定 80% 门槛,也不要求 Homework、R 或掌握才能使用 A-archive。
4
4
 
5
- 即时门验证当前理解;保持门验证间隔后的回忆和迁移。即时通过只把 change 设置为 `awaiting_retention`,默认首次保持测验安排在至少 24 小时后。学习者可以在 profile 中选择更长间隔,但不得把零间隔称为保持证据。
6
-
7
- 两个门都必须满足:总分至少 80%;所有关键目标通过;至少一道新情境迁移题通过;没有阻塞性误解。无法客观裁决的答案标记 `needs_review`,在用户或独立证据确认前不得通过。
8
-
9
- ## 周期复习
10
-
11
- 默认复习间隔为通过保持门后的 7、30、90 天。R 根据真实日期和上次证据安排;不伪造未来完成。周期复习失败不删除 archive,而是创建 remediation change,并把当前知识标记为 `needs_refresh`。
12
-
13
- ## 证据强度
14
-
15
- - 项目:准确定位、调用或数据流预测、可重复命令/测试、小型安全任务。
16
- - 产品:用户、问题、约束、指标、取舍和新场景判断。
17
- - 学科:回忆、因果解释、计算/推导或反例、迁移应用。
18
- - 语言:理解、主动产出、纠错、不同语境迁移。
19
- - 技能:独立执行、可观察结果、错误诊断和变式任务。
20
-
21
- 不引入 BKT、FSRS 或概率熟练度模型。只有积累了足以验证收益的历史数据后,才通过新 change 决定是否增加算法。
5
+ 保持掌握只能来自 R:必须在真实间隔后重新回忆、解释、反例和新情境迁移;时间未到只能记录 due date。通过后设置 `mastery.retention=passed`、`mastery.overall=retention_verified`,并允许 context 标记 mastered。失败不删除历史,必要时建立补救 Lesson/Change。
@@ -1,7 +1,5 @@
1
- # Learning 路径引用合同
1
+ # Learning v2 路径与引用
2
2
 
3
- 静态 Learning 文件使用完整 `<Path>{roots.workflows}/learning/<relative-path></Path>`;运行时状态使用完整 `<Path>{roots.state}/learning/<relative-path></Path>`。不得使用机器绝对路径、反斜杠、`..`、裸文件名或把 workflow `_state` 当作运行时目录。
3
+ 稳定标识是 `change_id`、`lesson_id`、`homework_id`、`review_id` `topic_id`;`locator` 只是当前位置:`changes/<id>`、`changes/<parent>/children/<id>` 或 `archive/YYYY-MM/<root>`。跨工件引用同时写 stable ID 和 `<Path>{roots.state}/learning/</Path>` 下的当前 locator。
4
4
 
5
- `{change}` 是启动协议选择的 change 名;`{domain}` 来自 `learning-plan.md` change 状态;`YYYY-MM` change 日期前缀派生。项目证据使用项目根相对路径,外部来源使用真实 URL。
6
-
7
- 动态知识路径必须先由 promotion plan 明确,确认后才允许创建或改写。完整路径只确定对象,不授予副作用权限。
5
+ `locations.json` 保存历史 locator、relocation 时间/原因和源目录哈希。新工件不得把旧路径当作永久链接;validator 必须跟随位置登记解析。源 Markdown 的相对链接在移动后由 owner 修正为 alias/ID 引用,已经提交的 Homework、Review 和 archive 内容不改写。
@@ -1,15 +1,15 @@
1
- # 通俗教学政策
1
+ # 完整课程教学政策
2
2
 
3
- 每次教学必须明确写出一个直接、具体的教学表达基线,例如“5 岁的小孩”或“大一新生”。不要把它改写成“普通初学者”“低门槛读者”或“没有专业背景的人”;这些抽象说法不能给出足够稳定的表达约束。用户没有指定时,默认使用“5 岁的小孩”。
3
+ ## 深度与表达
4
4
 
5
- 教学表达基线直接约束讲法:
5
+ `expression_level=eli5|plain` 只控制词汇、句法、脚手架和类比;`coverage_depth=overview|standard|deep` 控制事实、机制、边界、例子和迁移的覆盖。ELI5 不是删减内容,类比必须紧跟失效边界和精确定义。
6
6
 
7
- - **5 岁的小孩**:使用短句、具体物体和日常动作;一次只引入一个新概念;先说现象和用途,再给名称;不预设公式、专业术语或行业经验。
8
- - **大一新生**:可以使用基础逻辑、简单公式和抽象分类,但不预设专业课程、项目经验或行业惯例;每个首次出现的术语仍需定义。
9
- - **其他基线**:必须由用户明确指定,并写清可以预设的语言、数学、学科或实践基础。
7
+ ## 时间与结构
10
8
 
11
- 教学表达基线只决定如何讲,不代表学习者在该领域已经掌握什么。内容深度由 baseline 和经过索引、知识正文确认的 context 证据决定;已掌握概念可以作为桥梁,未验证内容不得当作基础。
9
+ 标准 Lesson `estimated_minutes` 为 30–40(默认 35),由 orientation、解释、视觉/示例、暂停和总结等 `time_budget` 加总;不以字符数作为时长证据。章节次序可按主题变更,但每个 OBJ 至少有动机/宏观地图、直觉、术语/English term、机制、文本视觉和文字等价物、正例、反例/边界、变式迁移、误区、总结和来源。
12
10
 
13
- 所有图解只使用 fenced code block 中的纯文本 ASCII,不使用 Mermaid、HTML、SVG 或其他依赖渲染器的格式。每张图只回答一个问题,箭头有方向;文字解释关系,不逐字重复图。首次出现术语时先给日常说法,再给专业名称。类比必须标出“哪里相像、哪里不相像”。
11
+ ## 图文与来源
14
12
 
15
- 教学采用脚手架递减:示范一个、共同完成一个、学习者独立完成一个。学习者未尝试前先给逐级提示,不直接泄露完整答案。解释结束不等于学会,必须进入练习和测验。
13
+ ASCII、Markdown table、公式或可选外链图片可以组合使用;每个视觉必须有 caption、alt 和附近的完整文字等价物,外链失效不能阻塞理解。`sources.md` 和 Lesson source table 记录 URL、标题、定位、访问日期、claim 映射、可信度和不确定性;无法验证的内容明确标为 open/uncertain。
14
+
15
+ Lesson 可以有非评分 pause/self-check,但不含 Q/A、答案、分数、verdict 或 mastered 字段。
@@ -1,41 +1,69 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "urn:speculo:learning:change-status:v1",
4
- "title": "Learning Change Status",
3
+ "$id": "urn:speculo:learning:change-status:v2",
4
+ "title": "Learning Change Status v2",
5
5
  "type": "object",
6
- "required": ["schema_version", "artifact", "change", "domain", "domain_type", "topic", "change_status", "phase", "current_work", "works_run", "created_at", "updated_at", "completed_at", "archived_at", "archive_path", "mastery", "blockers"],
6
+ "required": ["schema_version", "artifact", "change_id", "kind", "domain", "domain_type", "topic_id", "parent_change", "root_change", "locator", "lifecycle", "phase", "current_work", "works_run", "created_at", "updated_at", "closed_at", "archived_at", "closure_reason", "archive_path", "homework", "mastery", "children", "blockers"],
7
7
  "properties": {
8
- "schema_version": {"const": 1},
8
+ "schema_version": {"const": 2},
9
9
  "artifact": {"const": "learning-change-status"},
10
- "change": {"type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}-[a-z0-9]+(?:-[a-z0-9]+)*$"},
10
+ "change_id": {"type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[a-z0-9]+(?:-[a-z0-9]+)*$"},
11
+ "kind": {"enum": ["learning", "consolidation"]},
11
12
  "domain": {"type": "string", "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"},
12
13
  "domain_type": {"enum": ["project", "product", "subject", "language", "skill"]},
13
- "topic": {"type": "string", "minLength": 1},
14
- "change_status": {"enum": ["active", "blocked", "awaiting_retention", "completed", "archived"]},
15
- "phase": {"enum": ["intake", "assessment", "teaching", "practice", "immediate_quiz", "retention", "ready_to_archive", "archived"]},
14
+ "topic_id": {"type": "string", "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"},
15
+ "parent_change": {"type": ["string", "null"]},
16
+ "root_change": {"type": "string"},
17
+ "locator": {"type": "string", "pattern": "^(?:changes/.+|archive/[0-9]{4}-[0-9]{2}/.+)$"},
18
+ "lifecycle": {"enum": ["active", "blocked", "closed", "archived"]},
19
+ "phase": {"enum": ["planning", "teaching", "homework", "review", "consolidating", "closed", "archived"]},
16
20
  "current_work": {"type": ["string", "null"], "pattern": "^learning/"},
17
21
  "works_run": {"type": "array", "items": {"type": "string", "pattern": "^learning/"}, "uniqueItems": true},
18
22
  "created_at": {"type": "string", "format": "date-time"},
19
23
  "updated_at": {"type": "string", "format": "date-time"},
20
- "completed_at": {"type": ["string", "null"], "format": "date-time"},
24
+ "closed_at": {"type": ["string", "null"], "format": "date-time"},
21
25
  "archived_at": {"type": ["string", "null"], "format": "date-time"},
22
- "archive_path": {"anyOf": [{"type": "null"}, {"type": "string", "pattern": "^<Path>\\{roots\\.state\\}/learning/archive/[0-9]{4}-[0-9]{2}/[^<]+</Path>$"}]},
26
+ "closure_reason": {"type": ["string", "null"]},
27
+ "archive_path": {"type": ["string", "null"]},
28
+ "homework": {
29
+ "type": "object",
30
+ "required": ["status", "latest_id", "submitted_at"],
31
+ "properties": {
32
+ "status": {"enum": ["none", "pending", "reviewed", "needs_revision"]},
33
+ "latest_id": {"type": ["string", "null"]},
34
+ "submitted_at": {"type": ["string", "null"], "format": "date-time"}
35
+ },
36
+ "additionalProperties": false
37
+ },
23
38
  "mastery": {
24
39
  "type": "object",
25
- "required": ["immediate", "retention", "score", "critical_objectives_passed", "transfer_passed", "blocking_misconceptions", "evidence", "next_review_at"],
40
+ "required": ["overall", "immediate", "retention", "critical_objectives", "transfer", "blocking_misconceptions", "evidence", "next_review_at"],
26
41
  "properties": {
27
- "immediate": {"enum": ["not_attempted", "failed", "passed", "needs_review"]},
28
- "retention": {"enum": ["not_attempted", "failed", "passed", "needs_review"]},
29
- "score": {"type": ["number", "null"], "minimum": 0, "maximum": 100},
30
- "critical_objectives_passed": {"type": "boolean"},
31
- "transfer_passed": {"type": "boolean"},
42
+ "overall": {"enum": ["unverified", "immediate", "retention_verified"]},
43
+ "immediate": {"enum": ["not_attempted", "passed", "needs_review"]},
44
+ "retention": {"enum": ["not_scheduled", "due", "passed", "needs_review"]},
45
+ "critical_objectives": {"enum": ["not_attempted", "passed", "needs_review"]},
46
+ "transfer": {"enum": ["not_attempted", "passed", "needs_review"]},
32
47
  "blocking_misconceptions": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
33
- "evidence": {"type": "array", "items": {"type": "string", "pattern": "^<Path>\\{roots\\.state\\}/learning/(?:changes/[^<]+|archive/[0-9]{4}-[0-9]{2}/[^<]+)/quiz/[^<]+\\.md</Path>$"}, "uniqueItems": true},
48
+ "evidence": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
34
49
  "next_review_at": {"type": ["string", "null"], "format": "date-time"}
35
50
  },
36
51
  "additionalProperties": false
37
52
  },
38
- "blockers": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}
53
+ "children": {"type": "array", "items": {"$ref": "#/$defs/child"}, "uniqueItems": true},
54
+ "blockers": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
55
+ "child": {
56
+ "type": "object",
57
+ "required": ["change_id", "locator", "content_hash", "effective_at", "lifecycle"],
58
+ "properties": {
59
+ "change_id": {"type": "string"},
60
+ "locator": {"type": "string"},
61
+ "content_hash": {"type": "string", "pattern": "^[a-f0-9]{64}$"},
62
+ "effective_at": {"type": "string", "format": "date-time"},
63
+ "lifecycle": {"type": "string"}
64
+ },
65
+ "additionalProperties": false
66
+ }
39
67
  },
40
68
  "additionalProperties": false
41
69
  }
@@ -1,32 +1,33 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "urn:speculo:learning:status:v1",
4
- "title": "Learning Global Status",
3
+ "$id": "urn:speculo:learning:status:v2",
4
+ "title": "Learning Global Status v2",
5
5
  "type": "object",
6
6
  "required": ["schema_version", "workflow", "active", "archived"],
7
7
  "properties": {
8
- "schema_version": {"const": 1},
8
+ "schema_version": {"const": 2},
9
9
  "workflow": {"const": "learning"},
10
- "active": {
11
- "type": "array",
12
- "items": {
13
- "type": "object",
14
- "required": ["change", "domain", "topic", "current_work", "works_run"],
15
- "properties": {
16
- "change": {"type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}-[a-z0-9]+(?:-[a-z0-9]+)*$"},
17
- "domain": {"type": "string", "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"},
18
- "topic": {"type": "string", "minLength": 1},
19
- "current_work": {"type": ["string", "null"], "pattern": "^learning/"},
20
- "works_run": {"type": "array", "items": {"type": "string", "pattern": "^learning/"}, "uniqueItems": true}
21
- },
22
- "additionalProperties": false
23
- }
24
- },
25
- "archived": {
26
- "type": "array",
27
- "items": {"type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}-[a-z0-9]+(?:-[a-z0-9]+)*$"},
28
- "uniqueItems": true
29
- }
10
+ "active": {"type": "array", "items": {"$ref": "#/$defs/entry"}},
11
+ "archived": {"type": "array", "items": {"$ref": "#/$defs/entry"}}
30
12
  },
31
- "additionalProperties": false
13
+ "additionalProperties": false,
14
+ "$defs": {
15
+ "entry": {
16
+ "type": "object",
17
+ "required": ["change_id", "kind", "domain", "topic_id", "locator", "parent_change", "root_change", "current_work", "works_run"],
18
+ "properties": {
19
+ "change_id": {"type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}-[a-z0-9]+(?:-[a-z0-9]+)*$"},
20
+ "kind": {"enum": ["learning", "consolidation"]},
21
+ "domain": {"type": "string", "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"},
22
+ "topic_id": {"type": "string", "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"},
23
+ "locator": {"type": "string", "pattern": "^(?:changes/.+|archive/[0-9]{4}-[0-9]{2}/.+)$"},
24
+ "parent_change": {"type": ["string", "null"]},
25
+ "root_change": {"type": "string"},
26
+ "current_work": {"type": ["string", "null"], "pattern": "^learning/"},
27
+ "works_run": {"type": "array", "items": {"type": "string", "pattern": "^learning/"}, "uniqueItems": true},
28
+ "updated_at": {"type": "string", "format": "date-time"}
29
+ },
30
+ "additionalProperties": false
31
+ }
32
+ }
32
33
  }
@@ -0,0 +1,17 @@
1
+ ---
2
+ name: topic-synthesis
3
+ description: 将 Learning Change 作为不可变原料,生成带 claim 级 provenance 的主题综合并显式发布。
4
+ ---
5
+
6
+ # Topic Synthesis
7
+
8
+ 1. 只接收 C-consolidate 已确认的 source manifest;按稳定 Change ID 读取当前 locator,禁止依赖旧路径猜测。
9
+ 2. 对每个 claim 记录 source Change、Lesson/Homework/Review anchor、外部 source id、证据状态、冲突、空白和验证时间;不要把类比或索引摘要当成事实来源。
10
+ 3. 生成新的 `synthesis/revisions/<version>.md`,旧版本只读保留。综合可以包含未掌握证据,但不得写 `mastered`,除非 R evidence 已存在。
11
+ 4. 发布 context 前必须再次获得用户确认;发布只更新 topic INDEX/current view 和 provenance,不删除或改写任何 child 原料。
12
+
13
+ ## 引用模板
14
+
15
+ | claim_id | 陈述 | evidence_status | source_change_id | artifact anchor | external source | verified_at |
16
+ | --- | --- | --- | --- | --- | --- | --- |
17
+ | CLM-001 | `<claim>` | supported | `<change-id>` | `L-001#heading` | `S-001` | `<ISO-8601>` |
@@ -0,0 +1,15 @@
1
+ # Claim <CLM-NNN>
2
+
3
+ ## 陈述
4
+
5
+ ## Evidence status
6
+
7
+ `draft | supported | contested | unresolved`
8
+
9
+ ## 来源与定位
10
+
11
+ - Change ID / current locator:
12
+ - Lesson/Homework/Review anchor:
13
+ - External source ID / URL / access date:
14
+
15
+ ## 冲突、空白与下一次验证
@@ -0,0 +1,208 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { createHash } from "node:crypto";
4
+ import { existsSync } from "node:fs";
5
+ import { mkdir, open, readFile, rename, rm, writeFile } from "node:fs/promises";
6
+ import { dirname, join, relative, resolve, sep } from "node:path";
7
+
8
+ const CHANGE_ID = /^[0-9]{4}-[0-9]{2}-[0-9]{2}-[a-z0-9]+(?:-[a-z0-9]+)*$/;
9
+
10
+ function args(argv) {
11
+ const result = { stateRoot: null, parent: null, sources: [], apply: false, rollback: null };
12
+ for (let index = 0; index < argv.length; index += 1) {
13
+ const arg = argv[index];
14
+ if (arg === "--state-root") result.stateRoot = resolve(argv[++index] ?? "");
15
+ else if (arg === "--parent") result.parent = argv[++index] ?? null;
16
+ else if (arg === "--source") result.sources.push(argv[++index] ?? "");
17
+ else if (arg === "--apply") result.apply = true;
18
+ else if (arg === "--rollback") result.rollback = resolve(argv[++index] ?? "");
19
+ else throw new Error(`unknown option: ${arg}`);
20
+ }
21
+ if (!result.stateRoot) throw new Error("--state-root is required");
22
+ if (result.rollback) return result;
23
+ if (!result.parent || !CHANGE_ID.test(result.parent) || result.sources.length === 0) {
24
+ throw new Error("--parent and at least one --source are required");
25
+ }
26
+ if (result.sources.some((source) => !CHANGE_ID.test(source)) || new Set(result.sources).size !== result.sources.length) {
27
+ throw new Error("source ids must be unique valid Change IDs");
28
+ }
29
+ if (result.sources.includes(result.parent)) throw new Error("a parent cannot be one of its own sources");
30
+ return result;
31
+ }
32
+
33
+ async function json(path, fallback) {
34
+ if (!existsSync(path)) return fallback;
35
+ return JSON.parse(await readFile(path, "utf8"));
36
+ }
37
+
38
+ async function files(root) {
39
+ const result = [];
40
+ async function visit(current) {
41
+ const entries = await (await import("node:fs/promises")).readdir(current, { withFileTypes: true });
42
+ for (const entry of entries) {
43
+ const path = join(current, entry.name);
44
+ if (entry.isDirectory()) await visit(path);
45
+ else if (entry.isFile()) result.push(path);
46
+ }
47
+ }
48
+ await visit(root);
49
+ return result.sort();
50
+ }
51
+
52
+ async function contentHash(root) {
53
+ const hash = createHash("sha256");
54
+ for (const path of await files(root)) {
55
+ const rel = relative(root, path).split(sep).join("/");
56
+ if (rel === ".status.json") continue;
57
+ hash.update(rel);
58
+ hash.update(await readFile(path));
59
+ }
60
+ return hash.digest("hex");
61
+ }
62
+
63
+ async function loadLocations(stateRoot) {
64
+ const path = join(stateRoot, "locations.json");
65
+ const value = await json(path, { schema_version: 2, workflow: "learning", entries: [] });
66
+ if (value.schema_version !== 2 || value.workflow !== "learning" || !Array.isArray(value.entries)) {
67
+ throw new Error("locations.json must use Learning schema v2");
68
+ }
69
+ return { path, value };
70
+ }
71
+
72
+ function locatorFor(entry, stateRoot) {
73
+ const locator = entry?.locator;
74
+ if (typeof locator === "string" && (locator.startsWith("changes/") || locator.startsWith("archive/"))) return locator;
75
+ return `changes/${entry.change_id}`;
76
+ }
77
+
78
+ function updateLocation(value, id, from, to, hash, now, parent) {
79
+ const existing = value.entries.find((entry) => entry.change_id === id);
80
+ if (existing) {
81
+ existing.previous = Array.isArray(existing.previous) ? existing.previous : [];
82
+ existing.previous.push({ locator: from, moved_at: now, reason: "consolidate", content_hash: hash });
83
+ existing.locator = to;
84
+ existing.parent_change = parent;
85
+ existing.root_change = value.entries.find((entry) => entry.change_id === parent)?.root_change ?? parent;
86
+ existing.content_hash = hash;
87
+ existing.updated_at = now;
88
+ return;
89
+ }
90
+ value.entries.push({
91
+ change_id: id,
92
+ locator: to,
93
+ parent_change: parent,
94
+ root_change: parent,
95
+ content_hash: hash,
96
+ previous: [{ locator: from, moved_at: now, reason: "consolidate", content_hash: hash }],
97
+ updated_at: now,
98
+ });
99
+ }
100
+
101
+ async function updateStatus(path, id, to, parent, now) {
102
+ const statusPath = join(path, ".status.json");
103
+ if (!existsSync(statusPath)) return;
104
+ const value = await json(statusPath, null);
105
+ if (!value || value.schema_version !== 2 || value.change_id !== id) return;
106
+ value.parent_change = parent;
107
+ value.root_change = value.root_change === id ? parent : value.root_change;
108
+ value.locator = to;
109
+ value.updated_at = now;
110
+ await writeFile(statusPath, JSON.stringify(value, null, 2) + "\n", "utf8");
111
+ }
112
+
113
+ async function updateGlobalStatus(stateRoot, ids, parent, now) {
114
+ const path = join(stateRoot, "status.json");
115
+ const value = await json(path, null);
116
+ if (!value || value.schema_version !== 2) return;
117
+ const root = value.active.find((entry) => entry.change_id === parent)?.root_change ?? parent;
118
+ for (const entry of [...value.active, ...value.archived]) {
119
+ if (!ids.includes(entry.change_id)) continue;
120
+ entry.parent_change = parent;
121
+ entry.root_change = root;
122
+ entry.locator = entry.locator;
123
+ entry.updated_at = now;
124
+ }
125
+ await writeFile(path, JSON.stringify(value, null, 2) + "\n", "utf8");
126
+ }
127
+
128
+ async function rollbackManifest(manifestPath) {
129
+ const manifest = await json(manifestPath, null);
130
+ if (!manifest || !Array.isArray(manifest.moves)) throw new Error("invalid relocation manifest");
131
+ for (const move of [...manifest.moves].reverse()) {
132
+ if (existsSync(move.to) && !existsSync(move.from)) await rename(move.to, move.from);
133
+ }
134
+ await writeFile(manifestPath, JSON.stringify({ ...manifest, rolled_back_at: new Date().toISOString() }, null, 2) + "\n", "utf8");
135
+ console.log(JSON.stringify({ status: "rolled-back", manifest: manifestPath }));
136
+ }
137
+
138
+ async function main() {
139
+ const options = args(process.argv.slice(2));
140
+ if (options.rollback) {
141
+ await rollbackManifest(options.rollback);
142
+ return;
143
+ }
144
+ const changesRoot = join(options.stateRoot, "changes");
145
+ const parentPath = join(changesRoot, options.parent);
146
+ if (!existsSync(parentPath)) throw new Error(`parent Change does not exist: ${options.parent}`);
147
+ const { path: locationsPath, value: locations } = await loadLocations(options.stateRoot);
148
+ const moves = [];
149
+ for (const id of options.sources) {
150
+ const entry = locations.entries.find((candidate) => candidate.change_id === id);
151
+ const fromLocator = locatorFor(entry, options.stateRoot);
152
+ if (!fromLocator.startsWith("changes/")) throw new Error(`source is not an active/closed Change: ${id}`);
153
+ const from = join(options.stateRoot, ...fromLocator.split("/"));
154
+ if (!existsSync(from)) throw new Error(`source Change is missing: ${fromLocator}`);
155
+ const relativeFrom = fromLocator.split("/");
156
+ if (relativeFrom.slice(0, 2).join("/") === `changes/${options.parent}` || from === parentPath || parentPath.startsWith(from + sep)) {
157
+ throw new Error(`source/parent cycle detected for ${id}`);
158
+ }
159
+ const to = join(parentPath, "children", id);
160
+ if (existsSync(to)) throw new Error(`target child already exists: ${id}`);
161
+ moves.push({ id, from, to, from_locator: fromLocator, to_locator: `changes/${options.parent}/children/${id}` });
162
+ }
163
+ const hashes = [];
164
+ for (const move of moves) hashes.push({ id: move.id, content_hash: await contentHash(move.from) });
165
+ const now = new Date().toISOString();
166
+ const manifest = {
167
+ schema_version: 1,
168
+ artifact: "learning-relocation-manifest",
169
+ parent_change: options.parent,
170
+ created_at: now,
171
+ moves: moves.map((move) => ({ ...move, content_hash: hashes.find((item) => item.id === move.id).content_hash })),
172
+ };
173
+ if (!options.apply) {
174
+ console.log(JSON.stringify({ status: "dry-run", ...manifest }, null, 2));
175
+ return;
176
+ }
177
+
178
+ await mkdir(join(parentPath, "children"), { recursive: true });
179
+ const lockPath = join(options.stateRoot, ".consolidate.lock");
180
+ let lock;
181
+ try {
182
+ lock = await open(lockPath, "wx");
183
+ for (const move of moves) await rename(move.from, move.to);
184
+ for (const move of manifest.moves) {
185
+ updateLocation(locations, move.id, move.from_locator, move.to_locator, move.content_hash, now, options.parent);
186
+ await updateStatus(join(options.stateRoot, ...move.to_locator.split("/")), move.id, move.to_locator, options.parent, now);
187
+ }
188
+ await writeFile(locationsPath, JSON.stringify(locations, null, 2) + "\n", "utf8");
189
+ await updateGlobalStatus(options.stateRoot, options.sources, options.parent, now);
190
+ const manifestPath = join(parentPath, "synthesis", `relocation-${now.replaceAll(/[^0-9]/g, "").slice(0, 14)}.json`);
191
+ await mkdir(dirname(manifestPath), { recursive: true });
192
+ await writeFile(manifestPath, JSON.stringify(manifest, null, 2) + "\n", "utf8");
193
+ console.log(JSON.stringify({ status: "applied", manifest: manifestPath, moves: manifest.moves }, null, 2));
194
+ } catch (error) {
195
+ for (const move of [...moves].reverse()) {
196
+ if (existsSync(move.to) && !existsSync(move.from)) await rename(move.to, move.from);
197
+ }
198
+ throw error;
199
+ } finally {
200
+ if (lock) await lock.close();
201
+ await rm(lockPath, { force: true });
202
+ }
203
+ }
204
+
205
+ main().catch((error) => {
206
+ console.error(`Learning relocation failed: ${error.message}`);
207
+ process.exitCode = 1;
208
+ });