@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
@@ -274,13 +274,13 @@ export function deploymentReadme(status, dep, runId, generatedAt, { ledger = nul
274
274
  out.push("## 同项目部署位置\n", replicas.sort((a, b) => a.deployment_id < b.deployment_id ? -1 : 1).map((d) => `- ${d.host_id} / ${d.deployment_id}:${code(d.root)}`).join("\n") + "\n");
275
275
  if (controller) out.push("\n本目录是部署机的配置、凭据、运行证据和远端文档记录;不声称自动复制了远端业务数据。数据备份需要独立的备份策略和回执。\n");
276
276
  if (includeCredentials) out.push(credentialsText(credentialRefs(status, dep), ledger));
277
- else out.push("\n## 凭据记录\n\n本 README 默认不写密码。获授权管理员读取本项目 OPERATIONS.md(启用时)或部署机明文手册;密码未知时必须补齐,不能编造。\n");
277
+ else out.push("\n## 凭据记录\n\nsecret_ref: " + credentialRefs(status, dep).map(code).join(", ") + "\n\n本文件默认不写密码。凭据按 ID@版本定位受限 private/credentials.json;真实值仅在批准 plaintext_documentation 后导出。未知值不得编造。\n");
278
278
  return out.join("\n") + "\n";
279
279
  }
280
280
 
281
281
  export function hostReadme(status, hid, runId, at, { ledger = null, full = false, controller = false } = {}) {
282
282
  const host = status.hosts[hid];
283
- const includeCredentials = Boolean(full && controller && ledger);
283
+ const includeCredentials = Boolean(full && controller && ledger && plaintextDocumentation(status));
284
284
  const rows = overviewRows(status, hid, { ledger, includeCredentials });
285
285
  const out = [
286
286
  `# 主机 ${host.display_name} / ${hid}\n`,
@@ -368,6 +368,11 @@ const KNOWLEDGE_INDEX = `# 共享知识索引
368
368
  只收录经用户确认、带来源和最后验证时间的知识,不存密码。
369
369
  `;
370
370
 
371
+ // Legacy absent field retains existing deliberate behavior; init seeds false. Changing policy requires a new approved plan.
372
+ export function plaintextDocumentation(status) {
373
+ return status.policies.plaintext_documentation ?? true;
374
+ }
375
+
371
376
  export function deliveryBundle(state, plan, status, ledger, verifiedIds) {
372
377
  const at = now();
373
378
  const rid = plan.run_id;
@@ -381,9 +386,9 @@ export function deliveryBundle(state, plan, status, ledger, verifiedIds) {
381
386
  for (const did of [...new Set(verifiedIds)].sort()) {
382
387
  const d = status.deployments[did];
383
388
  const hid = d.host_id;
384
- const readme = deploymentReadme(status, d, rid, at, { ledger, includeCredentials: status.policies.server_readme_credentials });
389
+ const readme = deploymentReadme(status, d, rid, at, { ledger, includeCredentials: Boolean(status.policies.server_readme_credentials && plaintextDocumentation(status)) });
385
390
  addRemote(hid, pathFor(status, d, "README.md"), readme);
386
- const operations = deploymentReadme(status, d, rid, at, { ledger, includeCredentials: true });
391
+ const operations = deploymentReadme(status, d, rid, at, { ledger, includeCredentials: plaintextDocumentation(status) });
387
392
  if (status.policies.server_operations) addRemote(hid, pathFor(status, d, "OPERATIONS.md"), operations);
388
393
  addRemote(hid, pathFor(status, d, "project.yaml"), JSON.stringify(d, null, 2) + "\n");
389
394
  addRemote(hid, pathFor(status, d, "run/release-state.json"), JSON.stringify({
@@ -397,7 +402,7 @@ export function deliveryBundle(state, plan, status, ledger, verifiedIds) {
397
402
  addRemote(hid, targetJoin({ ...status.hosts[hid], root: projectroot }, "README.md"), text);
398
403
  }
399
404
  const prefix = `hosts/${hid}/deployments/${did}`;
400
- local[prefix + "/README.md"] = deploymentReadme(status, d, rid, at, { ledger, includeCredentials: true, controller: true });
405
+ local[prefix + "/README.md"] = deploymentReadme(status, d, rid, at, { ledger, includeCredentials: plaintextDocumentation(status), controller: true });
401
406
  local[prefix + "/OPERATIONS.md"] = operations;
402
407
  local[prefix + "/deployment.json"] = JSON.stringify(d, null, 2) + "\n";
403
408
  local[prefix + "/server/README.md"] = readme;
@@ -420,8 +425,8 @@ export function deliveryBundle(state, plan, status, ledger, verifiedIds) {
420
425
  local[`hosts/${hid}/knowledge/host-services.json`] = hostServicesDocument(h);
421
426
  if (ingressJson) local[`hosts/${hid}/knowledge/public-ingress.json`] = ingressJson;
422
427
  }
423
- local["FLEET-DEPLOYMENTS.md"] = fleetDocument(status, Object.keys(status.hosts), rid, at, { ledger, includeCredentials: true });
424
- local["README.md"] = "# OPS 控制端\n\n主机记录位于 hosts/<host_id>/;项目关联位于 status.json;项目部署记录位于 hosts/<host_id>/deployments/<deployment_id>/;完整明文总册位于 FLEET-DEPLOYMENTS.md;凭据账本位于 private/credentials.json;执行证据按 host runs / release 保存。主机级入口见 hosts/<id>/knowledge/host-services.json,跨主机公网入口见 knowledge/public-ingress.json。\n\n不得清理此运行态目录来替换静态 workflow。双边文档完成由每次运行 docs-receipt.json 证明。\n";
428
+ local["FLEET-DEPLOYMENTS.md"] = fleetDocument(status, Object.keys(status.hosts), rid, at, { ledger, includeCredentials: plaintextDocumentation(status) });
429
+ local["README.md"] = "# OPS 控制端\n\n主机记录位于 hosts/<host_id>/;项目关联位于 status.json;项目部署记录位于 hosts/<host_id>/deployments/<deployment_id>/;总册位于 FLEET-DEPLOYMENTS.md(新安装默认仅引用凭据版本);凭据账本位于 private/credentials.json;执行证据按 host runs / release 保存。主机级入口见 hosts/<id>/knowledge/host-services.json,跨主机公网入口见 knowledge/public-ingress.json。\n\n不得清理此运行态目录来替换静态 workflow。双边文档完成由每次运行 docs-receipt.json 证明。\n";
425
430
  local["docs/standards/DEPLOYMENT-STANDARD.md"] = STANDARD;
426
431
  if (!existsSync(join(state, "knowledge", "INDEX.md"))) {
427
432
  local["knowledge/INDEX.md"] = "# 共享知识索引\n\n只收录经用户批准、带来源与最后验证日期的通用知识。运行时环境和密码不自动提升为共享知识。跨主机入口规范见各主机 knowledge/public-ingress.json。\n";
@@ -237,6 +237,7 @@ export function compilePlan(state, specPath) {
237
237
  const current = load(state);
238
238
  if (current.controller == null) throw new OpsError("initialize the controller first");
239
239
  const after = structuredClone(current);
240
+ if (spec.plaintext_documentation !== undefined) after.policies.plaintext_documentation = spec.plaintext_documentation;
240
241
  const ledger = ledgerLoad(state);
241
242
  const revisions = spec.resource_updates || {};
242
243
  for (const [group, idkey, fixed] of [["hosts", "host_id", ["host_id", "root", "identity", "platform"]], ["projects", "project_id", ["project_id", "kind", "service_type"]]]) {
@@ -277,7 +278,12 @@ export function compilePlan(state, specPath) {
277
278
  return op;
278
279
  };
279
280
  const fileOp = (hostId, did, path, { content = undefined, binary = undefined, mode = undefined } = {}) => {
280
- const data = { path, mode: mode ?? defaultFileMode(path) };
281
+ const secretBearing = credentialsIn(content ?? "").size > 0;
282
+ const selectedMode = mode ?? (secretBearing ? 0o600 : defaultFileMode(path));
283
+ if ((secretBearing || /(?:^|[\\/])env[\\/]/.test(path)) && (selectedMode & 0o077)) {
284
+ throw new OpsError("secret-bearing config/env mode must be 0600 or stricter; use explicit service ownership, not world-readable secrets");
285
+ }
286
+ const data = { path, mode: selectedMode };
281
287
  if (content !== undefined) data.content = content;
282
288
  else data.content_b64 = Buffer.from(binary).toString("base64");
283
289
  return add(hostId, did, "write", data);
@@ -116,3 +116,9 @@ SpecDev 不假定运行时一定提供项目根变量。Ticket、Evidence、诊
116
116
  - 目录引用是否以 `/` 结束且文件引用是否不以 `/` 结束。
117
117
 
118
118
  路径检查通过仅表示引用结构正确,不代表工件语义、需求、设计或实现正确。
119
+
120
+ ## 9. 与标准 Skill 的分发边界
121
+
122
+ 本文件是 SpecDev 内部动态工件协议,不是通用 Agent Skills 的文件格式规范。独立标准 Skill 的静态 references/assets 使用相对路径;workflow 私有 Skill 声明 compatibility 后继续使用本 Path 合同。已有持久化引用不批量迁移。
123
+
124
+ 实际执行前可以使用只读 `speculo resolve <project-root> --path '<Path>{roots.workflows}/specdev/common/tools/validate-specdev.mjs</Path>'` 展开单个指针。解析器先打开字面 workspace 路径,拒绝未知 alias、未替换变量、通配符、越界路径与链接跳转;不执行 shell、不授予文件写入权限。完整命令由已获授权调用方使用参数数组构造,不把 Path 标记或用户文本直接交给 shell。
@@ -1,6 +1,8 @@
1
1
  ---
2
- name: specdev-code-review
2
+ name: code-review
3
3
  description: 从不可变固定点对本地 diff 执行隔离的标准轴与规范轴审查,供独立 C Work 和 I Work 最终审查共同调用。
4
+ compatibility: Requires the owning Speculo workflow and its workspace path resolver.
5
+ metadata: {"speculo-legacy-name": "specdev-code-review"}
4
6
  ---
5
7
 
6
8
  # SpecDev Code Review
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: dev-worktree
3
3
  description: 管理 SpecDev required Ticket 的 Git worktree;创建隔离来源 workspace、固定实现 commit,并由 Lead 在 parent-candidate checkout 验证后推进父分支。
4
+ compatibility: Requires the owning Speculo workflow and its workspace path resolver.
4
5
  ---
5
6
 
6
7
  # Dev Worktree
@@ -1,6 +1,8 @@
1
1
  ---
2
- name: specdev-plan-quality-review
2
+ name: plan-quality-review
3
3
  description: T 发布计划型 Ticket 或 P 发布/重规划 Goal 前检查决策完备、Skill 调用和执行门禁;不执行代码审查或代替用户批准。
4
+ compatibility: Requires the owning Speculo workflow and its workspace path resolver.
5
+ metadata: {"speculo-legacy-name": "specdev-plan-quality-review"}
4
6
  ---
5
7
 
6
8
  # Plan Quality Review
@@ -1,6 +1,8 @@
1
1
  ---
2
- name: specdev-research
2
+ name: research
3
3
  description: 为调用 Work 的具体决定研究外部技术、依赖、标准或未知行为,逐声明返回一手来源、限制与不确定性,由调用方写入其拥有的工件。
4
+ compatibility: Requires the owning Speculo workflow and its workspace path resolver.
5
+ metadata: {"speculo-legacy-name": "specdev-research"}
4
6
  ---
5
7
 
6
8
  # SpecDev Research
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: subagent-delivery
3
3
  description: 仅在 Lead 规划派单合同、发出受限任务或验收其返回时调用;不是通用实现入口,外部网页交付仍为 ZIP-only。
4
+ compatibility: Requires the owning Speculo workflow and its workspace path resolver.
4
5
  ---
5
6
 
6
7
  # Subagent Delivery