@namewta/speculo 1.0.18 → 1.0.19

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 (67) hide show
  1. package/package.json +5 -2
  2. package/template/canonical/canonical-specdev-goal-plan.md +2 -2
  3. package/template/canonical/canonical-specdev-grill-with-docs.md +2 -2
  4. package/template/canonical/canonical-specdev-spec.md +11 -136
  5. package/template/canonical/canonical-specdev-tickets.md +2 -2
  6. package/template/canonical/canonical-specdev-wayfinder.md +2 -2
  7. package/template/commands/handoff.md +16 -0
  8. package/template/commands/retro.md +1 -1
  9. package/template/skills/github-npm-ops/references/issue-pr-triage.md +8 -0
  10. package/template/skills/speculo-retro/references/entry-procedure.md +19 -3
  11. package/template/skills/speculo-retro/references/issue-drafting-sop.md +3 -1
  12. package/template/workflows/learning/A-archive/A-archive.md +2 -5
  13. package/template/workflows/learning/A-assess-and-plan/A-assess-and-plan.md +2 -5
  14. package/template/workflows/learning/C-consolidate/C-consolidate.md +2 -5
  15. package/template/workflows/learning/G-goal/G-goal.md +5 -23
  16. package/template/workflows/learning/H-homework/H-homework.md +2 -5
  17. package/template/workflows/learning/I-init-setup/I-init-setup.md +4 -9
  18. package/template/workflows/learning/L-lesson/L-lesson.md +2 -5
  19. package/template/workflows/learning/Q-question/Q-question.md +16 -8
  20. package/template/workflows/learning/Q-question/inquiry-template.md +2 -0
  21. package/template/workflows/learning/Q-question/tools/validate-inquiry.mjs +112 -0
  22. package/template/workflows/learning/R-review/R-review.md +3 -6
  23. package/template/workflows/learning/README.md +2 -2
  24. package/template/workflows/learning/common/rules/knowledge-organization.md +1 -1
  25. package/template/workflows/learning/common/rules/mastery-policy.md +1 -1
  26. package/template/workflows/learning/common/skills/socratic-questioning/SKILL.md +1 -1
  27. package/template/workflows/ops/D-project-deploy/D-project-deploy.md +6 -2
  28. package/template/workflows/ops/H-host-manage/H-host-manage.md +10 -18
  29. package/template/workflows/ops/H-host-manage/references/host-profiles.md +21 -0
  30. package/template/workflows/ops/I-initialize/I-initialize.md +4 -4
  31. package/template/workflows/ops/README.md +22 -84
  32. package/template/workflows/ops/S-server-connect/S-server-connect.md +3 -3
  33. package/template/workflows/ops/V-inventory-view/V-inventory-view.md +2 -2
  34. package/template/workflows/ops/common/rules/deployment-layout.md +51 -0
  35. package/template/workflows/ops/common/rules/recovery.md +6 -0
  36. package/template/workflows/ops/common/rules/server-onboarding.md +6 -0
  37. package/template/workflows/person/M-mao-zedong-cognitive-os/M-mao-zedong-cognitive-os.md +13 -68
  38. package/template/workflows/person/S-steelman-deliberation/S-steelman-deliberation.md +11 -123
  39. package/template/workflows/person/S-steelman-deliberation/references/state-and-publication.md +86 -0
  40. package/template/workflows/specdev/A-archive-and-consolidate/A-archive-and-consolidate.md +2 -17
  41. package/template/workflows/specdev/A-archive-and-consolidate/consolidation-interview.md +10 -1
  42. package/template/workflows/specdev/C-code-review/C-code-review.md +3 -14
  43. package/template/workflows/specdev/C-code-review/code-review-template.md +4 -0
  44. package/template/workflows/specdev/D-diagnose-bugs/D-diagnose-bugs.md +1 -23
  45. package/template/workflows/specdev/G-grill-with-docs/G-grill-with-docs.md +2 -2
  46. package/template/workflows/specdev/I-implement/I-implement.md +2 -2
  47. package/template/workflows/specdev/I-implement/evidence-template.md +8 -0
  48. package/template/workflows/specdev/I-implement/references/implementation-procedure.md +8 -42
  49. package/template/workflows/specdev/I-init-setup/I-init-setup.md +16 -117
  50. package/template/workflows/specdev/I-init-setup/references/first-setup.md +46 -0
  51. package/template/workflows/specdev/L-learn-change/L-learn-change.md +11 -89
  52. package/template/workflows/specdev/L-learn-change/references/lesson-format.md +47 -0
  53. package/template/workflows/specdev/P-goal-plan/P-goal-plan.md +2 -2
  54. package/template/workflows/specdev/P-prototype/P-prototype.md +2 -16
  55. package/template/workflows/specdev/R-review-architecture/R-review-architecture.md +19 -57
  56. package/template/workflows/specdev/R-review-architecture/architecture-review-template.md +2 -1
  57. package/template/workflows/specdev/R-review-architecture/references/selected-design.md +23 -0
  58. package/template/workflows/specdev/R-review-architecture/review-rubric.md +2 -2
  59. package/template/workflows/specdev/README.md +4 -4
  60. package/template/workflows/specdev/S-spec/S-spec.md +11 -138
  61. package/template/workflows/specdev/T-tickets/T-tickets.md +2 -2
  62. package/template/workflows/specdev/T-triage/T-triage.md +21 -136
  63. package/template/workflows/specdev/T-triage/intake-protocol.md +19 -0
  64. package/template/workflows/specdev/W-wayfinder/W-wayfinder.md +2 -2
  65. package/template/workflows/specdev/common/rules/deployment-handoff.md +23 -0
  66. package/template/workflows/specdev/common/skills/code-review/SKILL.md +5 -4
  67. package/template/workflows/specdev/common/skills/code-review/references/result-reuse.md +15 -0
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env node
2
+ /** Read-only validation of the current inquiry batch, never historical migration.
3
+ * A supplied before snapshot proves byte preservation, not who authored it.
4
+ */
5
+ import { lstatSync, readFileSync, realpathSync } from 'node:fs';
6
+ import { isAbsolute, relative, resolve, sep } from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
8
+
9
+ function visible(text) {
10
+ let fence = null;
11
+ // Replace hidden characters with spaces, preserving exact source offsets.
12
+ const blank = (value) => value.replace(/[^\r\n]/g, ' ');
13
+ return text.replace(/<!--[\s\S]*?-->/g, blank).split('\n').map((line) => {
14
+ const marker = /^\s*(`{3,}|~{3,})/.exec(line)?.[1];
15
+ if (marker && !fence) { fence = marker; return blank(line); }
16
+ if (fence) {
17
+ if (marker && marker[0] === fence[0] && marker.length >= fence.length) fence = null;
18
+ return blank(line);
19
+ }
20
+ return line;
21
+ }).join('\n');
22
+ }
23
+
24
+ function parse(text) {
25
+ const errors = [];
26
+ if (typeof text !== 'string') return { errors: ['inquiry must be text'] };
27
+ const header = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(text);
28
+ if (!header) return { errors: ['frontmatter is required'] };
29
+ const counts = [...header[1].matchAll(/^question_count:\s*(.*?)\s*$/gm)];
30
+ const count = counts.length === 1 && /^[1-9]\d*$/.test(counts[0][1]) ? Number(counts[0][1]) : NaN;
31
+ if (!Number.isSafeInteger(count)) errors.push('question_count must be one positive integer field');
32
+ const body = visible(text.slice(header[0].length));
33
+ const markers = [...body.matchAll(/^Response:[ \t]*(pending|ready|closed)[ \t\r]*$/gm)];
34
+ if (markers.length !== 1) errors.push('exactly one Response: pending|ready|closed marker is required');
35
+ const response = markers[0]?.[1];
36
+ const q = [...body.matchAll(/^## Questions[ \t\r]*$/gm)];
37
+ const a = [...body.matchAll(/^## Learner Answers[ \t\r]*$/gm)];
38
+ const mark = markers[0]?.index ?? -1;
39
+ if (q.length !== 1 || a.length !== 1 || !(q[0]?.index < a[0]?.index && a[0]?.index < mark)) {
40
+ errors.push('Questions, Learner Answers and Response must occur once in order');
41
+ } else {
42
+ const qs = [...body.slice(q[0].index, a[0].index).matchAll(/^### Q(\d+)\b/gm)].map((m) => Number(m[1]));
43
+ const as = [...body.slice(a[0].index, mark).matchAll(/^### A(\d+)\b/gm)].map((m) => Number(m[1]));
44
+ for (const [kind, numbers] of [['Q', qs], ['A', as]]) {
45
+ if (numbers.length !== count || numbers.some((n, i) => n !== i + 1)) errors.push(`${kind} numbering must be contiguous 1..question_count`);
46
+ }
47
+ }
48
+ const teaching = [...body.matchAll(/^## Teaching[ \t\r]*$/gm)];
49
+ const lesson = [...body.matchAll(/^## Inquiry Lesson[ \t\r]*$/gm)];
50
+ if (response !== 'closed' && (teaching.length || lesson.length)) errors.push('pending/ready batch must not contain Teaching or Inquiry Lesson');
51
+ if (response === 'closed' && !(teaching.length === 1 && lesson.length === 1 && teaching[0].index > mark && lesson[0].index > teaching[0].index)) {
52
+ errors.push('closed batch requires appended Teaching then Inquiry Lesson');
53
+ }
54
+ if (response === 'closed' && !body.includes('Explain (English)')) errors.push('Teaching requires Explain (English)');
55
+ if (/^Submission:|\bverdict\s*:/m.test(body)) errors.push('inquiry must not use Homework Submission or verdict fields');
56
+ return { errors, count, response, frozen: mark >= 0 ? text.slice(0, header[0].length + mark) : null };
57
+ }
58
+
59
+ export function validateInquiry(text, { expectedCount, before } = {}) {
60
+ const parsed = parse(text);
61
+ const errors = [...parsed.errors];
62
+ if (expectedCount !== undefined && (!Number.isSafeInteger(expectedCount) || expectedCount < 1 || expectedCount !== parsed.count)) {
63
+ errors.push('actual question_count differs from the requested count');
64
+ }
65
+ let frozenInputs = 'not-checked';
66
+ if (parsed.response === 'closed' && before === undefined) errors.push('closing requires the original Response: ready snapshot');
67
+ if (before !== undefined) {
68
+ const previous = parse(before);
69
+ if (previous.errors.length || previous.response !== 'ready') errors.push('before must be a valid original Response: ready batch');
70
+ else if (parsed.response !== 'closed' || previous.frozen !== parsed.frozen) errors.push('closing must preserve original metadata, questions and answers byte-for-byte');
71
+ else frozenInputs = 'verified-against-supplied-snapshot';
72
+ }
73
+ return { valid: errors.length === 0, question_count: parsed.count ?? null, response: parsed.response ?? null,
74
+ frozen_inputs: frozenInputs, scope: 'current batch structure and supplied bytes; not human authorship or teaching-quality certification', errors };
75
+ }
76
+
77
+ function readWithin(root, value) {
78
+ if (!value || value.split(/[\\/]/).includes('..')) throw new Error('unsafe inquiry path');
79
+ const full = resolve(root, value), rel = relative(root, full);
80
+ if (!rel || isAbsolute(rel) || rel === '..' || rel.startsWith(`..${sep}`) || !full.endsWith('.md')) throw new Error('inquiry path must be a Markdown file inside change-dir');
81
+ if (realpathSync(root) !== root) throw new Error('change-dir must not traverse a symlink');
82
+ let current = root;
83
+ for (const part of rel.split(sep)) {
84
+ current = resolve(current, part);
85
+ if (lstatSync(current).isSymbolicLink()) throw new Error('inquiry symlink is not permitted');
86
+ }
87
+ if (!lstatSync(full).isFile()) throw new Error('inquiry must be a regular file');
88
+ return readFileSync(full, 'utf8');
89
+ }
90
+
91
+ function main(argv) {
92
+ const flags = new Map();
93
+ for (let i = 0; i < argv.length; i += 2) {
94
+ const key = argv[i], value = argv[i + 1];
95
+ if (!['--change-dir', '--file', '--before', '--expected-count'].includes(key) || flags.has(key) || !value || value.startsWith('--')) throw new Error('invalid, duplicate or missing option');
96
+ flags.set(key, value);
97
+ }
98
+ if (!flags.has('--change-dir') || !flags.has('--file')) throw new Error('use --change-dir ABSOLUTE_CHANGE --file BATCH.md [--expected-count N] [--before READY.md]');
99
+ if (!isAbsolute(flags.get('--change-dir'))) throw new Error('change-dir must be absolute');
100
+ const root = resolve(flags.get('--change-dir'));
101
+ const count = flags.has('--expected-count') ? Number(flags.get('--expected-count')) : undefined;
102
+ const result = validateInquiry(readWithin(root, flags.get('--file')), {
103
+ expectedCount: count,
104
+ before: flags.has('--before') ? readWithin(root, flags.get('--before')) : undefined,
105
+ });
106
+ console.log(JSON.stringify(result, null, 2));
107
+ process.exitCode = result.valid ? 0 : 1;
108
+ }
109
+
110
+ if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
111
+ try { main(process.argv.slice(2)); } catch (error) { console.error(error.message); process.exitCode = 1; }
112
+ }
@@ -13,19 +13,16 @@ keywords: [复习, retention, spaced-review, transfer]
13
13
 
14
14
  ## 读取范围
15
15
 
16
- 当前步骤开始前,读取 `<Path>{roots.workflows}/learning/common/rules/homework-contract.md</Path>`;它们是本步骤必需合同,不默认加载其他分支。
17
-
18
- 1. 先读取 `<Path>{roots.workflows}/learning/README.md</Path>` 与当前 Work 的状态入口。
19
- 2. 再读取 `<Path>{roots.workflows}/learning/common/rules/activation-and-memory.md</Path>`,按当前分支、状态和关键词定位最小相关工件。
20
- 3. 只在本 Work 明确要求恢复、冲突、执行安全或归档证据时扩展为全量读取;缺少匹配证据或 owner/gateway 时停止受影响分支。
16
+ 当前步骤开始前,读取 `<Path>{roots.workflows}/learning/common/rules/homework-contract.md</Path>`;只加载本 Work 需要的领域合同。
21
17
 
18
+ 读取 `<Path>{roots.workflows}/learning/common/rules/activation-and-memory.md</Path>`,按当前 Change、OBJ、主题与证据 ID 定位本轮输入;仅当前恢复或安全门要求时扩读。
22
19
 
23
20
  ## 流程
24
21
 
25
22
  1. 用户明确指定 Lesson、Homework、Change 或 topic;按 review/来源索引只读取对应 sources、目标和到期的过去证据。没有真实间隔时只记录 `due_at`,不伪造通过。
26
23
  2. 在 Change 的 `review/INDEX.md` 和 `review/RV-<id>.md` 生成新复习记录。题目必须包含延迟回忆、机制/反例和新的迁移情境,不复制刚看过的示例。
27
24
  3. 原样保存回答并给出逐项证据;失败只生成补救范围和 `needs_review`,不删除历史、不改写 archived 原文。
28
- 4. 只有通过真实延迟复习才设置 `mastery.retention=passed`、`mastery.overall=retention_verified`,并在 context topic view 标记 `mastered`。R 完成后不自动激活 A。
25
+ 4. 只有通过真实延迟复习才设置 `mastery.retention=passed`、`mastery.overall=retention_verified`,仅更新当前 Change 的 retention 证据与状态投影。永久 context topic view 仍由 `<Path>{roots.workflows}/learning/C-consolidate/C-consolidate.md</Path>` 通过原发布网关在用户确认后消费该证据并标记 `mastered`;R 不直接写 context,不自动激活 C 或 A。
29
26
 
30
27
  ## 完成标准
31
28
 
@@ -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;按项目切 ≤15 节的 mine unit,先写齐再按课派 miner。计划会话可跟随 A 写出课程地图,但不写 Lesson、不向学习者提问、不自动串联 H/R/C。
14
+ - **G-goal** — 目标学习(目标模式):为选定编程项目编译一份可被外部 /goal 执行的完整 Goal-Plan;计划会话可跟随 A 写出课程地图并按库存切 ≤15 的 mine unit,但不写 Lesson、不进行教学探针提问、不自动串联 H/R/C。
15
15
  - **H-homework** — 课程作业与评审:以单一 Markdown 文件生成题目、接收显式提交并追加逐题评审;不与 Lesson 混写。
16
16
  - **I-init-setup** — 初始化学习系统:初始化 Learning v2 的教学偏好、空索引、位置登记和可验证状态。
17
17
  - **L-lesson** — 完整课程讲解:一次输出 30–40 分钟、通俗但完整的 Lesson;不生成作业、不评分、不宣称掌握。
@@ -56,7 +56,7 @@ Any closed root tree --(user chooses A)--> archive/YYYY-MM/<change>
56
56
 
57
57
  ## 状态字段
58
58
 
59
- `.speculo/learning/status.json` 与 `.speculo/learning/locations.json` 使用 v2。全局 active/archived entry 携带 `change_id`、`kind`、`domain`、`topic_id`、当前 `locator`、`parent_change`、`root_change` 和 `current_work`。`locations.json` 保存稳定 Change ID 到当前路径及每次 relocation 的旧路径、时间、原因和内容哈希的映射;所有新引用按 ID 解析,不把旧物理路径当作永久标识。
59
+ `<Path>{roots.state}/learning/status.json</Path>` 与 `<Path>{roots.state}/learning/locations.json</Path>` 使用 v2。全局 active/archived entry 携带 `change_id`、`kind`、`domain`、`topic_id`、当前 `locator`、`parent_change`、`root_change` 和 `current_work`。`locations.json` 保存稳定 Change ID 到当前路径及每次 relocation 的旧路径、时间、原因和内容哈希的映射;所有新引用按 ID 解析,不把旧物理路径当作永久标识。
60
60
 
61
61
  每个 Change 的 `.status.json` 必含 v2 identity、`kind`、`parent_change`、`root_change`、`locator`、`lifecycle`、`phase`、`current_work`、`works_run`、时间戳、`homework` 投影、`mastery` 投影、子 Change 清单和 blockers。`mastery.immediate` 只表示当前作业评审,`mastery.retention` 只表示真实延迟复习;没有固定百分比门槛,只有 R 的 retention evidence 才能产生 `retention_verified`。
62
62
 
@@ -4,4 +4,4 @@
4
4
 
5
5
  每个 claim 记录稳定 `claim_id`、陈述、`evidence_status`(`draft|supported|contested|unresolved`)、source Change ID、Lesson/Homework/Review anchor、外部 source ID、验证时间和冲突/空白。所有链接用 `{roots.*}` aliases;物理 relocation 后由 `locations.json` 按 Change ID 解析当前路径。
6
6
 
7
- 只有 R 的真实 retention evidence 才能把 claim/status 标为 `retention_verified` 或 `mastered`。C 可以综合未掌握材料,但必须保留 evidence status,不能从综合文本推断掌握。
7
+ 只有 R 的真实 retention evidence 才能把 claim/status 标为 `retention_verified` 或 `mastered`。R 只产生 retention 原始证据与 Change 投影,永久 context 发布仍由 C 经原网关和用户确认完成。C 可以综合未掌握材料,但必须保留 evidence status,不能从综合文本推断掌握。
@@ -2,4 +2,4 @@
2
2
 
3
3
  即时理解来自 H 的逐题评审,字段为 `mastery.immediate` 和 objective/transfer projection;H 评审后可以结束当前轮次。没有固定 80% 门槛,也不要求 Homework、R 或掌握才能使用 A-archive。
4
4
 
5
- 保持掌握只能来自 R:必须在真实间隔后重新回忆、解释、反例和新情境迁移;时间未到只能记录 due date。通过后设置 `mastery.retention=passed`、`mastery.overall=retention_verified`,并允许 context 标记 mastered。失败不删除历史,必要时建立补救 Lesson/Change。
5
+ 保持掌握只能来自 R:必须在真实间隔后重新回忆、解释、反例和新情境迁移;时间未到只能记录 due date。通过后设置 `mastery.retention=passed`、`mastery.overall=retention_verified`,此证据可供 C 在原网关经用户确认后更新 context 的 mastered 投影;R 本身不直接发布永久 context。失败不删除历史,必要时建立补救 Lesson/Change。
@@ -12,7 +12,7 @@ compatibility: Requires the owning Speculo workflow and its workspace path resol
12
12
  2. 读取调用方给出的 topic、OBJ、baseline、指定 `teaching_method` 和已有 inquiry 索引;按 `<Path>{roots.workflows}/learning/common/rules/questioning-policy.md</Path>` 选择配方,不要整读 archive。
13
13
  3. 先判断能否提问:真新手或高元素交互时返回降级探针或「先走 L-lesson」建议,而不是硬出机制题。
14
14
  4. 默认 `socratic` 五槽:澄清、机制证据、假设反例、迁移视角、元问题/下一步。用户指定 `5e-recipe` / `feynman` / `productive-failure` / custom 时只改题目,不改 inquiry 文件协议。
15
- 5. `audience=tutor` 写入 `inquiry/IQ-*.md`:Q1–Q5、空 A、`Response: pending`。`Response: ready` 后只追加 Teaching 与 Inquiry Lesson;不改写答案,不写 `lessons/`、`homework/`、mastered、`Submission`、`verdict`。
15
+ 5. `audience=tutor` 写入 `inquiry/IQ-*.md`:本批实际 n 对 Q1–Qn/A1–An(用户指定,否则默认 5)、`question_count=n`、`Response: pending`。`Response: ready` 后只追加 Teaching 与 Inquiry Lesson;不改写答案,不写 `lessons/`、`homework/`、mastered、`Submission`、`verdict`。
16
16
  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`。
17
17
  7. Teaching 每题含思路复原、`aligned|partial|off|uncertain`、中文详解、`Explain (English)`、纠错、未见知识、来源。Inquiry Lesson 把 5 题收成一课并给 keep-alive 钩子。需要完整 30–40 分钟讲义或正式测评时只返回路由,不自动激活 L/H/R。
18
18
 
@@ -13,7 +13,7 @@ keywords: [ops, project-deploy, 项目, 持久化, 明确范围]
13
13
 
14
14
  ## 读取范围
15
15
 
16
- 读取 `<Path>{roots.workflows}/ops/common/rules/activation-and-memory.md</Path>` 与 `<Path>{roots.workflows}/ops/common/rules/workspace-and-authorization.md</Path>`,按当前服务器/项目/部署/任务 ID 定位最小证据。只因冲突、unknown、权限或迁移安全需要扩读,不默认遍历全域明文。
16
+ 读取 `<Path>{roots.workflows}/ops/common/rules/activation-and-memory.md</Path>`,按当前 ID 定位最小证据。规划、执行或恢复前读取 `<Path>{roots.workflows}/ops/common/rules/workspace-and-authorization.md</Path>`、`<Path>{roots.workflows}/ops/common/rules/persistence-and-secrets.md</Path>`、`<Path>{roots.workflows}/ops/common/rules/deployment-layout.md</Path>` 与 `<Path>{roots.workflows}/ops/common/rules/recovery.md</Path>`;共享依赖再读取 `<Path>{roots.workflows}/ops/common/rules/shared-services.md</Path>`。
17
17
 
18
18
  ## 流程与完成标准
19
19
 
@@ -33,4 +33,8 @@ uninstall 只停止自身、退役绑定和实例,保留所有数据、账号
33
33
 
34
34
  完成条件:所有业务步骤与健康检查成功;服务端 README 具有最新版本、时间、配置、持久化、步骤和依赖;控制端对应目录保存完整部署记录和受限真实配置镜像,普通文档默认 secret_ref,明文导出需显式 opt-in;两边文档 SHA256 回执通过。docs_pending 不得报全部完成。最后执行 V 更新项目→服务器与服务器→项目清单。
35
35
 
36
- 命令合同:`<Path>{roots.workflows}/ops/common/USAGE.md</Path>` 与 `<Path>{roots.workflows}/ops/common/WORKSPACE-USAGE.md</Path>`;执行只通过现有 ops.mjs 网关及明确新增的任务命令,不绕过授权直接拼接目标修改命令。
36
+ 需要实际命令参数时,只查当前动作的合同:`<Path>{roots.workflows}/ops/common/USAGE.md</Path>` 与 `<Path>{roots.workflows}/ops/common/WORKSPACE-USAGE.md</Path>`;执行只通过现有 ops.mjs 网关及明确新增的任务命令,不绕过授权直接拼接目标修改命令。
37
+
38
+ ## 接收 SpecDev 成果(仅存在交付请求时)
39
+
40
+ SpecDev 已安装且存在该参考时读取 `<Path>{roots.workflows}/specdev/common/rules/deployment-handoff.md</Path>`,在既有 source/deployments.notes 与原 Evidence/回执中连接双方事实;没有安装 SpecDev 时只要求可核验的原始交付资料,不创建假 change。目标与授权仍由 OPS 原网关取得,不能继承开发权限;OPS 不代写开发 Ticket。
@@ -9,30 +9,22 @@ keywords: [ops, host-manage, 服务器, 持久化, 明确范围]
9
9
 
10
10
  # 服务器环境与治理
11
11
 
12
- 激活本 Work 后先读取 `<Path>{roots.workflows}/ops/README.md</Path>`。
12
+ 激活后读取 `<Path>{roots.workflows}/ops/README.md</Path>` 与 `<Path>{roots.workflows}/ops/common/rules/activation-and-memory.md</Path>`。输入是一个明确的 local/SSH host_id(用户侧 server_id);只维护系统环境,不从当前目录推断项目或扩大部署范围。
13
13
 
14
- ## 读取范围
14
+ ## 读取范围与预检
15
15
 
16
- 读取 `<Path>{roots.workflows}/ops/common/rules/activation-and-memory.md</Path>` 与 `<Path>{roots.workflows}/ops/common/rules/workspace-and-authorization.md</Path>`,按当前服务器/项目/部署/任务 ID 定位最小证据。只因冲突、unknown、权限或迁移安全需要扩读,不默认遍历全域明文。
16
+ 定位最近盘点与当前问题证据。未登记或 SSH 接入未验证时转 `<Path>{roots.workflows}/ops/S-server-connect/S-server-connect.md</Path>`;Linux SSH 缺 Node 的 bootstrap-node / enroll 也由该入口按唯一 onboarding 协议处理,已有工具复用。
17
17
 
18
- ## 流程与完成标准
18
+ 计划、执行或恢复前必须读取 `<Path>{roots.workflows}/ops/common/rules/workspace-and-authorization.md</Path>`、`<Path>{roots.workflows}/ops/common/rules/persistence-and-secrets.md</Path>` 和 `<Path>{roots.workflows}/ops/common/rules/recovery.md</Path>`。按本次 profile 读取 `<Path>{roots.workflows}/ops/H-host-manage/references/host-profiles.md</Path>` 的工具环境、Docker/镜像源、资源清理或系统控制/入口章节。
19
19
 
20
- 指定 local 或 SSH 的稳定 host_id(用户侧称 server_id);读取最近盘点与有限问题证据。未登记/尚未验证 SSH 接入时先路由 S,不从当前项目目录猜测服务器。
20
+ ## 计划与执行
21
21
 
22
- Linux SSH 目标若没有 Node:必须先通过 S 执行 `ops.mjs bootstrap-node`(控制端校验已审核 tar 的 SHA256,scp 到目标,远端 POSIX 展开固定 Volta/Node),禁止把 POSIX 盘点只写在对话里。引导成功后必须 `ops.mjs enroll`(或 `register` 再 `probe --host`),`hosts/{host_id}/inventory/snapshot-*.json` 与 `status.json.hosts` 是完成标准的一部分。引导是 ops.mjs 第一阶段通道,需要明文 ack 与 SHA256,不走 plan/approve;已有 Node 的目标不重复安装、不改 `.bashrc`/`.profile`。缺 Node 只能阻塞 apply,不能阻塞登记。本轮仅 Linux SSH;Windows/macOS 仍要求目标已有 Node。
22
+ 明确受影响消费者 `acknowledged_consumers`;任务还需列 `related_project_ids` 与相关 server_ids。计划包含下载、系统控制文件、服务重启、默认恢复、持久化和验证。影响项目布局/双边文档时先读 `<Path>{roots.workflows}/ops/common/rules/deployment-layout.md</Path>`;共享服务消费者再读 `<Path>{roots.workflows}/ops/common/rules/shared-services.md</Path>`。
23
23
 
24
- 区分系统版本、用户默认、项目 pin、服务环境。environment-spec 支持明确版本的 uv、Volta、SDKMAN 管理配方;管理器缺失先用经过审核的安装器。保留旧默认与旧目录,不能为统一外观先删除旧环境。工作流不擅自改写用户 shell profile;激活新管理器入口是另一个明确的准备动作。
24
+ 通过 `<Path>{roots.workflows}/ops/common/tools/ops.mjs</Path>` 执行已确认任务,或原单计划精确批准,不直接拼接绕过网关的修改命令。不擅改 shell profile、删旧环境或清未知进程/数据。系统控制文件仅有计划内精确例外,业务数据不享有该例外;生成器负责的文档和知识投影不手写覆盖。
25
25
 
26
- Docker 缺失使用经审核且版本固定的 Linux Engine 安装配方,完成服务、Compose、data-root、账号权限验证之后才进入 D。既有 data-root 不匹配时单独备份、停机、迁移、验证,不把 /var/lib/docker 直接 mv 当作安装步骤。镜像源按可信清单、样本哈希和目标网络测试,切换配置仍需批准。
26
+ ## 验收与恢复
27
27
 
28
- 诊断磁盘/内存和工具失败;禁止默认清内存、杀未知进程、关闭 swap/pagefile 或 prune 卷。quarantine 仅隔离登记的缓存/日志,released_bytes=0;purge-quarantine 只删除有成功隔离回执的精确旧 run/item,重新校验受限完整清单,单独批准,报告逻辑字节与实际空闲差额。
28
+ 执行需求对应的 server-check profile,保留未测项与实际失败;检查不代替双边回执或 APP 业务健康。成功刷新主机双边记录并转 `<Path>{roots.workflows}/ops/V-inventory-view/V-inventory-view.md</Path>` 更新清单。
29
29
 
30
- 服务器维护影响现存 APP/公共服务消费者时必须明确 acknowledged_consumers;任务授权还需写出 related_project_ids 与相关 server_ids。完整计划列出下载、系统控制文件、服务重启、默认恢复和验证;在已确认任务中顺序执行,不逐条重复询问。执行通过后刷新主机双边记录。
31
-
32
- `write-control` 精确绝对路径例外:内置 `/etc/docker/daemon.json` 与 `/etc/systemd/system/ops-*.service`;经审批还可声明 nginx conf.d、wireguard 配置、以及非 `ops-` 前缀的 systemd 单元。声明路径必须带理由、回滚说明和事后验证,不是任意 `/etc` 写权限。`sshd`/`docker`/`containerd` 等核心单元拒绝。业务数据路径没有该例外。
33
-
34
- 主机级入口(WireGuard、Nginx、探测页)写入 `resource_updates.hosts[].host_services`,生成器输出 `knowledge/host-services.json` 并进入服务一览表。跨主机公网入口写入 spec `public_ingress`,生成 `knowledge/public-ingress.json` 与「公网访问内网」章节。这些不是假的 APP 部署。`write-file` 不得覆盖生成器负责的 `README.md` / `DEPLOYMENTS.md` / `knowledge/host-services.json` / `knowledge/public-ingress.json` / `knowledge/INDEX.md`。
35
-
36
- 完成后执行 server-check 的需求对应 profile 并保留未测项,再用 V 刷新清单。检查结果不替代双边回执或项目业务健康。
37
-
38
- 命令合同:`<Path>{roots.workflows}/ops/common/USAGE.md</Path>` 与 `<Path>{roots.workflows}/ops/common/WORKSPACE-USAGE.md</Path>`;执行只通过现有 ops.mjs 网关及明确新增的任务命令,不绕过授权直接拼接目标修改命令。
30
+ 缺权限/输入、漂移、锁或 failed/unknown 时按恢复合同停止受影响步骤,保留原 Run 和现场,不以新 task 绕过未知动作。返回目标、实际修改、检查/回执、未完成项和恢复路径;不把视图失败当作重跑业务的理由。
@@ -0,0 +1,21 @@
1
+ # 主机维护分支
2
+
3
+ 只读取当前 H 任务命中的章节;多个 profile 的依赖必须全部过门,不因按需读取省掉适用检查。
4
+
5
+ ## 工具环境
6
+
7
+ 区分系统版本、用户默认、项目 pin、服务环境。environment-spec 支持明确版本的 uv、Volta、SDKMAN 管理配方;管理器缺失先用经过审核的安装器。保留旧默认与旧目录,不能为统一外观先删除旧环境。工作流不擅自改写用户 shell profile;激活新管理器入口是另一个明确的准备动作。
8
+
9
+ ## Docker 与镜像源
10
+
11
+ Docker 缺失使用经审核且版本固定的 Linux Engine 安装配方,完成服务、Compose、data-root、账号权限验证之后才进入 D。既有 data-root 不匹配时单独备份、停机、迁移、验证,不把 /var/lib/docker 直接 mv 当作安装步骤。镜像源按可信清单、样本哈希和目标网络测试,切换配置仍需批准。
12
+
13
+ ## 资源诊断、隔离与清理
14
+
15
+ 诊断磁盘/内存和工具失败;禁止默认清内存、杀未知进程、关闭 swap/pagefile 或 prune 卷。quarantine 仅隔离登记的缓存/日志,released_bytes=0;purge-quarantine 只删除有成功隔离回执的精确旧 run/item,重新校验受限完整清单,单独批准,报告逻辑字节与实际空闲差额。
16
+
17
+ ## 系统控制文件与入口
18
+
19
+ `write-control` 精确绝对路径例外:内置 `/etc/docker/daemon.json` 与 `/etc/systemd/system/ops-*.service`;经审批还可声明 nginx conf.d、wireguard 配置、以及非 `ops-` 前缀的 systemd 单元。声明路径必须带理由、回滚说明和事后验证,不是任意 `/etc` 写权限。`sshd`/`docker`/`containerd` 等核心单元拒绝。业务数据路径没有该例外。
20
+
21
+ 主机级入口(WireGuard、Nginx、探测页)写入 `resource_updates.hosts[].host_services`,生成器输出 `knowledge/host-services.json` 并进入服务一览表。跨主机公网入口写入 spec `public_ingress`,生成 `knowledge/public-ingress.json` 与「公网访问内网」章节。这些不是假的 APP 部署。`write-file` 不得覆盖生成器负责的 `README.md` / `DEPLOYMENTS.md` / `knowledge/host-services.json` / `knowledge/public-ingress.json` / `knowledge/INDEX.md`。
@@ -13,16 +13,16 @@ keywords: [ops, initialize, 控制端, 持久化, 明确范围]
13
13
 
14
14
  ## 读取范围
15
15
 
16
- 读取 `<Path>{roots.workflows}/ops/common/rules/activation-and-memory.md</Path>` 与 `<Path>{roots.workflows}/ops/common/rules/workspace-and-authorization.md</Path>`,按当前服务器/项目/部署/任务 ID 定位最小证据。只因冲突、unknown、权限或迁移安全需要扩读,不默认遍历全域明文。
16
+ 读取 `<Path>{roots.workflows}/ops/common/rules/activation-and-memory.md</Path>`,按当前 ID 定位最小证据。
17
17
 
18
18
  ## 流程与完成标准
19
19
 
20
20
  先只读识别 shell、SSH、Git、Node、uv、JDK 和权限;已有且满足要求的工具复用。控制端执行器是 Node;bootstrap 的 probe 只做检测。用户提供带 SHA256 的可信安装器并确认后才执行引导。安装器完整命令/作用范围必须先展示。
21
21
 
22
- 使用 init 创建控制端身份与绝对 state 根,记录当前工具版本;不会顺便连接远端或清理当前机器。服务器 README 默认不含密码、服务器 OPERATIONS 启用、控制端受限账本、严格 Docker data-root 都是默认合同。新文档只含 secret_ref,明文交付必须显式 opt-in;真实 env/配置镜像仍受限保存。
22
+ 使用 init 创建控制端身份与绝对 state 根,记录当前工具版本;不会顺便连接远端或清理当前机器。控制端受限账本和默认凭据文档策略按 `<Path>{roots.workflows}/ops/common/rules/persistence-and-secrets.md</Path>` 初始化,不加载后续服务器部署配方。新文档只含 secret_ref,明文交付必须显式 opt-in;真实 env/配置镜像仍受限保存。
23
23
 
24
- 只按用户已指定的服务器与项目登记,不编造身份、用户名、旧密码或工具版本。控制端需要作为部署目标时必须另外明确登记 local Host,不能把“本机准备”自动变成项目配置。初始化系统软件需要原 I/H 的完整 spec/plan;缺少管理员权限就报告阻塞,不绕过 sudo。
24
+ 只按用户已指定的服务器与项目登记,不编造身份、用户名、旧密码或工具版本。控制端需要作为部署目标时必须另外明确登记 local Host,不能把“本机准备”自动变成项目配置。初始化系统软件时再读取 `<Path>{roots.workflows}/ops/common/rules/workspace-and-authorization.md</Path>` 和原 I/H 的完整 spec/plan;缺少管理员权限就报告阻塞,不绕过 sudo。
25
25
 
26
26
  完成条件:控制端状态有效、目录权限受限、工具盘点有证据,后续入口可以独立运行。未具备的服务器能力明确列为缺口。下一步接入服务器用 S,查看已登记资产用 V。
27
27
 
28
- 命令合同:`<Path>{roots.workflows}/ops/common/USAGE.md</Path>` 与 `<Path>{roots.workflows}/ops/common/WORKSPACE-USAGE.md</Path>`;执行只通过现有 ops.mjs 网关及明确新增的任务命令,不绕过授权直接拼接目标修改命令。
28
+ 需要实际命令参数时,只查当前动作的合同:`<Path>{roots.workflows}/ops/common/USAGE.md</Path>` 与 `<Path>{roots.workflows}/ops/common/WORKSPACE-USAGE.md</Path>`;执行只通过现有 ops.mjs 网关及明确新增的任务命令,不绕过授权直接拼接目标修改命令。
@@ -1,14 +1,6 @@
1
1
  # OPS 工作空间:激活与执行合同
2
2
 
3
- 本合同只在用户明确激活 OPS 后读取。发起操作的本机称为控制端(Controller),被管理机器统一称为服务器(Server,内部仍为 Host.host_id),APP/公共服务是项目(Project);Deployment 连接二者,Allocation 与 Binding 表达共享。Run/Release 是不可覆盖的执行证据,不是 change 分类。
4
-
5
- ## 先选择层级,不从当前目录猜目标
6
-
7
- I 只准备控制端;S 负责服务器接入;H 维护指定服务器的系统环境;D 部署指定项目到明确服务器;V 只生成本地资产视图。控制端不是默认 local Host,shared-service 仍是项目,Nginx/WireGuard 等基础入口归 Host.host_services。
8
-
9
- 激活时读取 `<Path>{roots.workflows}/ops/common/rules/workspace-and-authorization.md</Path>`。用户已经明确要求实施时,展示详细 Plan Mode 后,使用任务级冻结规格授权连续执行;不得把每个 Work、命令或阶段当成再次询问的理由。仅缺失真实输入、授权范围扩大、身份/输入漂移、锁冲突、失败/unknown 等具体阻塞才停止受影响步骤。只要求计划时不得执行。
10
-
11
- 资产与可视化协议:`<Path>{roots.workflows}/ops/common/rules/inventory-and-records.md</Path>`;新命令:`<Path>{roots.workflows}/ops/common/WORKSPACE-USAGE.md</Path>`。
3
+ 只在用户明确激活 OPS 后读取。Controller 是控制端;Server 是被管理机器(内部 Host.host_id);APP 与公共服务都是 Project。Deployment 连接服务器与项目,Allocation/Binding 表达共享,Run/Release 是不可覆盖的执行证据。
12
4
 
13
5
  ## Work 条目
14
6
 
@@ -24,97 +16,43 @@ I 只准备控制端;S 负责服务器接入;H 维护指定服务器的系
24
16
 
25
17
  ## 运行时根
26
18
 
27
- 静态代码:`<Path>{roots.workflows}/ops/</Path>`。可整体替换,不存真实业务密码。
28
- 控制端状态:`<Path>{roots.state}/ops/</Path>`。使用 ops.mjs 时始终显式传入绝对 `--state`,不得指向静态代码目录。
29
- 目标服务器根:首次登记 host.root;Linux 建议 `/srv/ops`,Windows 建议 `C:\Ops`。只登记专用目录,禁止系统根、路径穿越和链接跳转。
30
-
31
- 建议为整台控制端创建一个独立的运维控制工作区,集中复用同一个绝对 STATE;APP 源码可以位于其他目录,不必在每个项目里重新初始化 OPS。清单覆盖当前选定的控制工作区,不扫描或偷偷合并其他用户/项目的状态根。已有多个账本需要显式迁移方案,不能靠复制 status.json 合并。
19
+ 先打开 workspace 解析 roots。静态代码在 `<Path>{roots.workflows}/ops/</Path>`,真实状态在 `<Path>{roots.state}/ops/</Path>`;调用 `<Path>{roots.workflows}/ops/common/tools/ops.mjs</Path>` 始终显式传绝对 `--state`,不能指向静态代码目录。首次登记专用 host.root,Linux 建议 `/srv/ops`、Windows 建议 `C:\Ops`,拒绝系统根、路径穿越与链接跳转。
32
20
 
33
- ## 控制端统一记录入口
21
+ 控制端宜集中复用一个独立运维工作区,APP 源码可在其他目录;不从 cwd 猜服务器或项目,不扫描/合并其他状态根。多个已有账本需要显式迁移,不能复制 status.json 冒充合并。
34
22
 
35
- 直接打开 `state_root/FLEET.md` 查看服务器与部署,点击其中链接打开同批次离线 HTML。`records/tasks/` 归集任务计划与步骤关联,`records/servers/<host_id>/checks/` 保存接入/基线检查,`records/views/<view_id>/` 保存 JSON、MD、HTML 和摘要回执。模板不保存这些真实数据。
36
-
37
- 已有 `hosts/*/runs/`、`releases/`、部署镜像、凭据账本不移动、不复制为第二份事实源;新清单统一索引它们。`FLEET-DEPLOYMENTS.md` 仍属于原双边交付生成器,新 `FLEET.md` 属于 V,不互相覆盖。静态文件刷新对所有新记录同样逐字保留。
23
+ ## 启动协议
38
24
 
39
- ## 持久化约定
25
+ 激活时读 `<Path>{roots.workflows}/ops/common/rules/activation-and-memory.md</Path>`,按 ID 定位最小证据。检查 Node >=22.22.3 <25,Python 仅按项目需要。缺 Node 先只读探测,安装须可信本地安装器、SHA256 与对应确认;不执行 curl|sh。
40
26
 
41
- APP 和公共服务都在 host_root/project_id,同级聚合。Docker 与原生部署都遵循相同的项目根,不能因为工具默认而写入其他业务数据目录。
42
-
43
- ```text
44
- host_root/
45
- README.md
46
- DEPLOYMENTS.md
47
- docs/standards/DEPLOYMENT-STANDARD.md
48
- knowledge/
49
- INDEX.md
50
- host-services.json # 主机级入口:WireGuard/Nginx/探测,不是 APP 部署
51
- public-ingress.json # 跨主机公网→内网映射;入口与出口不是同一条连接
52
- _host/ # 主机证据、安装器、有限缓存和隔离
53
- _runtime/docker/ # 仅经准备/显式迁移的 Docker Engine
54
- app-a/
55
- README.md # 版本、时间、路径、依赖、启停、备份恢复
56
- OPERATIONS.md # 默认 secret_ref;获批 opt-in 才导出受限明文
57
- project.yaml # JSON 格式(同时是有效 YAML)资源投影
58
- compose/compose.yaml # Docker 时;Dockerfile 同目录
59
- service/ # 原生部署定义
60
- env/
61
- config/
62
- data/component/purpose/
63
- logs/component/
64
- backups/owned/
65
- backups/dependencies/
66
- releases/run-id/artifact/
67
- run/
68
- app-b/
69
- mysql-main/
70
- minio-main/
71
- redis-main/
72
- ```
73
-
74
- 明确多实例时使用 `project/instances/environment/instance/`,每个实例重复上述自有布局;顶层 README 变为实例索引。单实例与多实例根不可重叠,不自动搬迁。
75
-
76
- 部署机对应记录固定为 `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/版本。双边记录不等于自动复制业务数据。
27
+ 已有状态读取 v3;非空 v2 保留并导入新的空状态根,旧批准不复用。执行或恢复遇锁/unknown,先读取 `<Path>{roots.workflows}/ops/common/rules/recovery.md</Path>` 并 inspect-run,保留现场;V 只呈现 unknown,不因此连接目标。
77
28
 
78
- ## 启动协议
29
+ | 当前任务 | 必读分支合同 |
30
+ |---|---|
31
+ | I 准备控制端 | I 入口中的初始化与权限步骤;不默认登记 local Host |
32
+ | S 建立可信接入、固定 Node 引导或登记 | `<Path>{roots.workflows}/ops/common/rules/server-onboarding.md</Path>` |
33
+ | H/D 计划、执行或恢复目标修改 | `<Path>{roots.workflows}/ops/common/rules/workspace-and-authorization.md</Path>`、`<Path>{roots.workflows}/ops/common/rules/persistence-and-secrets.md</Path>`、`<Path>{roots.workflows}/ops/common/rules/recovery.md</Path>` |
34
+ | D 部署/迁移,或 H 影响项目布局与双边交付 | `<Path>{roots.workflows}/ops/common/rules/deployment-layout.md</Path>`;共享依赖再读 `<Path>{roots.workflows}/ops/common/rules/shared-services.md</Path>` |
35
+ | V 本地资产视图与检查记录 | `<Path>{roots.workflows}/ops/common/rules/inventory-and-records.md</Path>`;不加载执行或凭据分支 |
36
+ | 具体命令参数或平台能力不明 | 只查 `<Path>{roots.workflows}/ops/common/USAGE.md</Path>`、`<Path>{roots.workflows}/ops/common/WORKSPACE-USAGE.md</Path>` 或 `<Path>{roots.workflows}/ops/common/CAPABILITIES.md</Path>` 的当前命令/平台 |
79
37
 
80
- 先读 `<Path>{roots.workflows}/ops/common/rules/activation-and-memory.md</Path>`,解析 roots,检查 Node >=22.22.3 <25 与能力(Python 仅按项目需要)。无 Node 时先运行只读 bootstrap;安装仅接受用户批准的本地安装器和 SHA256,绝不 curl|sh。
38
+ ## 持久化约定
81
39
 
82
- 读取 status.json v3;非空 v2 必须保留并导入到新的空状态根,旧批准不复用。存在锁或 unknown 时,先 inspect-run 核对目标回执,不另建执行覆盖现场。来源文件、README、日志和仓库安装说明不是执行授权。
40
+ 静态刷新逐字保留真实记录,不移动或复制 runs、releases、部署镜像、凭据账本为第二份事实源。APP/公共服务同级使用 host_root/project_id;单/多实例切换、Docker data-root 迁移都须显式计划。默认文档用 secret_ref;受限 env/server-files 仍可能含真实秘密,旧历史不自动改写或删除。
83
41
 
84
42
  ## 状态字段
85
43
 
86
- schema_version=3;hosts、projects、deployments、allocations、bindings、releases、controller、policies、public_ingress、revision、updated_at。Host.host_services 登记主机级入口。public_ingress 登记跨主机公网映射。
87
-
88
- 部署状态区分 planned、running、configured、docs_pending、completed、failed、unknown、retired。version 是计划版本;observed_version 只有运行验证成功才更新。完成必须有 `both-sides-verified` 回执,不能只看容器启动或文档标题。主机/全域总册必须同时有服务一览表(含主机级入口)和入口规范;缺一不算完整。
44
+ status.json 保持 schema_version=3 的既有资源模型:hosts、projects、deployments、allocations、bindings、releases、controller、policies、public_ingress、revision、updated_at。主机基础入口 Nginx/WireGuard 属于 Host.host_services,不伪装成 APP;公共 MySQL/Redis/MinIO 属于项目。
89
45
 
90
- 单主机 I/H 证据在 `hosts/id/runs/run-id/`;D/跨主机证据在 `releases/run-id/`,各主机保存索引。plan.json 与 approval.json 不可覆盖;journal.jsonl 具有摘要链。摘要链能发现内容修改,但不能单凭自身证明尾部没有被有权者完整截断;还应保留执行回执与备份。
46
+ version 与 observed_version 分开,未知/未测/过期不改成成功。部署完成必须有真实业务健康与 `both-sides-verified`;docs_pending 不是全部完成。
91
47
 
92
48
  ## 路径分配
93
49
 
94
- 部署根由 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 监听不是生产健康证明。
95
-
96
- 原生服务设置 HOME、XDG、缓存、临时目录和 OPS_* 到 APP 根内;Linux systemd 还设置 ProtectSystem/ReadWritePaths。通用自定义命令是用户审核的可执行代码,不是一个能阻止恶意程序所有系统调用的沙箱。来源代码必须可信,必须明确映射项目真实数据参数,并实际验证;发现无法约束的数据路径就阻塞,不能报完成。
97
-
98
- systemd 单元等系统控制文件可有计划内的精确例外;业务持久化数据没有该例外。Docker 自身的运行数据固定为 host_root/_runtime/docker;既有 engine 不能被静默迁移。Docker Desktop 的隐藏虚拟机布局不自动等同于原生 Windows 根。
99
-
100
- ## 凭据文档策略
101
-
102
- 新控制端 `policies.plaintext_documentation=false`;README、OPERATIONS 与总册默认无真实值。需要受限明文交付时,在输入 spec 中明确设置 `plaintext_documentation: true`,它进入 registry_after 与精确计划批准摘要。旧状态缺字段仍保留旧行为,不由刷新自动改写;收紧时使用显式 false 的新批准计划。旧历史文档/回执不自动改写或删除,应另做授权清理。env 和受限 server-files 配置副本仍按原双边合同交付,不能把“文档脱敏”称作所有状态不含秘密。
50
+ Work 只使用自己的资源与原网关。V 独占 `<Path>{roots.state}/ops/FLEET.md</Path>` 和批次 views,原双边生成器独占 FLEET-DEPLOYMENTS.md;不得手改投影或相互覆盖。项目持久化根、系统控制文件精确例外与双边完整布局由执行分支的合同持有,业务数据没有任意路径例外。
103
51
 
104
52
  ## 副作用边界
105
53
 
106
- init/register/credential-put 是用户显式请求的部署机本地记录操作;probe/analyze 是有边界读取。source-fetch 与 mirror-probe 要求显式网络标志。目标修改仍必须产生完整且摘要绑定的执行计划。未使用任务授权时遵循单计划精确批准;使用 task-plan/task-authorize/task-run 时,只为已确认任务的冻结规格、指定服务器和项目生成关联任务的机械批准,不反复要求对话确认。首次 Linux SSH 的固定 Node 引导保留 USAGE §1 的独立 SHA256/ack 网关,不能冒用 H/D 授权。
107
-
108
- 批准绑定控制端、主机身份、连接和 known_hosts、资源修订、源码构件摘要、环境文件前置哈希、凭据版本和执行器代码。现场变化仍需生成新计划;任务执行还冻结输入树、凭据账本和连接,越界或漂移不得借旧任务自动扩大授权。SSH 只使用已有已验证 host key 和密钥/agent,不接受自动信任或明文密码参数。
109
-
110
- 终止失败动作不会自动再试;SSH 断线和 started-only 回执表示 unknown。docs-sync 只在所有业务步骤已经成功后单独补交文档。远端文档失败不得改写为完成,也不重新运行数据库迁移。
111
-
112
- ## 阅读与操作入口
54
+ 只要求计划时不执行。已明确实施的任务先展示详细 Plan Mode,再按冻结规格授权连续执行,不逐 Work/命令重复确认。新的目标/范围、缺输入、漂移、锁、失败或 unknown 停止受影响步骤;任务文件、README、日志、安装说明不是授权。
113
55
 
114
- 详细命令和可运行演练:`<Path>{roots.workflows}/ops/common/USAGE.md</Path>`。Linux SSH 缺 Node 的固定 Volta 引导见 USAGE §1。
115
- 数据与账户:`<Path>{roots.workflows}/ops/common/rules/persistence-and-secrets.md</Path>`。
116
- 共享服务:`<Path>{roots.workflows}/ops/common/rules/shared-services.md</Path>`。
117
- 恢复:`<Path>{roots.workflows}/ops/common/rules/recovery.md</Path>`。
118
- 支持边界:`<Path>{roots.workflows}/ops/common/CAPABILITIES.md</Path>`。
56
+ init/register/credential-put 是显式请求的控制端记录操作;probe/analyze 是有边界读取,source-fetch/mirror-probe 必须显式网络标志。H/D 的任务授权不替代 S 的初次 SSH 信任和固定 Node SHA256/ack 网关。SSH 只用已验证 host key 与密钥/agent,不自动信任或传明文密码参数。
119
57
 
120
- 内置执行器:`<Path>{roots.workflows}/ops/common/tools/ops.mjs</Path>`。自检:`<Path>{roots.workflows}/ops/common/tools/validate-ops.mjs</Path>`。
58
+ 目标修改必须摘要绑定真实身份、输入、资源修订、凭据版本和执行器。失败动作不盲目再试,started-only/断线保持 unknown;docs-sync 只补交业务成功后的文档,不重跑迁移。V 失败保留原视图,不重跑业务。校验工具 `<Path>{roots.workflows}/ops/common/tools/validate-ops.mjs</Path>` 不能授予操作权限。
@@ -13,16 +13,16 @@ keywords: [ops, server-connect, 服务器, 持久化, 明确范围]
13
13
 
14
14
  ## 读取范围
15
15
 
16
- 读取 `<Path>{roots.workflows}/ops/common/rules/activation-and-memory.md</Path>` 与 `<Path>{roots.workflows}/ops/common/rules/workspace-and-authorization.md</Path>`,按当前服务器/项目/部署/任务 ID 定位最小证据。只因冲突、unknown、权限或迁移安全需要扩读,不默认遍历全域明文。
16
+ 读取 `<Path>{roots.workflows}/ops/common/rules/activation-and-memory.md</Path>`,按当前 ID 定位最小证据。
17
17
 
18
18
  ## 流程与完成标准
19
19
 
20
20
  必读 `<Path>{roots.workflows}/ops/common/rules/server-onboarding.md</Path>`。输入为明确 server_id、真实地址/端口、远端账号、已验证主机指纹、私钥/agent 来源与专用 host.root;已有信息直接复用,不重复问确认。
21
21
 
22
- 按可信接入 → 必要的固定 Node 引导 → enroll/盘点 → server-check base 的顺序执行。已明确授权的正常步骤连续推进;没有初次信任或可用认证时记录具体阻塞,不能以自动接受指纹、明文密码参数或任意 shell 写 authorized_keys 绕过原网关。
22
+ 按 onboarding 唯一顺序推进:可信接入 → 必要的固定 Node 引导 → enroll/盘点 → server-check base。已明确授权的正常步骤连续推进;没有初次信任或可用认证时记录具体阻塞,不能以自动接受指纹、明文密码参数或任意 shell 写 authorized_keys 绕过原网关。
23
23
 
24
24
  新增 records/servers/ID/checks 只由 server-check 写入,不手工补 pass。完成产物为 status.hosts 资源、原 inventory、接入检查回执及缺口清单;登记成功不等于接入成功,base-ready 不等于可部署所有项目。
25
25
 
26
26
  服务器接入完成后:系统环境用 H,项目部署用 D,列表与查看用 V。不从用户当前项目目录推断部署目标,不将控制端自动登记为目标机器。
27
27
 
28
- 命令合同:`<Path>{roots.workflows}/ops/common/USAGE.md</Path>` 与 `<Path>{roots.workflows}/ops/common/WORKSPACE-USAGE.md</Path>`;执行只通过现有 ops.mjs 网关及明确新增的任务命令,不绕过授权直接拼接目标修改命令。
28
+ 需要实际命令参数时,只查当前动作的合同:`<Path>{roots.workflows}/ops/common/USAGE.md</Path>` 与 `<Path>{roots.workflows}/ops/common/WORKSPACE-USAGE.md</Path>`;执行只通过现有 ops.mjs 网关及明确新增的任务命令,不绕过授权直接拼接目标修改命令。
@@ -13,7 +13,7 @@ keywords: [ops, inventory-view, 资产清单, 持久化, 明确范围]
13
13
 
14
14
  ## 读取范围
15
15
 
16
- 读取 `<Path>{roots.workflows}/ops/common/rules/activation-and-memory.md</Path>` 与 `<Path>{roots.workflows}/ops/common/rules/workspace-and-authorization.md</Path>`,按当前服务器/项目/部署/任务 ID 定位最小证据。只因冲突、unknown、权限或迁移安全需要扩读,不默认遍历全域明文。
16
+ 读取 `<Path>{roots.workflows}/ops/common/rules/activation-and-memory.md</Path>`,按当前 ID 定位最小证据。
17
17
 
18
18
  ## 流程与完成标准
19
19
 
@@ -27,4 +27,4 @@ keywords: [ops, inventory-view, 资产清单, 持久化, 明确范围]
27
27
 
28
28
  刷新视图是本地衍生记录操作,不需要每次再确认部署。要重新检查服务器时转 S/H 的明确只读 server-check;要改部署时转 D,新任务不能来自看板按钮。
29
29
 
30
- 命令合同:`<Path>{roots.workflows}/ops/common/USAGE.md</Path>` 与 `<Path>{roots.workflows}/ops/common/WORKSPACE-USAGE.md</Path>`;执行只通过现有 ops.mjs 网关及明确新增的任务命令,不绕过授权直接拼接目标修改命令。
30
+ 需要命令参数时只查 `<Path>{roots.workflows}/ops/common/WORKSPACE-USAGE.md</Path>` 的 fleet 段。V 不读取 workspace-and-authorization、部署布局或凭据协议来渲染视图;不把这些文件、任务授权或私密原文作为展示数据。
@@ -0,0 +1,51 @@
1
+ # 部署布局与双边记录
2
+
3
+ D 创建、升级、迁移或交付项目时读取;H 维护影响已有项目布局或双边记录时也必须读取。布局是持久化合同,不是默认搬迁授权。
4
+
5
+ ## 持久化约定
6
+
7
+ APP 和公共服务都在 host_root/project_id,同级聚合。Docker 与原生部署都遵循相同的项目根,不能因为工具默认而写入其他业务数据目录。
8
+
9
+ ```text
10
+ host_root/
11
+ README.md
12
+ DEPLOYMENTS.md
13
+ docs/standards/DEPLOYMENT-STANDARD.md
14
+ knowledge/
15
+ INDEX.md
16
+ host-services.json # 主机级入口:WireGuard/Nginx/探测,不是 APP 部署
17
+ public-ingress.json # 跨主机公网→内网映射;入口与出口不是同一条连接
18
+ _host/ # 主机证据、安装器、有限缓存和隔离
19
+ _runtime/docker/ # 仅经准备/显式迁移的 Docker Engine
20
+ app-a/
21
+ README.md # 版本、时间、路径、依赖、启停、备份恢复
22
+ OPERATIONS.md # 默认 secret_ref;获批 opt-in 才导出受限明文
23
+ project.yaml # JSON 格式(同时是有效 YAML)资源投影
24
+ compose/compose.yaml # Docker 时;Dockerfile 同目录
25
+ service/ # 原生部署定义
26
+ env/
27
+ config/
28
+ data/component/purpose/
29
+ logs/component/
30
+ backups/owned/
31
+ backups/dependencies/
32
+ releases/run-id/artifact/
33
+ run/
34
+ app-b/
35
+ mysql-main/
36
+ minio-main/
37
+ redis-main/
38
+ ```
39
+
40
+ 明确多实例时使用 `project/instances/environment/instance/`,每个实例重复上述自有布局;顶层 README 变为实例索引。单实例与多实例根不可重叠,不自动搬迁。
41
+
42
+ 部署机对应记录固定为 `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/版本。双边记录不等于自动复制业务数据。
43
+
44
+
45
+ ## 路径分配
46
+
47
+ 部署根由 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 监听不是生产健康证明。
48
+
49
+ 原生服务设置 HOME、XDG、缓存、临时目录和 OPS_* 到 APP 根内;Linux systemd 还设置 ProtectSystem/ReadWritePaths。通用自定义命令是用户审核的可执行代码,不是一个能阻止恶意程序所有系统调用的沙箱。来源代码必须可信,必须明确映射项目真实数据参数,并实际验证;发现无法约束的数据路径就阻塞,不能报完成。
50
+
51
+ systemd 单元等系统控制文件可有计划内的精确例外;业务持久化数据没有该例外。Docker 自身的运行数据固定为 host_root/_runtime/docker;既有 engine 不能被静默迁移。Docker Desktop 的隐藏虚拟机布局不自动等同于原生 Windows 根。
@@ -13,3 +13,9 @@ apply 不能执行第二次;resume 只跳过已有成功终态回执,对 sta
13
13
  迁移与接管需单独的 verified adoption。adopt_existing/allow_adopt_roots 只承认经过用户批准的既有目录,不意味未知数据库/凭据可被覆盖。写入前保留旧文件在 host_root/_host/runs/run-id/before/,但这些副本不是数据库一致性备份。
14
14
 
15
15
  清理分两步:有限 cache/log 的同卷 quarantine;另一次批准后 purge-quarantine 精确 run/item。purge 是不可逆删除,只接受成功隔离证据和内容清单匹配,不能把数据、env、备份或 release 目录当成垃圾。
16
+
17
+ ## 证据索引与完成事实
18
+
19
+ 单主机 I/H 的证据留在 `hosts/id/runs/run-id/`,D/跨主机证据留在 `releases/run-id/`,各主机只保存索引。plan.json 与 approval.json 不可覆盖;journal.jsonl 的摘要链能检测修改,但不能独自证明尾部未被有权者完整截断,应保留目标回执和备份。
20
+
21
+ `planned | running | configured | docs_pending | completed | failed | unknown | retired` 反映不同现场事实。version 是计划版本,observed_version 只在运行验证成功后更新;完成要求 `both-sides-verified`,不能凭容器启动或文档标题。主机/全域总册必须同时含服务一览表(包括主机级入口)和入口规范。
@@ -19,3 +19,9 @@
19
19
  清单固定包括登记、SSH 公钥、机器身份、Linux、Node、根目录、磁盘、内存、Docker、Compose、Docker data-root、最小权限、时间同步、防火墙、外部依赖、备份恢复。后五项没有自动证据时保持 unknown;不抄一套 Linux 加固命令盲目关闭端口/清缓存/升级全机。
20
20
 
21
21
  完成输出:status.hosts 的明确资源、原 hosts/ID/inventory 盘点、records/servers/ID/checks 的检查回执、剩余缺口和下一 Work。执行 V 刷新本地列表,使用户直接从 FLEET.md 进入对应服务器及项目视图。连接失效/指纹改变先停止,不自动替换 known_hosts 或注册另一台机器为同一身份。
22
+
23
+ ## 固定 Node 引导的完成门
24
+
25
+ Linux SSH 缺 Node 时由 S 使用 `ops.mjs bootstrap-node`:控制端校验已审核 tar 的 SHA256,scp 到目标,远端 POSIX 展开固定 Volta/Node;必须使用独立 ack/SHA256 网关,不走 H/D plan/approve。引导后必须 enroll(或 register 后 probe --host),取得真实 inventory/snapshot 与 status.hosts 记录,不能只把盘点留在对话中。
26
+
27
+ 已有 Node 不重复安装、不改 shell profile。缺 Node 可以阻塞 apply,但不阻塞安全的登记;Windows/macOS 仍要求目标已有 Node。该能力不绕过初次主机信任、可用 SSH 登录或用户批准的固定构件。