@namewta/speculo 1.0.16 → 1.0.17

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 (79) hide show
  1. package/README.md +17 -5
  2. package/dist/src/agent-files.d.ts +9 -0
  3. package/dist/src/agent-files.js +106 -0
  4. package/dist/src/agent-files.js.map +1 -0
  5. package/dist/src/cli.js +64 -14
  6. package/dist/src/cli.js.map +1 -1
  7. package/dist/src/doctor.d.ts +2 -0
  8. package/dist/src/doctor.js +120 -53
  9. package/dist/src/doctor.js.map +1 -1
  10. package/dist/src/external-files.d.ts +22 -0
  11. package/dist/src/external-files.js +110 -0
  12. package/dist/src/external-files.js.map +1 -0
  13. package/dist/src/index.d.ts +3 -0
  14. package/dist/src/index.js +29 -182
  15. package/dist/src/index.js.map +1 -1
  16. package/dist/src/paths.d.ts +12 -0
  17. package/dist/src/paths.js +57 -0
  18. package/dist/src/paths.js.map +1 -0
  19. package/dist/src/transaction.d.ts +27 -0
  20. package/dist/src/transaction.js +187 -0
  21. package/dist/src/transaction.js.map +1 -0
  22. package/dist/src/utils.js +4 -2
  23. package/dist/src/utils.js.map +1 -1
  24. package/dist/src/workflows.js +3 -4
  25. package/dist/src/workflows.js.map +1 -1
  26. package/package.json +6 -4
  27. package/template/.speculo/README.md +9 -3
  28. package/template/.speculo/refresh-contract.json +6 -2
  29. package/template/AGENTS.md +1 -1
  30. package/template/commands/docs-sync.md +13 -11
  31. package/template/skills/archive-and-consolidate/SKILL.md +2 -4
  32. package/template/skills/docs-sync/SKILL.md +22 -11
  33. package/template/skills/docs-sync/references/agents-contract.md +6 -4
  34. package/template/skills/docs-sync/references/document-lifecycle-contract.md +2 -0
  35. package/template/skills/docs-sync/references/entry-procedure.md +2 -17
  36. package/template/skills/docs-sync/references/git-state-contract.md +1 -1
  37. package/template/skills/docs-sync/references/mode-contract.md +21 -0
  38. package/template/skills/docs-sync/references/workflow-scope-contract.md +5 -3
  39. package/template/skills/engineering-standards-builder/SKILL.md +3 -1
  40. package/template/skills/engineering-standards-builder/scripts/validate-builder.mjs +5 -1
  41. package/template/skills/git-history-squash/SKILL.md +3 -3
  42. package/template/skills/github-npm-ops/SKILL.md +2 -4
  43. package/template/skills/source-code-zip/SKILL.md +0 -1
  44. package/template/skills/speculo-retro/SKILL.md +2 -4
  45. package/template/skills/upstream-fork-sync/SKILL.md +1 -3
  46. package/template/skills/writing-great-skills/SKILL.md +3 -3
  47. package/template/workflows/learning/A-archive/A-archive.md +2 -0
  48. package/template/workflows/learning/A-assess-and-plan/A-assess-and-plan.md +2 -0
  49. package/template/workflows/learning/C-consolidate/C-consolidate.md +2 -0
  50. package/template/workflows/learning/G-goal/G-goal.md +6 -0
  51. package/template/workflows/learning/G-goal/references/external-goal-runner.md +4 -0
  52. package/template/workflows/learning/H-homework/H-homework.md +2 -0
  53. package/template/workflows/learning/L-lesson/L-lesson.md +2 -0
  54. package/template/workflows/learning/Q-question/Q-question.md +2 -0
  55. package/template/workflows/learning/R-review/R-review.md +2 -0
  56. package/template/workflows/learning/README.md +4 -53
  57. package/template/workflows/learning/common/rules/artifact-layouts.md +46 -0
  58. package/template/workflows/learning/common/rules/consolidation-contract.md +5 -0
  59. package/template/workflows/learning/common/rules/homework-contract.md +3 -0
  60. package/template/workflows/learning/common/rules/lesson-contract.md +7 -0
  61. package/template/workflows/learning/common/skills/socratic-questioning/SKILL.md +1 -0
  62. package/template/workflows/learning/common/skills/topic-synthesis/SKILL.md +1 -0
  63. package/template/workflows/ops/README.md +7 -3
  64. package/template/workflows/ops/_state/status.json +2 -1
  65. package/template/workflows/ops/common/USAGE.md +4 -0
  66. package/template/workflows/ops/common/rules/persistence-and-secrets.md +4 -2
  67. package/template/workflows/ops/common/schemas/plan.schema.json +4 -0
  68. package/template/workflows/ops/common/schemas/spec.schema.json +4 -0
  69. package/template/workflows/ops/common/schemas/status.schema.json +4 -0
  70. package/template/workflows/ops/common/tests/test_ops.mjs +33 -2
  71. package/template/workflows/ops/common/tools/opslib/core.mjs +1 -1
  72. package/template/workflows/ops/common/tools/opslib/docs.mjs +12 -7
  73. package/template/workflows/ops/common/tools/opslib/planner.mjs +7 -1
  74. package/template/workflows/specdev/common/rules/path-reference-contract.md +6 -0
  75. package/template/workflows/specdev/common/skills/code-review/SKILL.md +3 -1
  76. package/template/workflows/specdev/common/skills/dev-worktree/SKILL.md +1 -0
  77. package/template/workflows/specdev/common/skills/plan-quality-review/SKILL.md +3 -1
  78. package/template/workflows/specdev/common/skills/research/SKILL.md +3 -1
  79. package/template/workflows/specdev/common/skills/subagent-delivery/SKILL.md +1 -0
@@ -1,11 +1,13 @@
1
1
  ---
2
2
  name: engineering-standards-builder
3
3
  description: Generate or refresh project-specific engineering skills after the user explicitly invokes the builder.
4
- disable-model-invocation: true
4
+ metadata: {"speculo-invocation": "user-only"}
5
5
  ---
6
6
 
7
7
  # engineering-standards-builder
8
8
 
9
+ Activation is explicit-only. Metadata is descriptive; enforce host permissions and obtain user authorization before effects.
10
+
9
11
  This file is the routing entry. Read [`references/entry-procedure.md`](references/entry-procedure.md) only after this skill is selected. Read a named reference there only for the active branch.
10
12
 
11
13
  ## Scope
@@ -141,7 +141,11 @@ async function main() {
141
141
  if (frontmatter.values.name !== 'engineering-standards-builder') errors.push('stable Skill ID must remain engineering-standards-builder for drop-in replacement');
142
142
  if (!frontmatter.values.description) errors.push('frontmatter description is missing');
143
143
  if (/[<>]/.test(frontmatter.values.description ?? '')) errors.push('frontmatter description must not contain angle brackets');
144
- if (frontmatter.values['disable-model-invocation'] !== 'true') errors.push('Builder must remain explicitly user-invoked with disable-model-invocation: true');
144
+ let metadata = {};
145
+ try { metadata = JSON.parse(frontmatter.values.metadata ?? '{}'); }
146
+ catch { errors.push('Builder metadata must use the emitted JSON-flow mapping'); }
147
+ if (metadata['speculo-invocation'] !== 'user-only') errors.push('Builder must remain explicitly user-invoked with metadata.speculo-invocation=user-only');
148
+ if (!skillText.includes('Activation is explicit-only.')) errors.push('Builder must retain its visible explicit user selection boundary');
145
149
  }
146
150
  const contractText = entryProcedure ? await readFile(entryProcedure.abs, 'utf8') : '';
147
151
  for (const requiredConcept of ['Agent Team', 'generated-skill-set.json', '最小原则', 'Skill 边界']) {
@@ -1,13 +1,13 @@
1
1
  ---
2
- id: git-history-squash
3
- type: skill
4
2
  name: git-history-squash
5
3
  description: Plan and execute a confirmed first-parent Git history squash with recoverable refs and exact remote leases; never auto-trigger.
6
- disable-model-invocation: true
4
+ metadata: {"speculo-id": "git-history-squash", "speculo-kind": "skill", "speculo-invocation": "user-only"}
7
5
  ---
8
6
 
9
7
  # git-history-squash
10
8
 
9
+ Activation is explicit-only. Metadata is descriptive; enforce host permissions and obtain user authorization before effects.
10
+
11
11
  This file is the routing entry. Read [`references/entry-procedure.md`](references/entry-procedure.md) only after this skill is selected. Read a named reference there only for the active branch.
12
12
 
13
13
  ## Scope
@@ -1,9 +1,7 @@
1
1
  ---
2
- id: github-npm-ops
3
- type: skill
4
- name: GitHub npm Ops
2
+ name: github-npm-ops
5
3
  description: Perform a requested GitHub issue/PR/CI/security or npm release operation with its matching reference contract.
6
-
4
+ metadata: {"speculo-id": "github-npm-ops", "speculo-kind": "skill", "speculo-legacy-name": "GitHub npm Ops"}
7
5
  ---
8
6
 
9
7
  # GitHub npm Ops
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  name: source-code-zip
3
3
  description: Create a requested source ZIP using the bundled Node script and its ignore rules; do not install dependencies.
4
-
5
4
  ---
6
5
 
7
6
  # source-code-zip
@@ -1,9 +1,7 @@
1
1
  ---
2
- id: speculo-retro
3
- type: skill
4
- name: Speculo Retro
2
+ name: speculo-retro
5
3
  description: Extract and deduplicate friction from Speculo evidence into issue proposals; do not create issues without command confirmation.
6
-
4
+ metadata: {"speculo-id": "speculo-retro", "speculo-kind": "skill", "speculo-legacy-name": "Speculo Retro"}
7
5
  ---
8
6
 
9
7
  # Speculo Retro
@@ -1,9 +1,7 @@
1
1
  ---
2
- id: upstream-fork-sync
3
- type: skill
4
2
  name: upstream-fork-sync
5
3
  description: Assess a requested fork/upstream checkpoint and produce reproducible diff, conflict, and customization-risk evidence.
6
-
4
+ metadata: {"speculo-id": "upstream-fork-sync", "speculo-kind": "skill"}
7
5
  ---
8
6
 
9
7
  # upstream-fork-sync
@@ -1,13 +1,13 @@
1
1
  ---
2
- id: writing-great-skills
3
- type: skill
4
2
  name: writing-great-skills
5
3
  description: Reference guidance for explicitly requested skill authoring or review; never auto-trigger.
6
- disable-model-invocation: true
4
+ metadata: {"speculo-id": "writing-great-skills", "speculo-kind": "skill", "speculo-invocation": "user-only"}
7
5
  ---
8
6
 
9
7
  # writing-great-skills
10
8
 
9
+ Activation is explicit-only. Metadata is descriptive; enforce host permissions and obtain user authorization before effects.
10
+
11
11
  This file is the routing entry. Read [`references/entry-procedure.md`](references/entry-procedure.md) only after this skill is selected. Read a named reference there only for the active branch.
12
12
 
13
13
  ## Scope
@@ -13,6 +13,8 @@ keywords: [archive, cold-archive, close]
13
13
 
14
14
  ## 读取范围
15
15
 
16
+ 当前步骤开始前,读取 `<Path>{roots.workflows}/learning/common/rules/consolidation-contract.md</Path>`、`<Path>{roots.workflows}/learning/common/rules/artifact-layouts.md</Path>`;它们是本步骤必需合同,不默认加载其他分支。
17
+
16
18
  1. 先读取 `<Path>{roots.workflows}/learning/README.md</Path>` 与当前 Work 的状态入口。
17
19
  2. 再读取 `<Path>{roots.workflows}/learning/common/rules/activation-and-memory.md</Path>`,按当前分支、状态和关键词定位最小相关工件。
18
20
  3. 只在本 Work 明确要求恢复、冲突、执行安全或归档证据时扩展为全量读取;缺少匹配证据或 owner/gateway 时停止受影响分支。
@@ -13,6 +13,8 @@ keywords: [评估, baseline, course-design, objectives, background]
13
13
 
14
14
  ## 读取范围
15
15
 
16
+ 当前步骤开始前,读取 `<Path>{roots.workflows}/learning/common/rules/artifact-layouts.md</Path>`;它们是本步骤必需合同,不默认加载其他分支。
17
+
16
18
  1. 先读取 `<Path>{roots.workflows}/learning/README.md</Path>` 与当前 Work 的状态入口。
17
19
  2. 再读取 `<Path>{roots.workflows}/learning/common/rules/activation-and-memory.md</Path>`,按当前分支、状态和关键词定位最小相关工件。
18
20
  3. 只在本 Work 明确要求恢复、冲突、执行安全或归档证据时扩展为全量读取;缺少匹配证据或 owner/gateway 时停止受影响分支。
@@ -13,6 +13,8 @@ keywords: [consolidate, synthesis, provenance, topic, relocate]
13
13
 
14
14
  ## 读取范围
15
15
 
16
+ 当前步骤开始前,读取 `<Path>{roots.workflows}/learning/common/rules/consolidation-contract.md</Path>`、`<Path>{roots.workflows}/learning/common/rules/artifact-layouts.md</Path>`;它们是本步骤必需合同,不默认加载其他分支。
17
+
16
18
  1. 先读取 `<Path>{roots.workflows}/learning/README.md</Path>` 与当前 Work 的状态入口。
17
19
  2. 再读取 `<Path>{roots.workflows}/learning/common/rules/activation-and-memory.md</Path>`,按当前分支、状态和关键词定位最小相关工件。
18
20
  3. 只在本 Work 明确要求恢复、冲突、执行安全或归档证据时扩展为全量读取;缺少匹配证据或 owner/gateway 时停止受影响分支。
@@ -15,6 +15,8 @@ G-goal 是计划编译器,不是授课编排器。激活后写出完整 Goal-P
15
15
 
16
16
  ## 读取范围
17
17
 
18
+ 当前步骤开始前,读取 `<Path>{roots.workflows}/learning/common/rules/lesson-contract.md</Path>`;它们是本步骤必需合同,不默认加载其他分支。
19
+
18
20
  1. 先读取 `<Path>{roots.workflows}/learning/README.md</Path>` 与当前 Work 的状态入口。
19
21
  2. 再读取 `<Path>{roots.workflows}/learning/common/rules/activation-and-memory.md</Path>`,按当前 Change、项目路径和关键词定位最小相关工件。
20
22
  3. 只在本 Work 明确要求恢复、冲突、执行安全、创建 Change 或归档证据时扩展为全量读取;缺少匹配证据或 owner/gateway 时停止受影响分支。
@@ -72,3 +74,7 @@ G-goal 是计划编译器,不是授课编排器。激活后写出完整 Goal-P
72
74
  - 覆盖尺:`<Path>{roots.workflows}/learning/G-goal/references/coverage-bar.md</Path>`
73
75
  - 停止规则:`<Path>{roots.workflows}/learning/G-goal/references/stop-rules.md</Path>`
74
76
  - 外部执行器:`<Path>{roots.workflows}/learning/G-goal/references/external-goal-runner.md</Path>`
77
+
78
+ ## 无 slash-command 宿主
79
+
80
+ `/goal` 是宿主适配示例,不是额外授权源。宿主没有该命令时,用户可在新执行会话明确请求“按此 Goal-Plan 执行”,同时提供计划路径;该会话成为外部执行器并遵守相同最小读取清单、owner、停止和验收合同。计划会话仍只编译并停止,不自动接管执行。无 team 时按原派单包串行执行;缺必需文件或工具只阻塞受影响分支,不伪造子代理调用。30–40 分钟、每单元 ≤15 节与每课最多10问仍是 Learning 领域合同,不传播为全局 Agent 限制。
@@ -48,3 +48,7 @@
48
48
  ## 停止
49
49
 
50
50
  读 `<Path>{roots.workflows}/learning/G-goal/references/stop-rules.md</Path>`。任一过程即停或完成即停触发后,写 `goal/verify.md`:哪些格子闭合、哪些 deferred、哪些仍 uncovered、恢复入口。课写完不是完成。
51
+
52
+ ## 无 slash-command 宿主
53
+
54
+ `/goal` 是宿主适配示例,不是额外授权源。宿主没有该命令时,用户可在新执行会话明确请求“按此 Goal-Plan 执行”,同时提供计划路径;该会话成为外部执行器并遵守相同最小读取清单、owner、停止和验收合同。计划会话仍只编译并停止,不自动接管执行。无 team 时按原派单包串行执行;缺必需文件或工具只阻塞受影响分支,不伪造子代理调用。30–40 分钟、每单元 ≤15 节与每课最多10问仍是 Learning 领域合同,不传播为全局 Agent 限制。
@@ -13,6 +13,8 @@ keywords: [homework, assignment, questions, grading, explain]
13
13
 
14
14
  ## 读取范围
15
15
 
16
+ 当前步骤开始前,读取 `<Path>{roots.workflows}/learning/common/rules/homework-contract.md</Path>`;它们是本步骤必需合同,不默认加载其他分支。
17
+
16
18
  1. 先读取 `<Path>{roots.workflows}/learning/README.md</Path>` 与当前 Work 的状态入口。
17
19
  2. 再读取 `<Path>{roots.workflows}/learning/common/rules/activation-and-memory.md</Path>`,按当前分支、状态和关键词定位最小相关工件。
18
20
  3. 只在本 Work 明确要求恢复、冲突、执行安全或归档证据时扩展为全量读取;缺少匹配证据或 owner/gateway 时停止受影响分支。
@@ -13,6 +13,8 @@ keywords: [lesson, 教学, eli5, 图文, explanation]
13
13
 
14
14
  ## 读取范围
15
15
 
16
+ 当前步骤开始前,读取 `<Path>{roots.workflows}/learning/common/rules/lesson-contract.md</Path>`;它们是本步骤必需合同,不默认加载其他分支。
17
+
16
18
  1. 先读取 `<Path>{roots.workflows}/learning/README.md</Path>` 与当前 Work 的状态入口。
17
19
  2. 再读取 `<Path>{roots.workflows}/learning/common/rules/activation-and-memory.md</Path>`,按当前分支、状态和关键词定位最小相关工件。
18
20
  3. 只在本 Work 明确要求恢复、冲突、执行安全或归档证据时扩展为全量读取;缺少匹配证据或 owner/gateway 时停止受影响分支。
@@ -13,6 +13,8 @@ keywords: [反问, 苏格拉底, socratic, inquiry, 提问课, questioning, 激
13
13
 
14
14
  ## 读取范围
15
15
 
16
+ 当前步骤开始前,读取 `<Path>{roots.workflows}/learning/common/rules/artifact-layouts.md</Path>`;它们是本步骤必需合同,不默认加载其他分支。
17
+
16
18
  1. 先读取 `<Path>{roots.workflows}/learning/README.md</Path>` 与当前 Work 的状态入口。
17
19
  2. 再读取 `<Path>{roots.workflows}/learning/common/rules/activation-and-memory.md</Path>`,按当前分支、状态和关键词定位最小相关工件。
18
20
  3. 只在本 Work 明确要求恢复、冲突、执行安全、创建 Change 或归档证据时扩展为全量读取;缺少匹配证据或 owner/gateway 时停止受影响分支。
@@ -13,6 +13,8 @@ keywords: [复习, retention, spaced-review, transfer]
13
13
 
14
14
  ## 读取范围
15
15
 
16
+ 当前步骤开始前,读取 `<Path>{roots.workflows}/learning/common/rules/homework-contract.md</Path>`;它们是本步骤必需合同,不默认加载其他分支。
17
+
16
18
  1. 先读取 `<Path>{roots.workflows}/learning/README.md</Path>` 与当前 Work 的状态入口。
17
19
  2. 再读取 `<Path>{roots.workflows}/learning/common/rules/activation-and-memory.md</Path>`,按当前分支、状态和关键词定位最小相关工件。
18
20
  3. 只在本 Work 明确要求恢复、冲突、执行安全或归档证据时扩展为全量读取;缺少匹配证据或 owner/gateway 时停止受影响分支。
@@ -52,50 +52,7 @@ Any closed root tree --(user chooses A)--> archive/YYYY-MM/<change>
52
52
 
53
53
  ## Change 工件布局
54
54
 
55
- 普通学习 Change 至少包含:
56
-
57
- ```text
58
- changes/<change-id>/
59
- INDEX.md
60
- course.md
61
- background/foundation.md
62
- baseline.md
63
- sources.md
64
- lessons/INDEX.md
65
- lessons/L-001-<slug>.md
66
- homework/INDEX.md
67
- homework/HW-001-<slug>-attempt-01.md
68
- inquiry/INDEX.md
69
- inquiry/IQ-001-<slug>-batch-01.md
70
- goal/goal-plan.md
71
- goal/chain.md
72
- goal/coverage-matrix.md
73
- goal/progress.md
74
- goal/probes/
75
- notes/
76
- learning-log.md
77
- .status.json
78
- ```
79
-
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
-
82
- 综合父 Change 使用:
83
-
84
- ```text
85
- changes/<topic>-consolidation/
86
- INDEX.md
87
- .status.json
88
- children/<child-id>/ # C 确认后物理搬入,内容字节不改写
89
- synthesis/INDEX.md
90
- synthesis/source-manifest.json
91
- synthesis/overview.md
92
- synthesis/claim-matrix.md
93
- synthesis/concept-map.md
94
- synthesis/conflicts-and-gaps.md
95
- synthesis/revisions/<version>.md
96
- ```
97
-
98
- 子 Change 的 Lesson、Homework 和后续 Markdown 仍写入 `children/<child-id>/`;父 Change 负责根级锁、位置登记和路由,子 Change 仍是这些工件的 owner。综合输出是可重建的派生视图,不覆盖原始课程、答案或评审。
55
+ 创建、恢复或物理移动 Change 时,必须读取 `<Path>{roots.workflows}/learning/common/rules/artifact-layouts.md</Path>`;其他 Work 不加载该分支。该引用保留完整合同,不改变数量、所有权、权限与完成标准。
99
56
 
100
57
  ## 状态字段
101
58
 
@@ -115,21 +72,15 @@ Workflow 自身只读模板;Change 内容只写当前 Change 或其 `children/
115
72
 
116
73
  ## 课程合同
117
74
 
118
- `L-lesson` 的每份 Lesson 必须有 `lesson_id`、`objective_ids`、`estimated_minutes`(默认 35,标准范围 30–40)、可加总的 `time_budget`、`expression_level`、`coverage_depth` 和 `source_ids`。时间按阅读/视觉/示例/停顿/总结等活动估算,不按字符数承诺。章节顺序可以随主题变化,但每个核心目标都必须有动机与宏观图、通俗直觉、精确定义和英文术语、机制/因果链、至少一种视觉表示及其完整文字等价物、正例、反例或边界、迁移说明、误区、总结和来源。类比必须标出失效边界。
119
-
120
- `expression_level=eli5|plain` 只控制词汇、句法、脚手架和类比比例;`coverage_depth=overview|standard|deep` 控制覆盖强度。Lesson 可放非评分的 pause/self-check,但不得生成 Q/A、答案、分数、verdict 或 mastered 字段。外部图片只是可选增强,必须有 alt、caption、source、访问日期和文字等价物,课程不能依赖链接可用性。
121
-
122
- 学习者苏格拉底批次只允许出现在 `Q-question` 拥有的 `inquiry/` 内。`G-goal` 的 `audience=mine` probes 写入 `goal/probes/`,不是 Lesson Q/A,且不占用 30–40 分钟 Lesson 预算。每课最多 10 问(两批 5 槽);满 10 后只拆课,不续问。
75
+ L 授课、G 编译授课合同或验收 Lesson 时,必须读取 `<Path>{roots.workflows}/learning/common/rules/lesson-contract.md</Path>`;其他 Work 不加载该分支。该引用保留完整合同,不改变数量、所有权、权限与完成标准。
123
76
 
124
77
  ## Homework 合同
125
78
 
126
- `H-homework` 默认生成五题,覆盖回忆/定义、机制解释、变式应用、全新情境迁移和误区辨析;数量可由用户指定。一个 `HW-...-attempt-NN.md` 按以下顺序包含元数据、Q1…、空白 A1…、`Submission: pending`。学习者填写答案后必须显式写入 `Submission: ready` 并再次激活 H。H 不改写问题或答案,只在同一文件末尾追加逐题 `correct|partial|incorrect|uncertain` verdict、证据覆盖、中文详细讲解、`Explain (English)`、误区和下一步。评审后文件冻结;重答创建新的 attempt 文件并链接旧文件。H 可更新 immediate projection,但不把内容标记为 mastered,也不自动路由其他 Work。
79
+ H 生成/评审作业或 R 引用作业证据时,必须读取 `<Path>{roots.workflows}/learning/common/rules/homework-contract.md</Path>`;其他 Work 不加载该分支。该引用保留完整合同,不改变数量、所有权、权限与完成标准。
127
80
 
128
81
  ## 主题整合与冷归档
129
82
 
130
- `C-consolidate` 接受用户选定的 active 或 closed、尚未冷归档的 Change;已冷归档树保持不可变,只能先由用户显式恢复后再参与。C 先输出包含源 ID、当前/旧 locator、时间、哈希、关系、冲突和目标 topic 的 dry-run,用户确认后在锁内原子移动整个目录到 `children/<child-id>/`,失败则回滚。不得选择祖先与后代形成循环,也不得拆开已有综合子树。
131
-
132
- 综合 claim 必须带 `source_change_id`、Lesson/Homework anchor、外部 `source_id`、evidence status(`draft|supported|contested|unresolved`)和验证时间;C 只有在第二次确认后才更新 `context/domains/<domain>/topics/<topic-id>/`。父 Change 的 effective date 是所有选中源 `updated_at` 的最大值;原始创建/更新时间仍保留。A 只接受用户 close/confirm,在没有活动子树和根锁后把整个树移动到 `archive/YYYY-MM/<root-change>`;不检查作业或掌握,不做综合。
83
+ C 整合、A 归档或恢复物理移动时,必须读取 `<Path>{roots.workflows}/learning/common/rules/consolidation-contract.md</Path>`;其他 Work 不加载该分支。该引用保留完整合同,不改变数量、所有权、权限与完成标准。
133
84
 
134
85
  ## 破坏式升级
135
86
 
@@ -0,0 +1,46 @@
1
+ # Change 工件布局
2
+
3
+ 普通学习 Change 至少包含:
4
+
5
+ ```text
6
+ changes/<change-id>/
7
+ INDEX.md
8
+ course.md
9
+ background/foundation.md
10
+ baseline.md
11
+ sources.md
12
+ lessons/INDEX.md
13
+ lessons/L-001-<slug>.md
14
+ homework/INDEX.md
15
+ homework/HW-001-<slug>-attempt-01.md
16
+ inquiry/INDEX.md
17
+ inquiry/IQ-001-<slug>-batch-01.md
18
+ goal/goal-plan.md
19
+ goal/chain.md
20
+ goal/coverage-matrix.md
21
+ goal/progress.md
22
+ goal/probes/
23
+ notes/
24
+ learning-log.md
25
+ .status.json
26
+ ```
27
+
28
+ `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/`。
29
+
30
+ 综合父 Change 使用:
31
+
32
+ ```text
33
+ changes/<topic>-consolidation/
34
+ INDEX.md
35
+ .status.json
36
+ children/<child-id>/ # C 确认后物理搬入,内容字节不改写
37
+ synthesis/INDEX.md
38
+ synthesis/source-manifest.json
39
+ synthesis/overview.md
40
+ synthesis/claim-matrix.md
41
+ synthesis/concept-map.md
42
+ synthesis/conflicts-and-gaps.md
43
+ synthesis/revisions/<version>.md
44
+ ```
45
+
46
+ 子 Change 的 Lesson、Homework 和后续 Markdown 仍写入 `children/<child-id>/`;父 Change 负责根级锁、位置登记和路由,子 Change 仍是这些工件的 owner。综合输出是可重建的派生视图,不覆盖原始课程、答案或评审。
@@ -0,0 +1,5 @@
1
+ # 主题整合与冷归档
2
+
3
+ `C-consolidate` 接受用户选定的 active 或 closed、尚未冷归档的 Change;已冷归档树保持不可变,只能先由用户显式恢复后再参与。C 先输出包含源 ID、当前/旧 locator、时间、哈希、关系、冲突和目标 topic 的 dry-run,用户确认后在锁内原子移动整个目录到 `children/<child-id>/`,失败则回滚。不得选择祖先与后代形成循环,也不得拆开已有综合子树。
4
+
5
+ 综合 claim 必须带 `source_change_id`、Lesson/Homework anchor、外部 `source_id`、evidence status(`draft|supported|contested|unresolved`)和验证时间;C 只有在第二次确认后才更新 `context/domains/<domain>/topics/<topic-id>/`。父 Change 的 effective date 是所有选中源 `updated_at` 的最大值;原始创建/更新时间仍保留。A 只接受用户 close/confirm,在没有活动子树和根锁后把整个树移动到 `archive/YYYY-MM/<root-change>`;不检查作业或掌握,不做综合。
@@ -0,0 +1,3 @@
1
+ # Homework 合同
2
+
3
+ `H-homework` 默认生成五题,覆盖回忆/定义、机制解释、变式应用、全新情境迁移和误区辨析;数量可由用户指定。一个 `HW-...-attempt-NN.md` 按以下顺序包含元数据、Q1…、空白 A1…、`Submission: pending`。学习者填写答案后必须显式写入 `Submission: ready` 并再次激活 H。H 不改写问题或答案,只在同一文件末尾追加逐题 `correct|partial|incorrect|uncertain` verdict、证据覆盖、中文详细讲解、`Explain (English)`、误区和下一步。评审后文件冻结;重答创建新的 attempt 文件并链接旧文件。H 可更新 immediate projection,但不把内容标记为 mastered,也不自动路由其他 Work。
@@ -0,0 +1,7 @@
1
+ # 课程合同
2
+
3
+ `L-lesson` 的每份 Lesson 必须有 `lesson_id`、`objective_ids`、`estimated_minutes`(默认 35,标准范围 30–40)、可加总的 `time_budget`、`expression_level`、`coverage_depth` 和 `source_ids`。时间按阅读/视觉/示例/停顿/总结等活动估算,不按字符数承诺。章节顺序可以随主题变化,但每个核心目标都必须有动机与宏观图、通俗直觉、精确定义和英文术语、机制/因果链、至少一种视觉表示及其完整文字等价物、正例、反例或边界、迁移说明、误区、总结和来源。类比必须标出失效边界。
4
+
5
+ `expression_level=eli5|plain` 只控制词汇、句法、脚手架和类比比例;`coverage_depth=overview|standard|deep` 控制覆盖强度。Lesson 可放非评分的 pause/self-check,但不得生成 Q/A、答案、分数、verdict 或 mastered 字段。外部图片只是可选增强,必须有 alt、caption、source、访问日期和文字等价物,课程不能依赖链接可用性。
6
+
7
+ 学习者苏格拉底批次只允许出现在 `Q-question` 拥有的 `inquiry/` 内。`G-goal` 的 `audience=mine` probes 写入 `goal/probes/`,不是 Lesson Q/A,且不占用 30–40 分钟 Lesson 预算。每课最多 10 问(两批 5 槽);满 10 后只拆课,不续问。
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: socratic-questioning
3
3
  description: 苏格拉底问答、反问授课或用提问教;当学习者卡住、不知道下一步、指定提问/5E/费曼/生产性失败,或 Q-question 需要出题与讲解配方时使用。
4
+ compatibility: Requires the owning Speculo workflow and its workspace path resolver.
4
5
  ---
5
6
 
6
7
  # Socratic Questioning
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: topic-synthesis
3
3
  description: 将 Learning Change 作为不可变原料,生成带 claim 级 provenance 的主题综合并显式发布。
4
+ compatibility: Requires the owning Speculo workflow and its workspace path resolver.
4
5
  ---
5
6
 
6
7
  # Topic Synthesis
@@ -35,7 +35,7 @@ host_root/
35
35
  _runtime/docker/ # 仅经准备/显式迁移的 Docker Engine
36
36
  app-a/
37
37
  README.md # 版本、时间、路径、依赖、启停、备份恢复
38
- OPERATIONS.md # 策略启用时:受限真实明文凭据
38
+ OPERATIONS.md # 默认 secret_ref;获批 opt-in 才导出受限明文
39
39
  project.yaml # JSON 格式(同时是有效 YAML)资源投影
40
40
  compose/compose.yaml # Docker 时;Dockerfile 同目录
41
41
  service/ # 原生部署定义
@@ -55,7 +55,7 @@ host_root/
55
55
 
56
56
  明确多实例时使用 `project/instances/environment/instance/`,每个实例重复上述自有布局;顶层 README 变为实例索引。单实例与多实例根不可重叠,不自动搬迁。
57
57
 
58
- 部署机对应记录固定为 `state_root/hosts/host_id/deployments/deployment_id/`,包含完整 README、OPERATIONS、deployment.json、server/README、server-files 配置副本与 docs-receipt。全域总册为 FLEET-DEPLOYMENTS.md,真实明文账本为 private/credentials.json。双边记录不等于自动复制业务数据。
58
+ 部署机对应记录固定为 `state_root/hosts/host_id/deployments/deployment_id/`,包含完整 README、OPERATIONS、deployment.json、server/README、server-files 配置副本与 docs-receipt。全域总册为 FLEET-DEPLOYMENTS.md,受限真实值账本为 private/credentials.json;新安装的文档默认只记录 secret_ref/版本。双边记录不等于自动复制业务数据。
59
59
 
60
60
  ## 启动协议
61
61
 
@@ -73,12 +73,16 @@ schema_version=3;hosts、projects、deployments、allocations、bindings、rel
73
73
 
74
74
  ## 路径分配
75
75
 
76
- 部署根由 host/project/layout 唯一派生。所有声明的 APP data/config/env/log/backups 路径都必须在该根内;容器只用显式 bind,禁止命名卷、匿名卷、跨项目 bind。默认只读容器根;仅当镜像仍必须写根文件系统时,才允许带 `writable_root_justification` 的 `read_only: false`。config/env 默认 0644 只读挂载,供非 root 镜像用户读取。项目 env 文件集中在 env/;Compose 使用 raw env_file,要求实际 Compose >=2.30。`compose --wait` 之后仍检查容器 running 与 Health=healthy;TCP/docker-proxy 监听不是生产健康证明。
76
+ 部署根由 host/project/layout 唯一派生。所有声明的 APP data/config/env/log/backups 路径都必须在该根内;容器只用显式 bind,禁止命名卷、匿名卷、跨项目 bind。默认只读容器根;仅当镜像仍必须写根文件系统时,才允许带 `writable_root_justification` 的 `read_only: false`。env、带 credential 占位符的配置和明文文档采用 0600;不含秘密的 config 默认 0644 只读挂载。不能为非 root 容器读取方便把秘密放宽到 world-readable,应在批准的部署计划中安排 owner/secret 注入。项目 env 文件集中在 env/;Compose 使用 raw env_file,要求实际 Compose >=2.30。`compose --wait` 之后仍检查容器 running 与 Health=healthy;TCP/docker-proxy 监听不是生产健康证明。
77
77
 
78
78
  原生服务设置 HOME、XDG、缓存、临时目录和 OPS_* 到 APP 根内;Linux systemd 还设置 ProtectSystem/ReadWritePaths。通用自定义命令是用户审核的可执行代码,不是一个能阻止恶意程序所有系统调用的沙箱。来源代码必须可信,必须明确映射项目真实数据参数,并实际验证;发现无法约束的数据路径就阻塞,不能报完成。
79
79
 
80
80
  systemd 单元等系统控制文件可有计划内的精确例外;业务持久化数据没有该例外。Docker 自身的运行数据固定为 host_root/_runtime/docker;既有 engine 不能被静默迁移。Docker Desktop 的隐藏虚拟机布局不自动等同于原生 Windows 根。
81
81
 
82
+ ## 凭据文档策略
83
+
84
+ 新控制端 `policies.plaintext_documentation=false`;README、OPERATIONS 与总册默认无真实值。需要受限明文交付时,在输入 spec 中明确设置 `plaintext_documentation: true`,它进入 registry_after 与精确计划批准摘要。旧状态缺字段仍保留旧行为,不由刷新自动改写;收紧时使用显式 false 的新批准计划。旧历史文档/回执不自动改写或删除,应另做授权清理。env 和受限 server-files 配置副本仍按原双边合同交付,不能把“文档脱敏”称作所有状态不含秘密。
85
+
82
86
  ## 副作用边界
83
87
 
84
88
  init/register/credential-put 是用户显式请求的部署机本地记录操作;probe/analyze 是有边界读取。source-fetch 与 mirror-probe 要求显式网络标志。其余目标修改全部先生成完整计划,用户确认精确摘要后执行,包括本地可逆动作。
@@ -12,7 +12,8 @@
12
12
  "policies": {
13
13
  "server_readme_credentials": false,
14
14
  "server_operations": true,
15
- "strict_docker_root": true
15
+ "strict_docker_root": true,
16
+ "plaintext_documentation": false
16
17
  },
17
18
  "updated_at": null
18
19
  }
@@ -129,3 +129,7 @@ node /path/to/ops/common/tools/validate-ops.mjs --self-check
129
129
  ```
130
130
 
131
131
  演练只使用指定新空目录,创建演示 APP 与演示明文凭据,运行真实有限 Node 程序,完成双边文档;不连接其他主机、不安装软件。它不是生产系统验收。
132
+
133
+ ## 凭据文档 opt-in
134
+
135
+ 新安装默认文档只含 secret_ref。输入 spec 的可选 `plaintext_documentation` 布尔值是受计划摘要保护的策略变更,设 true 才导出受限明文,设 false 可显式收紧旧状态。生成计划后照常批准精确摘要并执行;更改策略使旧批准失效。旧状态缺字段保留原语义,CLI 刷新不改历史凭据或回执。env 与 server-files 配置副本仍是受限真实配置,不属于无秘密文档承诺。
@@ -14,9 +14,11 @@ Docker 要求明确映射镜像声明的全部 VOLUME,实际启动后再次检
14
14
 
15
15
  private/credentials.json 是版本化明文账本;credential-put 不会修改服务器密码。相同版本不可覆盖,轮换先增加新版本,再通过独立部署/迁移计划应用与验证。引用形如 app-a-db@1,值占位形如 {{credential:app-a-db@1:password}}。
16
16
 
17
- 控制端项目 README、OPERATIONS 与全域总册保存真实值,特殊字符逐字保留。服务端 README 默认无密码但不能缺路径、版本、时间、启动停止、依赖和备份恢复;OPERATIONS 与 DEPLOYMENTS 默认是受限明文。未知旧密码不能编造;密钥认证没有密码,记录实际密钥认证而不是虚构一个口令。
17
+ 新安装的控制端项目 README、OPERATIONS 与全域总册默认仅保存 secret_ref、凭据版本与用途;只有获批 `plaintext_documentation: true` 的计划才逐字导出真实值。服务端 README 默认无密码但不能缺路径、版本、时间、启动停止、依赖和备份恢复;OPERATIONS 与 DEPLOYMENTS 默认只保存引用;显式 opt-in 时按各文档职责导出受限明文。未知旧密码不能编造;密钥认证没有密码,记录实际密钥认证而不是虚构一个口令。
18
18
 
19
- POSIX 账本、env 与明文文档 0600、控制端目录 0700。`config/` 与 `*.conf`/`*.acl` 默认 0644,供非 root 容器用户读取只读挂载;Windows 使用受限 ACL,不以 chmod 代替 DACL。明文不进入 Git、Web 目录、构建上下文、普通日志;env 是指定的本地配置文件例外,不能再进入镜像层。示例凭据全为演示,不可用于生产。
19
+ POSIX 账本、env 与明文文档 0600、控制端目录 0700。不含秘密的 `config/` 与 `*.conf`/`*.acl` 默认 0644;含 credential 占位符的配置自动收紧为 0600,显式宽权限会阻塞规划,不以非 root 读取为由放宽秘密权限;Windows 使用受限 ACL,不以 chmod 代替 DACL。明文不进入 Git、Web 目录、构建上下文、普通日志;env 是指定的本地配置文件例外,不能再进入镜像层。示例凭据全为演示,不可用于生产。
20
+
21
+ 旧状态缺 `plaintext_documentation` 字段时保留旧语义,新的 false/true 策略必须通过新计划与精确摘要批准;不复用旧批准,不回写历史 Run/Release。关闭导出不代表删除既有历史明文,历史清理需另行授权。服务 env 与受限 server-files 副本仍包含运行必需真实值;未知硬编码秘密不能仅依赖占位符检测,计划审核仍需人工/Agent 检查。
20
22
 
21
23
  ## 双边完成门
22
24
 
@@ -469,6 +469,10 @@
469
469
  },
470
470
  "strict_docker_root": {
471
471
  "const": true
472
+ },
473
+ "plaintext_documentation": {
474
+ "type": "boolean",
475
+ "description": "Explicit restricted plaintext document export. Missing on legacy state retains the prior policy; new states default false."
472
476
  }
473
477
  },
474
478
  "required": [
@@ -1155,6 +1155,10 @@
1155
1155
  "mappings"
1156
1156
  ],
1157
1157
  "additionalProperties": false
1158
+ },
1159
+ "plaintext_documentation": {
1160
+ "type": "boolean",
1161
+ "description": "Explicitly opt in/out of restricted plaintext document delivery; bound by the plan approval digest."
1158
1162
  }
1159
1163
  },
1160
1164
  "required": [
@@ -185,6 +185,10 @@
185
185
  },
186
186
  "strict_docker_root": {
187
187
  "const": true
188
+ },
189
+ "plaintext_documentation": {
190
+ "type": "boolean",
191
+ "description": "Explicit restricted plaintext document export. Missing on legacy state retains the prior policy; new states default false."
188
192
  }
189
193
  },
190
194
  "required": [
@@ -580,8 +580,9 @@ test("planner contracts", async (t) => {
580
580
  });
581
581
  });
582
582
 
583
- test("real native and dual docs", () => runFx((fx) => {
584
- const [info, , result] = fx.runSpec();
583
+ test("real native and explicitly approved plaintext dual docs", () => runFx((fx) => {
584
+ const input = fx.spec(); input.plaintext_documentation = true;
585
+ const [info, , result] = fx.runSpec(input);
585
586
  assert.equal(result.status, "completed", JSON.stringify(result));
586
587
  const dep = model.load(fx.state).deployments["app-a-prod"];
587
588
  assert.equal(dep.observed_version, "v1");
@@ -980,3 +981,33 @@ test("host services and public ingress are delivered", () => runFx((fx) => {
980
981
  assert.ok(fleet.includes("公网访问内网"));
981
982
  assert.ok(!fleet.includes("<code>"));
982
983
  }));
984
+
985
+
986
+ test("new controllers keep secrets out of default documentation", () => runFx((fx) => {
987
+ assert.equal(model.load(fx.state).policies.plaintext_documentation, false);
988
+ const [info, plan, result] = fx.runSpec();
989
+ assert.equal(result.status, "completed", JSON.stringify(result));
990
+ const remote = join(fx.target, "app-a");
991
+ const local = join(fx.state, "hosts", "node-a", "deployments", "app-a-prod");
992
+ for (const path of [join(remote,"README.md"), join(remote,"OPERATIONS.md"), join(local,"README.md"), join(local,"OPERATIONS.md"), join(fx.state,"FLEET-DEPLOYMENTS.md"), join(fx.target,"DEPLOYMENTS.md")]) {
993
+ assert.ok(!readFileSync(path,"utf8").includes(PASSWORD), path);
994
+ }
995
+ assert.match(readFileSync(join(local,"OPERATIONS.md"),"utf8"), /secret_ref:.*app-auth@1/);
996
+ assert.ok(readFileSync(join(remote,"env","app.env"),"utf8").includes(PASSWORD));
997
+ assert.equal(plan.operations.find((o) => o.path?.endsWith("env/app.env")).mode, 0o600);
998
+ if (process.platform !== "win32") assert.equal(statSync(join(remote,"env","app.env")).mode & 0o777, 0o600);
999
+ assert.equal(readJson(join(local,"docs-receipt.json")).status, "both-sides-verified");
1000
+ }));
1001
+
1002
+ test("legacy absent policy retains previous explicit plaintext behavior without migration", () => {
1003
+ const status = emptyStatus(); delete status.policies.plaintext_documentation;
1004
+ assert.equal(docs.plaintextDocumentation(status), true);
1005
+ status.policies.plaintext_documentation = false;
1006
+ assert.equal(docs.plaintextDocumentation(status), false);
1007
+ });
1008
+
1009
+ test("credential-bearing config rejects permissive explicit file modes", () => runFx((fx) => {
1010
+ const spec = fx.spec();
1011
+ spec.deployments[0].files.push({path:"config/auth.conf",content:"{{credential:app-auth@1:password}}",mode:0o644});
1012
+ assert.throws(() => fx.plan(spec), /secret-bearing|private|0600|permission/i);
1013
+ }));
@@ -397,7 +397,7 @@ export function emptyStatus() {
397
397
  allocations: {},
398
398
  bindings: {},
399
399
  releases: {},
400
- policies: { server_readme_credentials: false, server_operations: true, strict_docker_root: true },
400
+ policies: { server_readme_credentials: false, server_operations: true, strict_docker_root: true, plaintext_documentation: false },
401
401
  updated_at: null,
402
402
  };
403
403
  }