create-yss-spec 3.5.4 → 3.5.6

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 (105) hide show
  1. package/README.md +10 -0
  2. package/cli-core.lock.json +130 -0
  3. package/package.json +7 -3
  4. package/resources/upgrade-skill/SKILL.md +26 -0
  5. package/resources/upgrade-skill/protocol.md +40 -0
  6. package/resources/upgrade-skill/references/cli-families.md +14 -0
  7. package/src/cli/help.js +1 -0
  8. package/src/cli/index.js +12 -0
  9. package/src/filesystem/transaction-runner.js +27 -0
  10. package/src/template/asset-runtime.js +9 -1
  11. package/src/template/instance-runtime.js +4 -2
  12. package/src/template/verification-runtime.js +9 -1
  13. package/template/.agents/skills/.strategic-design-skills-manifest.json +2 -2
  14. package/template/.agents/skills/yss-harness-upgrade/SKILL.md +26 -0
  15. package/template/.agents/skills/yss-harness-upgrade/references/cli-families.md +14 -0
  16. package/template/.agents/skills/yss-implementation-contract-compiler/references/boundaries.md +1 -0
  17. package/template/.agents/skills/yss-product-lifecycle/SKILL.md +15 -19
  18. package/template/.agents/skills/yss-stage-decision/SKILL.md +3 -0
  19. package/template/.agents/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +8 -4
  20. package/template/.agents/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +8 -4
  21. package/template/.agents/skills/yss-stage-decision/tests/run-scenarios.mjs +13 -1
  22. package/template/.codex/skills/yss-harness-upgrade/SKILL.md +26 -0
  23. package/template/.codex/skills/yss-harness-upgrade/references/cli-families.md +14 -0
  24. package/template/.codex/skills/yss-implementation-contract-compiler/references/boundaries.md +1 -0
  25. package/template/.codex/skills/yss-product-lifecycle/SKILL.md +15 -19
  26. package/template/.codex/skills/yss-stage-decision/SKILL.md +3 -0
  27. package/template/.codex/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +8 -4
  28. package/template/.codex/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +8 -4
  29. package/template/.codex/skills/yss-stage-decision/tests/run-scenarios.mjs +13 -1
  30. package/template/.cursor/skills/yss-harness-upgrade/SKILL.md +26 -0
  31. package/template/.cursor/skills/yss-harness-upgrade/references/cli-families.md +14 -0
  32. package/template/.cursor/skills/yss-implementation-contract-compiler/references/boundaries.md +1 -0
  33. package/template/.cursor/skills/yss-product-lifecycle/SKILL.md +15 -19
  34. package/template/.cursor/skills/yss-stage-decision/SKILL.md +3 -0
  35. package/template/.cursor/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +8 -4
  36. package/template/.cursor/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +8 -4
  37. package/template/.cursor/skills/yss-stage-decision/tests/run-scenarios.mjs +13 -1
  38. package/template/.pi/skills/yss-harness-upgrade/SKILL.md +26 -0
  39. package/template/.pi/skills/yss-harness-upgrade/references/cli-families.md +14 -0
  40. package/template/.pi/skills/yss-implementation-contract-compiler/references/boundaries.md +1 -0
  41. package/template/.pi/skills/yss-product-lifecycle/SKILL.md +15 -19
  42. package/template/.pi/skills/yss-stage-decision/SKILL.md +3 -0
  43. package/template/.pi/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +8 -4
  44. package/template/.pi/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +8 -4
  45. package/template/.pi/skills/yss-stage-decision/tests/run-scenarios.mjs +13 -1
  46. package/template/.template-spec/agents/yss-skill-registry.yaml +9 -1
  47. package/template/.template-spec/process/contract-reading.md +1 -1
  48. package/template/.template-spec/process/document-writing.md +12 -0
  49. package/template/.template-spec/process/harness-upgrade.md +40 -0
  50. package/template/.template-spec/process/lifecycle-artifact-map.md +2 -2
  51. package/template/.template-spec/process/lifecycle-registry.yaml +3 -1
  52. package/template/.template-spec/process/schemas/approval-record.schema.json +23 -0
  53. package/template/.template-spec/process/stage-tracking.md +2 -2
  54. package/template/.template-spec/process/structured-assets.md +35 -0
  55. package/template/.template-spec/process/templates/lifecycle-checkpoint-template.json +48 -0
  56. package/template/.template-spec/templates/approval-record-template.json +23 -0
  57. package/template/scripts/check-plan-stage-coverage +12 -0
  58. package/template/scripts/contract +6 -0
  59. package/template/scripts/lib/approval-record.mjs +4 -3
  60. package/template/scripts/lib/asset-command.mjs +35 -0
  61. package/template/scripts/lib/asset-migration.mjs +101 -0
  62. package/template/scripts/lib/asset-transactions.mjs +235 -0
  63. package/template/scripts/lib/checkpoint-boundary.mjs +3 -0
  64. package/template/scripts/lib/contract-views.mjs +7 -1
  65. package/template/scripts/lib/json-schema.mjs +43 -4
  66. package/template/scripts/lib/lifecycle-context-query.mjs +7 -0
  67. package/template/scripts/lib/lifecycle-presentation.mjs +77 -0
  68. package/template/scripts/lib/lifecycle-status.mjs +16 -1
  69. package/template/scripts/lib/plan-stage-coverage.mjs +40 -0
  70. package/template/scripts/lib/reading-view-bundle.mjs +1 -0
  71. package/template/scripts/lib/reading-view-markdown.mjs +8 -6
  72. package/template/scripts/lib/stage-tracking-migration.mjs +11 -3
  73. package/template/scripts/lib/structured-assets.mjs +81 -0
  74. package/template/scripts/lib/task-package.mjs +4 -1
  75. package/template/scripts/lifecycle-status +6 -3
  76. package/template/scripts/verify-lifecycle-checkpoint +8 -5
  77. package/template/skills-lock.json +21 -9
  78. package/template.manifest.json +2 -1
  79. package/template.snapshot.json +5 -5
  80. package/vendor/cli-core/build.mjs +302 -0
  81. package/vendor/cli-core/bundle.mjs +198 -0
  82. package/vendor/cli-core/cli.mjs +182 -0
  83. package/vendor/cli-core/command-runner.mjs +107 -0
  84. package/vendor/cli-core/engine.mjs +239 -0
  85. package/vendor/cli-core/family.mjs +28 -0
  86. package/vendor/cli-core/gitignore.mjs +15 -0
  87. package/vendor/cli-core/identity.mjs +274 -0
  88. package/vendor/cli-core/io.mjs +153 -0
  89. package/vendor/cli-core/layout-migration.mjs +64 -0
  90. package/vendor/cli-core/legacy.mjs +19 -0
  91. package/vendor/cli-core/migrate.mjs +237 -0
  92. package/vendor/cli-core/migration-adapter.mjs +19 -0
  93. package/vendor/cli-core/package.json +13 -0
  94. package/vendor/cli-core/scaffold.mjs +117 -0
  95. package/vendor/cli-core/spec-migration-adapter.mjs +83 -0
  96. package/vendor/cli-core/tests/build.test.mjs +40 -0
  97. package/vendor/cli-core/tests/cli.test.mjs +1065 -0
  98. package/vendor/cli-core/tests/migration-version.test.mjs +8 -0
  99. package/vendor/cli-core/tests/package-contract.mjs +89 -0
  100. package/vendor/cli-core/transaction.mjs +529 -0
  101. package/vendor/cli-core/update.mjs +79 -0
  102. package/vendor/cli-core/vendor/NOTICES.md +6 -0
  103. package/vendor/cli-core/vendor/yaml.LICENSE +13 -0
  104. package/vendor/cli-core/vendor/yaml.mjs +6311 -0
  105. package/vendor/cli-core/verification.mjs +131 -0
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { readFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { assertAssetTransactionIdle, assertCurrentAssetReference } from "../../../../scripts/lib/asset-transactions.mjs";
3
5
  import { createHash } from "node:crypto";
4
6
  import { resolve } from "node:path";
5
7
  import process from "node:process";
@@ -7,6 +9,7 @@ import { parseArgs } from "node:util";
7
9
  import { parseDocument } from "../../../../scripts/vendor/yaml.mjs";
8
10
  import { loadApprovalRecord, resolveApprovalRef, validateApprovalRecord } from "../../../../scripts/lib/approval-record.mjs";
9
11
  import { verifyContextSnapshot } from "../../../../scripts/lib/context-contract.mjs";
12
+ import { parseAsset, validateAssetStructure } from '../../../../scripts/lib/structured-assets.mjs';
10
13
 
11
14
  const required = ["schema_version", "stage_decision_id", "package_version", "status", "problem_statement", "target_users", "mvp", "non_goals", "success_criteria", "test_seams", "confirmed_decisions", "assumptions", "constraints", "unresolved_items", "context_snapshot", "domain_strategy_ref", "impact_assessment", "downstream_mapping", "evidence_refs", "approval"];
12
15
  const idPattern = /^stage-decision\.[a-z0-9][a-z0-9-]*$/;
@@ -25,6 +28,7 @@ function canonical(value) { if (Array.isArray(value)) return value.map(canonical
25
28
  function digest(value) { return `sha256:${createHash("sha256").update(JSON.stringify(canonical(value))).digest("hex")}`; }
26
29
 
27
30
  async function validate(data, contextRoot) {
31
+ validateAssetStructure(data, 'stage-decision-package');
28
32
  const errors = [];
29
33
  if (!data || typeof data !== "object" || Array.isArray(data)) return ["合同必须是对象"];
30
34
  if (data.schema_version === 1) return ["migration-required: stage decision package v1 必须迁移到 v2 context_snapshot"];
@@ -143,11 +147,11 @@ const file = positionals[0];
143
147
  if (!file) fail(["用法: validate-stage-decision-package.mjs <package.yaml> [--root <project-root>]"]);
144
148
  else {
145
149
  try {
150
+ assertAssetTransactionIdle(values.root);
151
+ assertCurrentAssetReference(values.root, path.relative(path.resolve(values.root), path.resolve(file)));
146
152
  const source = await readFile(file, "utf8");
147
- const document = parseDocument(source, { maxAliasCount: 0, uniqueKeys: true });
148
- if (document.errors.length) fail([document.errors[0].message]);
149
- else {
150
- const errors = await validate(document.toJS({ maxAliasCount: 0 }), values.root);
153
+ {
154
+ const errors = await validate(parseAsset(source, file), values.root);
151
155
  if (errors.length) fail(errors);
152
156
  else process.stdout.write(JSON.stringify({ result: "completed", contract: file }, null, 2) + "\n");
153
157
  }
@@ -200,6 +200,18 @@ try {
200
200
  await writeFile(packageFile, packageSource);
201
201
  const packagePass = run(packageValidator, packageFile, temporaryRoot);
202
202
  if (packagePass.status !== 0) throw new Error(`valid v3 stage decision package should pass: ${packagePass.stderr}`);
203
+ for (const format of ['yaml', 'json']) {
204
+ for (const [name, mutate, pattern] of [
205
+ ['record-note', p => {p.test_seams[0].note='undeclared';}, /Additional properties/],
206
+ ['mapping-note', p => {p.downstream_mapping[0].note='undeclared';}, /Additional properties/],
207
+ ['duplicate-source', p => p.test_seams[0].source_refs.push(p.test_seams[0].source_refs[0]), /non-unique/]
208
+ ]) {
209
+ const value = parseYaml(packageSource); mutate(value);
210
+ const file = join(temporaryRoot, `${name}.${format}`);
211
+ await writeFile(file, JSON.stringify(value, null, 2));
212
+ expectBlocked(run(packageValidator, file, temporaryRoot), pattern, `${name} ${format}`);
213
+ }
214
+ }
203
215
 
204
216
  const bundleFile = join(temporaryRoot, "plan-review-bundle.json");
205
217
  const bundledDomainValue = parseYaml(domainSource);
@@ -265,7 +277,7 @@ try {
265
277
  const wrongArrayValue = parseYaml(packageSource);
266
278
  wrongArrayValue.target_users = [42];
267
279
  await writeFile(packageWrongArray, JSON.stringify(wrongArrayValue, null, 2));
268
- expectBlocked(run(packageValidator, packageWrongArray, temporaryRoot), /target_users\[0\]/, "package non-string array item");
280
+ expectBlocked(run(packageValidator, packageWrongArray, temporaryRoot), /target_users(?:\[0\]|\.0)/, "package non-string array item");
269
281
  } finally {
270
282
  await rm(temporaryRoot, { recursive: true, force: true });
271
283
  }
@@ -0,0 +1,26 @@
1
+ ---
2
+ name: yss-harness-upgrade
3
+ description: 升级既有 YSS 模板实例,规划同家族版本迁移、定制合并、持久归档、验证与恢复;用于 create-yss-spec 及 design/backend/frontend CLI 实例升级,不处理业务代码或数据库迁移。
4
+ ---
5
+
6
+ # YSS 实例升级
7
+
8
+ 先读取根 `yss-project.yaml`、`CONTEXT.md` 和 [统一协议](../../../.template-spec/process/harness-upgrade.md)。目标必须是已登记的同家族 `project-instance`;身份/schema 不支持时只诊断,不能重新 init 或猜测身份。模板源的技能维护由 `maintaining-skills` 处理。
9
+
10
+ ## 工作顺序
11
+
12
+ 1. 确认目标绝对路径、用户授权范围、实际 CLI 路径及版本、模板 metadata、工作区已有改动。依据 [家族适配](references/cli-families.md) 选定 CLI。未指定目标版本时查询同家族稳定版本并固定精确包版本和摘要,执行期间不再引用 `latest`。CLI 程序升级和项目实例迁移分别记录,不自动安装全局版本。
13
+ 2. 用固定 CLI 的 `migrate status` 检查中断事务。有中断时先 `migrate recover` 预览,按既有授权执行 `--apply`,然后重新规划。不得删除状态目录绕过恢复。
14
+ 3. `migrate plan --target-dir <绝对路径> --output <项目外全新计划.json>`。旧治理布局迁移显式附加 `--migrate-layout`;退出分发文件清理显式附加 `--prune`。计划阶段不修改项目;检查计划中的版本、变化路径、保留项和归档位置。
15
+ 4. 冲突时在项目外起草候选差异和 `--resolutions` 文件。只使用计划报告的可信受管冲突;`preserve` 保留原文,`merge` 提供候选内容并绑定原文件及新模板摘要。向用户展示新的语义取舍,复用已有范围授权;未获授权的新决定保持等待。不能使用 `--force` 批量覆盖,不能先修改项目来消除冲突。
16
+ 5. 用相同固定 CLI 执行 `migrate apply --plan <计划>`。执行器重新生成候选并验证计划、归档、身份和输入摘要;`STALE_PLAN` 时重新规划,不能手改摘要续跑。迁移不自动 commit、stash、reset、push 或发布。
17
+ 6. 查看回执和实例验证结果,执行相同选项的新计划验证无重复迁移。核对业务文件与既有工作区改动保持,说明保留的定制和现有失败。输出实际版本、归档/回执位置、验证证据和剩余问题;结构检查不能代替真实 CLI 或 Agent 行为验证。
18
+
19
+ ## 恢复与边界
20
+
21
+ - `migrate recover` 恢复未完成事务;`migrate rollback` 预览最近一次成功迁移的整体回退。两者仅加 `--apply` 才写入。回退遇到后续修改或损坏归档必须保留现场,不覆盖新工作。
22
+ - Spec、Ticket、合同、人工回复、批准和正式验证默认保留。路径迁移不产生新批准;语义或批准绑定变化交回原生命周期,不补造旧状态。
23
+ - 不执行跨家族转换、任意历史版本跳转、业务重构或数据库迁移。未知基线不能由 Agent 猜测;归档不能作为扩大写入授权的理由。
24
+ - 旧实例缺少本技能时,从已固定 CLI 包的 `resources/upgrade-skill/` 在项目外读取入口及相邻协议。不得先同步目标项目来安装升级技能。
25
+
26
+ 本技能只编排,不复制迁移执行器。研究新技术事实时用 `yss-research`;结论输出按 `lifecycle-document-output` 使用 `i-have-adhd`。不批准 Slice、不设置 `ready-for-agent`、不宣称可发布。
@@ -0,0 +1,14 @@
1
+ # 同家族升级适配
2
+
3
+ | CLI | 实例 metadata |
4
+ |---|---|
5
+ | create-yss-spec | .yss-template.json |
6
+ | create-yss-harness-design | .yss-harness-design.json |
7
+ | create-yss-harness-backend | .yss-harness-backend.json |
8
+ | create-yss-harness-frontend | .yss-harness-frontend.json |
9
+
10
+ 身份还需通过各 CLI 原有 profile/schema 检查。多个 metadata、旧 dev 家族、未知 schema、符号链接或矛盾 profile 均不能自动接管。`migrate` 不是降级或跨家族工具。
11
+
12
+ 四类 CLI 的新入口一致:`migrate plan|apply|status|recover|rollback`,支持 `--json`。`migrate plan` 只向显式指定的项目外新文件输出;`apply --plan <文件>` 显式写入。recover/rollback 默认只读,写入需 `--apply`。不要从旧 `sync` 推断新接口:主 CLI 普通 sync 会写入,专职 CLI sync 默认预览。
13
+
14
+ 目标模板来自当前固定 CLI 包,不接受计划中的下载地址或脚本。版本、模板和执行器摘要不匹配时更换为生成计划的原包,或重新规划;不得修改计划来绕过。已有 `update/upgrade` 仅更新 CLI 程序,既有 `recover` 仍只恢复原事务。
@@ -114,6 +114,7 @@
114
114
  | `yss-implementation-contract-compiler` | `i-have-adhd` | `context-conditional` | `lifecycle-document-output` |
115
115
  | `yss-openapi-governance` | `i-have-adhd` | `context-conditional` | `lifecycle-document-output` |
116
116
  | `yss-stage-decision` | `i-have-adhd` | `context-conditional` | `lifecycle-document-output` |
117
+ | `yss-harness-upgrade` | `i-have-adhd` | `context-conditional` | `lifecycle-document-output` |
117
118
  | `yss-research` | `i-have-adhd` | `context-conditional` | `lifecycle-document-output` |
118
119
  | `yss-product-lifecycle` | `i-have-adhd` | `context-conditional` | `lifecycle-document-output` |
119
120
  | `code-review` | `i-have-adhd` | `context-conditional` | `lifecycle-document-output` |
@@ -7,27 +7,27 @@ description: 编排 YSS 研发全生命周期;当阶段、产物、门禁或 S
7
7
 
8
8
  合同阅读与托管更新:`scripts/contract view|render|check-views`;见 `.template-spec/process/contract-reading.md`。
9
9
 
10
- 文档按 `document_writing` 条件调用 `i-have-adhd`,并传递 `.template-spec/process/document-writing.md` 规范。
10
+ 五类机器资产默认 JSON;读写、迁移及恢复须遵循 `.template-spec/process/structured-assets.md`。
11
11
 
12
- ## 事实源与按需加载
12
+ 文档及进度按 `document_writing` 调用 `i-have-adhd`,遵循 `.template-spec/process/document-writing.md`;正文用中文名称与动作,编号按需追踪。
13
13
 
14
- 先读 `yss-project.yaml`、`AGENTS.md`、`CONTEXT.md` 和当前任务视图,按需加载合同子树:
14
+ ## 事实源
15
+
16
+ 先读 `yss-project.yaml`、`AGENTS.md`、`CONTEXT.md` 和当前任务视图,按需读合同:
15
17
 
16
18
  | 事实 | 权威来源 |
17
19
  |---|---|
18
- | 阶段、聚合门禁、内部检查、产物、工作单元、证据和稳定 ID | `.template-spec/process/lifecycle-registry.yaml` |
19
- | 执行模式、流转、readiness、暂停、结果和授权语义 | `references/orchestration-contract.yaml` |
20
- | Skill 身份、capability、依赖和路由 | `.template-spec/agents/yss-skill-registry.yaml` |
20
+ | 阶段、门禁、产物、工作单元、证据及 ID | `.template-spec/process/lifecycle-registry.yaml` |
21
+ | 执行、流转和授权合同 | `references/orchestration-contract.yaml` |
22
+ | Skill 身份、能力、依赖和路由 | `.template-spec/agents/yss-skill-registry.yaml` |
21
23
  | 数字人角色、运行时和会签策略 | `.template-spec/agents/digital-human-roles.yaml` |
22
- | 影响面、裁剪和模板维护强度 | `.template-spec/process/harness-process-tailoring.md`、`.template-source/process/maintenance-intensity.yaml` |
23
-
24
- 查询合同:
24
+ | 影响面、裁剪与维护强度 | `.template-spec/process/harness-process-tailoring.md`、`.template-source/process/maintenance-intensity.yaml` |
25
25
 
26
26
  ```bash
27
27
  scripts/query-lifecycle-context --mode route --stage stage.plan --work-unit work-unit.plan-requirements --include execution_efficiency
28
28
  ```
29
29
 
30
- `--include` 接受合同顶层键,非法键或 ID 失败。按需读 `references/orchestration.md` 及索引,证据充分即停止加载。
30
+ `--include` 接受合同顶层键,非法输入失败。详见 `references/orchestration.md`。
31
31
 
32
32
  按 `execution_efficiency` 合并查询、复用未变资料;仍核验当前资产、词汇和门禁,产品流转不重跑模板套件。
33
33
 
@@ -71,27 +71,21 @@ Plan 入口读取 `.template-spec/plan/README.md` 和 `.template-spec/process/pl
71
71
  3. 原生工作单元由主控持有正式资产;专项按结构化任务包派发,只调用合同允许的 model-invoked skill。
72
72
  4. `to-spec`、`to-tickets`、`implement` 等兼容入口只可由用户显式调用。主控预检输入、路径和门禁;入口按 `matt_invocation_boundary` 写入其产物并回交验收,不批准或改变 Ticket 就绪状态。原生资产仍归主控。
73
73
  5. 先逐项验收用户目标、遗漏、错误假设和未决问题,再验收 `Workflow Execution Result`:工作单元、合同、允许写路径、`context_reconciliation`、证据、实际验证、延期 seam、漂移和下一路由必须可核验。实现派发额外绑定当前 Slice Implementation Contract v3;其他阶段不得伪造该合同。
74
- 6. 在授权边界内继续下一工作单元;遇到人工决定、新授权、证据冲突、专项失败或合并/发布结论时暂停。暂停不得阻断无依赖的独立工作。
74
+ 6. 在授权边界内继续下一工作单元;遇到人工决定、新授权、证据冲突、专项失败或合并/发布结论时暂停。暂停不得阻断独立工作。
75
75
 
76
76
  每个 `project-instance` 工作单元批准或流转前,回写稳定术语到唯一根 `CONTEXT.md`,并生成通过校验的 `context_reconciliation`。候选术语、错误路径、摘要漂移或未决冲突必须阻断。质量标准只由 `engineering-baseline` 定义一次;高风险影响按裁剪合同补充反证和残余风险。
77
77
 
78
78
  ## 面向业务角色
79
79
 
80
- 使用业务语言推进:
81
-
82
- `机会与目标 → 业务故事 → 责任与交接 → 规则、例子与疑问 → 可验收需求 → 页面验证 → 业务任务 → 交接研发`
83
-
84
80
  澄清目标、流程、责任、规则、范围和验收例子。业务总览引用权威资产,展示状态、未决项、责任人和下一步,不复制正文或新增门禁。
85
81
 
86
82
  Plan → Spec(含正式草稿、恢复与显式 `to-spec`)写入前,按 `.template-spec/plan/entry-review.md` 持久化审阅包并运行 `node scripts/verify-plan-spec-entry <state.yaml>`。检查默认 pending,缺项、过期或无真实回复即阻断;独立调研可继续。
87
83
 
88
84
  ## 结果与暂停
89
85
 
90
- 咨询直接回答;工作单元留证并展示当前决定。
91
-
92
- 结果包含:模式、仓库身份、阶段/工作单元、影响面、资产/门禁/Ticket/Slice/合同状态、证据及新鲜度、`ready-for-agent`、阻塞、本轮动作、下一单元、暂停/继续理由、Ticket 同步和 Git checkpoint。
86
+ 结构化结果保留模式、仓库身份、阶段/工作单元、影响面、资产/门禁/Ticket/Slice/合同状态、证据新鲜度、就绪、阻塞、本轮动作、下一单元、暂停理由、Ticket 同步和 Git checkpoint;用户说明按中文写作规范呈现。
93
87
 
94
- 暂停会签时追加门禁 ID、`role_id`、`runtime_id`、会签文件路径、推荐答案和恢复动作;恢复前运行适用验证器。只有同一候选快照通过全部适用审查轴与 fresh verification,才能提出合并、发布或完成结论;发布仍须生物人决定。
88
+ 会签记录保留门禁 ID、`role_id`、`runtime_id`、文件路径、推荐答案和恢复动作;用户提示解释待决定事项与责任方。恢复前运行适用验证器。同一候选通过适用审查与 fresh verification 才可提出完成、合并或发布结论;发布仍须生物人决定。
95
89
 
96
90
  Plan / Spec / Design 追踪按 `.template-spec/process/stage-tracking.md`。
97
91
 
@@ -102,3 +96,5 @@ Spec 起草业务票,Design 校准后正式化;无设计影响直接正式
102
96
  ## 功能资产整理
103
97
 
104
98
  整理与旧票迁移先读 `.template-spec/process/feature-assets.md`,只按显式候选计划处理,保留状态、批准依据和原文;不得推进阶段。临时输入与草稿放临时工作区,引用后按正式资产保留。
99
+
100
+ 既有 YSS 实例的模板版本升级、旧治理布局迁移或升级恢复路由到 `yss-harness-upgrade`,按 `.template-spec/process/harness-upgrade.md` 执行;不由升级动作推进产品阶段或重写历史批准。
@@ -38,6 +38,9 @@ Plan 成功标准在既有 `success_criteria[].statement` 细化指标定义、
38
38
 
39
39
  ## 控制面引用
40
40
 
41
+ - 新建领域战略、阶段决策包使用 JSON;存量 YAML 保持只读兼容,显式迁移见 `.template-spec/process/structured-assets.md`。使用 `scripts/contract plan-write/apply-write` 结构化落盘,不对合同作字符串拼接。校验器必须执行权威 Schema 后再检查跨文件语义。
42
+ - 复用 Plan 原始决定编号,不自行重排;批准前用 `scripts/check-plan-stage-coverage` 生成绑定 Plan、决定记录和阶段包摘要的逐项对照证据,非目标使用显式映射。无法提取、缺项或映射歧义必须补齐;语义等价仍由独立评审确认,覆盖检查不构成批准。
43
+
41
44
  - 用户决定遵循 [用户决定协议](../yss-product-lifecycle/references/user-decisions.md):决定必须绑定资产、版本、范围和证据;范围、风险、摘要或失效条件变化后重新确认。
42
45
  - checkpoint 是唯一机器状态源,业务 Ticket 只保存追踪引用,不复制阶段状态。
43
46
  - 每次批准、重发和消费者流转前执行 `context_reconciliation`,核验 `CONTEXT.md` 的全文与引用术语双摘要。
@@ -1,10 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import { readFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { assertAssetTransactionIdle, assertCurrentAssetReference } from "../../../../scripts/lib/asset-transactions.mjs";
3
5
  import process from "node:process";
4
6
  import { parseArgs } from "node:util";
5
7
  import { parseDocument } from "../../../../scripts/vendor/yaml.mjs";
6
8
  import { loadApprovalRecord, resolveApprovalRef, validateApprovalRecord } from "../../../../scripts/lib/approval-record.mjs";
7
9
  import { verifyContextSnapshot } from "../../../../scripts/lib/context-contract.mjs";
10
+ import { parseAsset, validateAssetStructure } from '../../../../scripts/lib/structured-assets.mjs';
8
11
 
9
12
  import { extractTraceability } from "../../../../scripts/lib/strategic-handoff-rules.mjs";
10
13
 
@@ -49,6 +52,7 @@ function requireArray(object, field, path, errors, min = 0, itemKind = "string")
49
52
  }
50
53
 
51
54
  function validate(data, contextRoot) {
55
+ validateAssetStructure(data, 'domain-strategy');
52
56
  const errors = [];
53
57
  if (data?.traceability_version !== undefined) { try { extractTraceability(data); } catch(error) { errors.push(error.message); } }
54
58
  if (!data || typeof data !== "object" || Array.isArray(data)) return ["合同必须是对象"];
@@ -210,11 +214,11 @@ const file = positionals[0];
210
214
  if (!file) fail(["用法: validate-domain-strategy.mjs <contract.yaml> [--root <project-root>]"]);
211
215
  else {
212
216
  try {
217
+ assertAssetTransactionIdle(values.root);
218
+ assertCurrentAssetReference(values.root, path.relative(path.resolve(values.root), path.resolve(file)));
213
219
  const source = await readFile(file, "utf8");
214
- const document = parseDocument(source, { maxAliasCount: 0, uniqueKeys: true });
215
- if (document.errors.length) fail([document.errors[0].message]);
216
- else {
217
- const errors = validate(document.toJS({ maxAliasCount: 0 }), values.root);
220
+ {
221
+ const errors = validate(parseAsset(source, file), values.root);
218
222
  if (errors.length) fail(errors);
219
223
  else process.stdout.write(JSON.stringify({ result: "completed", contract: file }, null, 2) + "\n");
220
224
  }
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { readFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { assertAssetTransactionIdle, assertCurrentAssetReference } from "../../../../scripts/lib/asset-transactions.mjs";
3
5
  import { createHash } from "node:crypto";
4
6
  import { resolve } from "node:path";
5
7
  import process from "node:process";
@@ -7,6 +9,7 @@ import { parseArgs } from "node:util";
7
9
  import { parseDocument } from "../../../../scripts/vendor/yaml.mjs";
8
10
  import { loadApprovalRecord, resolveApprovalRef, validateApprovalRecord } from "../../../../scripts/lib/approval-record.mjs";
9
11
  import { verifyContextSnapshot } from "../../../../scripts/lib/context-contract.mjs";
12
+ import { parseAsset, validateAssetStructure } from '../../../../scripts/lib/structured-assets.mjs';
10
13
 
11
14
  const required = ["schema_version", "stage_decision_id", "package_version", "status", "problem_statement", "target_users", "mvp", "non_goals", "success_criteria", "test_seams", "confirmed_decisions", "assumptions", "constraints", "unresolved_items", "context_snapshot", "domain_strategy_ref", "impact_assessment", "downstream_mapping", "evidence_refs", "approval"];
12
15
  const idPattern = /^stage-decision\.[a-z0-9][a-z0-9-]*$/;
@@ -25,6 +28,7 @@ function canonical(value) { if (Array.isArray(value)) return value.map(canonical
25
28
  function digest(value) { return `sha256:${createHash("sha256").update(JSON.stringify(canonical(value))).digest("hex")}`; }
26
29
 
27
30
  async function validate(data, contextRoot) {
31
+ validateAssetStructure(data, 'stage-decision-package');
28
32
  const errors = [];
29
33
  if (!data || typeof data !== "object" || Array.isArray(data)) return ["合同必须是对象"];
30
34
  if (data.schema_version === 1) return ["migration-required: stage decision package v1 必须迁移到 v2 context_snapshot"];
@@ -143,11 +147,11 @@ const file = positionals[0];
143
147
  if (!file) fail(["用法: validate-stage-decision-package.mjs <package.yaml> [--root <project-root>]"]);
144
148
  else {
145
149
  try {
150
+ assertAssetTransactionIdle(values.root);
151
+ assertCurrentAssetReference(values.root, path.relative(path.resolve(values.root), path.resolve(file)));
146
152
  const source = await readFile(file, "utf8");
147
- const document = parseDocument(source, { maxAliasCount: 0, uniqueKeys: true });
148
- if (document.errors.length) fail([document.errors[0].message]);
149
- else {
150
- const errors = await validate(document.toJS({ maxAliasCount: 0 }), values.root);
153
+ {
154
+ const errors = await validate(parseAsset(source, file), values.root);
151
155
  if (errors.length) fail(errors);
152
156
  else process.stdout.write(JSON.stringify({ result: "completed", contract: file }, null, 2) + "\n");
153
157
  }
@@ -200,6 +200,18 @@ try {
200
200
  await writeFile(packageFile, packageSource);
201
201
  const packagePass = run(packageValidator, packageFile, temporaryRoot);
202
202
  if (packagePass.status !== 0) throw new Error(`valid v3 stage decision package should pass: ${packagePass.stderr}`);
203
+ for (const format of ['yaml', 'json']) {
204
+ for (const [name, mutate, pattern] of [
205
+ ['record-note', p => {p.test_seams[0].note='undeclared';}, /Additional properties/],
206
+ ['mapping-note', p => {p.downstream_mapping[0].note='undeclared';}, /Additional properties/],
207
+ ['duplicate-source', p => p.test_seams[0].source_refs.push(p.test_seams[0].source_refs[0]), /non-unique/]
208
+ ]) {
209
+ const value = parseYaml(packageSource); mutate(value);
210
+ const file = join(temporaryRoot, `${name}.${format}`);
211
+ await writeFile(file, JSON.stringify(value, null, 2));
212
+ expectBlocked(run(packageValidator, file, temporaryRoot), pattern, `${name} ${format}`);
213
+ }
214
+ }
203
215
 
204
216
  const bundleFile = join(temporaryRoot, "plan-review-bundle.json");
205
217
  const bundledDomainValue = parseYaml(domainSource);
@@ -265,7 +277,7 @@ try {
265
277
  const wrongArrayValue = parseYaml(packageSource);
266
278
  wrongArrayValue.target_users = [42];
267
279
  await writeFile(packageWrongArray, JSON.stringify(wrongArrayValue, null, 2));
268
- expectBlocked(run(packageValidator, packageWrongArray, temporaryRoot), /target_users\[0\]/, "package non-string array item");
280
+ expectBlocked(run(packageValidator, packageWrongArray, temporaryRoot), /target_users(?:\[0\]|\.0)/, "package non-string array item");
269
281
  } finally {
270
282
  await rm(temporaryRoot, { recursive: true, force: true });
271
283
  }
@@ -0,0 +1,26 @@
1
+ ---
2
+ name: yss-harness-upgrade
3
+ description: 升级既有 YSS 模板实例,规划同家族版本迁移、定制合并、持久归档、验证与恢复;用于 create-yss-spec 及 design/backend/frontend CLI 实例升级,不处理业务代码或数据库迁移。
4
+ ---
5
+
6
+ # YSS 实例升级
7
+
8
+ 先读取根 `yss-project.yaml`、`CONTEXT.md` 和 [统一协议](../../../.template-spec/process/harness-upgrade.md)。目标必须是已登记的同家族 `project-instance`;身份/schema 不支持时只诊断,不能重新 init 或猜测身份。模板源的技能维护由 `maintaining-skills` 处理。
9
+
10
+ ## 工作顺序
11
+
12
+ 1. 确认目标绝对路径、用户授权范围、实际 CLI 路径及版本、模板 metadata、工作区已有改动。依据 [家族适配](references/cli-families.md) 选定 CLI。未指定目标版本时查询同家族稳定版本并固定精确包版本和摘要,执行期间不再引用 `latest`。CLI 程序升级和项目实例迁移分别记录,不自动安装全局版本。
13
+ 2. 用固定 CLI 的 `migrate status` 检查中断事务。有中断时先 `migrate recover` 预览,按既有授权执行 `--apply`,然后重新规划。不得删除状态目录绕过恢复。
14
+ 3. `migrate plan --target-dir <绝对路径> --output <项目外全新计划.json>`。旧治理布局迁移显式附加 `--migrate-layout`;退出分发文件清理显式附加 `--prune`。计划阶段不修改项目;检查计划中的版本、变化路径、保留项和归档位置。
15
+ 4. 冲突时在项目外起草候选差异和 `--resolutions` 文件。只使用计划报告的可信受管冲突;`preserve` 保留原文,`merge` 提供候选内容并绑定原文件及新模板摘要。向用户展示新的语义取舍,复用已有范围授权;未获授权的新决定保持等待。不能使用 `--force` 批量覆盖,不能先修改项目来消除冲突。
16
+ 5. 用相同固定 CLI 执行 `migrate apply --plan <计划>`。执行器重新生成候选并验证计划、归档、身份和输入摘要;`STALE_PLAN` 时重新规划,不能手改摘要续跑。迁移不自动 commit、stash、reset、push 或发布。
17
+ 6. 查看回执和实例验证结果,执行相同选项的新计划验证无重复迁移。核对业务文件与既有工作区改动保持,说明保留的定制和现有失败。输出实际版本、归档/回执位置、验证证据和剩余问题;结构检查不能代替真实 CLI 或 Agent 行为验证。
18
+
19
+ ## 恢复与边界
20
+
21
+ - `migrate recover` 恢复未完成事务;`migrate rollback` 预览最近一次成功迁移的整体回退。两者仅加 `--apply` 才写入。回退遇到后续修改或损坏归档必须保留现场,不覆盖新工作。
22
+ - Spec、Ticket、合同、人工回复、批准和正式验证默认保留。路径迁移不产生新批准;语义或批准绑定变化交回原生命周期,不补造旧状态。
23
+ - 不执行跨家族转换、任意历史版本跳转、业务重构或数据库迁移。未知基线不能由 Agent 猜测;归档不能作为扩大写入授权的理由。
24
+ - 旧实例缺少本技能时,从已固定 CLI 包的 `resources/upgrade-skill/` 在项目外读取入口及相邻协议。不得先同步目标项目来安装升级技能。
25
+
26
+ 本技能只编排,不复制迁移执行器。研究新技术事实时用 `yss-research`;结论输出按 `lifecycle-document-output` 使用 `i-have-adhd`。不批准 Slice、不设置 `ready-for-agent`、不宣称可发布。
@@ -0,0 +1,14 @@
1
+ # 同家族升级适配
2
+
3
+ | CLI | 实例 metadata |
4
+ |---|---|
5
+ | create-yss-spec | .yss-template.json |
6
+ | create-yss-harness-design | .yss-harness-design.json |
7
+ | create-yss-harness-backend | .yss-harness-backend.json |
8
+ | create-yss-harness-frontend | .yss-harness-frontend.json |
9
+
10
+ 身份还需通过各 CLI 原有 profile/schema 检查。多个 metadata、旧 dev 家族、未知 schema、符号链接或矛盾 profile 均不能自动接管。`migrate` 不是降级或跨家族工具。
11
+
12
+ 四类 CLI 的新入口一致:`migrate plan|apply|status|recover|rollback`,支持 `--json`。`migrate plan` 只向显式指定的项目外新文件输出;`apply --plan <文件>` 显式写入。recover/rollback 默认只读,写入需 `--apply`。不要从旧 `sync` 推断新接口:主 CLI 普通 sync 会写入,专职 CLI sync 默认预览。
13
+
14
+ 目标模板来自当前固定 CLI 包,不接受计划中的下载地址或脚本。版本、模板和执行器摘要不匹配时更换为生成计划的原包,或重新规划;不得修改计划来绕过。已有 `update/upgrade` 仅更新 CLI 程序,既有 `recover` 仍只恢复原事务。
@@ -114,6 +114,7 @@
114
114
  | `yss-implementation-contract-compiler` | `i-have-adhd` | `context-conditional` | `lifecycle-document-output` |
115
115
  | `yss-openapi-governance` | `i-have-adhd` | `context-conditional` | `lifecycle-document-output` |
116
116
  | `yss-stage-decision` | `i-have-adhd` | `context-conditional` | `lifecycle-document-output` |
117
+ | `yss-harness-upgrade` | `i-have-adhd` | `context-conditional` | `lifecycle-document-output` |
117
118
  | `yss-research` | `i-have-adhd` | `context-conditional` | `lifecycle-document-output` |
118
119
  | `yss-product-lifecycle` | `i-have-adhd` | `context-conditional` | `lifecycle-document-output` |
119
120
  | `code-review` | `i-have-adhd` | `context-conditional` | `lifecycle-document-output` |
@@ -7,27 +7,27 @@ description: 编排 YSS 研发全生命周期;当阶段、产物、门禁或 S
7
7
 
8
8
  合同阅读与托管更新:`scripts/contract view|render|check-views`;见 `.template-spec/process/contract-reading.md`。
9
9
 
10
- 文档按 `document_writing` 条件调用 `i-have-adhd`,并传递 `.template-spec/process/document-writing.md` 规范。
10
+ 五类机器资产默认 JSON;读写、迁移及恢复须遵循 `.template-spec/process/structured-assets.md`。
11
11
 
12
- ## 事实源与按需加载
12
+ 文档及进度按 `document_writing` 调用 `i-have-adhd`,遵循 `.template-spec/process/document-writing.md`;正文用中文名称与动作,编号按需追踪。
13
13
 
14
- 先读 `yss-project.yaml`、`AGENTS.md`、`CONTEXT.md` 和当前任务视图,按需加载合同子树:
14
+ ## 事实源
15
+
16
+ 先读 `yss-project.yaml`、`AGENTS.md`、`CONTEXT.md` 和当前任务视图,按需读合同:
15
17
 
16
18
  | 事实 | 权威来源 |
17
19
  |---|---|
18
- | 阶段、聚合门禁、内部检查、产物、工作单元、证据和稳定 ID | `.template-spec/process/lifecycle-registry.yaml` |
19
- | 执行模式、流转、readiness、暂停、结果和授权语义 | `references/orchestration-contract.yaml` |
20
- | Skill 身份、capability、依赖和路由 | `.template-spec/agents/yss-skill-registry.yaml` |
20
+ | 阶段、门禁、产物、工作单元、证据及 ID | `.template-spec/process/lifecycle-registry.yaml` |
21
+ | 执行、流转和授权合同 | `references/orchestration-contract.yaml` |
22
+ | Skill 身份、能力、依赖和路由 | `.template-spec/agents/yss-skill-registry.yaml` |
21
23
  | 数字人角色、运行时和会签策略 | `.template-spec/agents/digital-human-roles.yaml` |
22
- | 影响面、裁剪和模板维护强度 | `.template-spec/process/harness-process-tailoring.md`、`.template-source/process/maintenance-intensity.yaml` |
23
-
24
- 查询合同:
24
+ | 影响面、裁剪与维护强度 | `.template-spec/process/harness-process-tailoring.md`、`.template-source/process/maintenance-intensity.yaml` |
25
25
 
26
26
  ```bash
27
27
  scripts/query-lifecycle-context --mode route --stage stage.plan --work-unit work-unit.plan-requirements --include execution_efficiency
28
28
  ```
29
29
 
30
- `--include` 接受合同顶层键,非法键或 ID 失败。按需读 `references/orchestration.md` 及索引,证据充分即停止加载。
30
+ `--include` 接受合同顶层键,非法输入失败。详见 `references/orchestration.md`。
31
31
 
32
32
  按 `execution_efficiency` 合并查询、复用未变资料;仍核验当前资产、词汇和门禁,产品流转不重跑模板套件。
33
33
 
@@ -71,27 +71,21 @@ Plan 入口读取 `.template-spec/plan/README.md` 和 `.template-spec/process/pl
71
71
  3. 原生工作单元由主控持有正式资产;专项按结构化任务包派发,只调用合同允许的 model-invoked skill。
72
72
  4. `to-spec`、`to-tickets`、`implement` 等兼容入口只可由用户显式调用。主控预检输入、路径和门禁;入口按 `matt_invocation_boundary` 写入其产物并回交验收,不批准或改变 Ticket 就绪状态。原生资产仍归主控。
73
73
  5. 先逐项验收用户目标、遗漏、错误假设和未决问题,再验收 `Workflow Execution Result`:工作单元、合同、允许写路径、`context_reconciliation`、证据、实际验证、延期 seam、漂移和下一路由必须可核验。实现派发额外绑定当前 Slice Implementation Contract v3;其他阶段不得伪造该合同。
74
- 6. 在授权边界内继续下一工作单元;遇到人工决定、新授权、证据冲突、专项失败或合并/发布结论时暂停。暂停不得阻断无依赖的独立工作。
74
+ 6. 在授权边界内继续下一工作单元;遇到人工决定、新授权、证据冲突、专项失败或合并/发布结论时暂停。暂停不得阻断独立工作。
75
75
 
76
76
  每个 `project-instance` 工作单元批准或流转前,回写稳定术语到唯一根 `CONTEXT.md`,并生成通过校验的 `context_reconciliation`。候选术语、错误路径、摘要漂移或未决冲突必须阻断。质量标准只由 `engineering-baseline` 定义一次;高风险影响按裁剪合同补充反证和残余风险。
77
77
 
78
78
  ## 面向业务角色
79
79
 
80
- 使用业务语言推进:
81
-
82
- `机会与目标 → 业务故事 → 责任与交接 → 规则、例子与疑问 → 可验收需求 → 页面验证 → 业务任务 → 交接研发`
83
-
84
80
  澄清目标、流程、责任、规则、范围和验收例子。业务总览引用权威资产,展示状态、未决项、责任人和下一步,不复制正文或新增门禁。
85
81
 
86
82
  Plan → Spec(含正式草稿、恢复与显式 `to-spec`)写入前,按 `.template-spec/plan/entry-review.md` 持久化审阅包并运行 `node scripts/verify-plan-spec-entry <state.yaml>`。检查默认 pending,缺项、过期或无真实回复即阻断;独立调研可继续。
87
83
 
88
84
  ## 结果与暂停
89
85
 
90
- 咨询直接回答;工作单元留证并展示当前决定。
91
-
92
- 结果包含:模式、仓库身份、阶段/工作单元、影响面、资产/门禁/Ticket/Slice/合同状态、证据及新鲜度、`ready-for-agent`、阻塞、本轮动作、下一单元、暂停/继续理由、Ticket 同步和 Git checkpoint。
86
+ 结构化结果保留模式、仓库身份、阶段/工作单元、影响面、资产/门禁/Ticket/Slice/合同状态、证据新鲜度、就绪、阻塞、本轮动作、下一单元、暂停理由、Ticket 同步和 Git checkpoint;用户说明按中文写作规范呈现。
93
87
 
94
- 暂停会签时追加门禁 ID、`role_id`、`runtime_id`、会签文件路径、推荐答案和恢复动作;恢复前运行适用验证器。只有同一候选快照通过全部适用审查轴与 fresh verification,才能提出合并、发布或完成结论;发布仍须生物人决定。
88
+ 会签记录保留门禁 ID、`role_id`、`runtime_id`、文件路径、推荐答案和恢复动作;用户提示解释待决定事项与责任方。恢复前运行适用验证器。同一候选通过适用审查与 fresh verification 才可提出完成、合并或发布结论;发布仍须生物人决定。
95
89
 
96
90
  Plan / Spec / Design 追踪按 `.template-spec/process/stage-tracking.md`。
97
91
 
@@ -102,3 +96,5 @@ Spec 起草业务票,Design 校准后正式化;无设计影响直接正式
102
96
  ## 功能资产整理
103
97
 
104
98
  整理与旧票迁移先读 `.template-spec/process/feature-assets.md`,只按显式候选计划处理,保留状态、批准依据和原文;不得推进阶段。临时输入与草稿放临时工作区,引用后按正式资产保留。
99
+
100
+ 既有 YSS 实例的模板版本升级、旧治理布局迁移或升级恢复路由到 `yss-harness-upgrade`,按 `.template-spec/process/harness-upgrade.md` 执行;不由升级动作推进产品阶段或重写历史批准。
@@ -38,6 +38,9 @@ Plan 成功标准在既有 `success_criteria[].statement` 细化指标定义、
38
38
 
39
39
  ## 控制面引用
40
40
 
41
+ - 新建领域战略、阶段决策包使用 JSON;存量 YAML 保持只读兼容,显式迁移见 `.template-spec/process/structured-assets.md`。使用 `scripts/contract plan-write/apply-write` 结构化落盘,不对合同作字符串拼接。校验器必须执行权威 Schema 后再检查跨文件语义。
42
+ - 复用 Plan 原始决定编号,不自行重排;批准前用 `scripts/check-plan-stage-coverage` 生成绑定 Plan、决定记录和阶段包摘要的逐项对照证据,非目标使用显式映射。无法提取、缺项或映射歧义必须补齐;语义等价仍由独立评审确认,覆盖检查不构成批准。
43
+
41
44
  - 用户决定遵循 [用户决定协议](../yss-product-lifecycle/references/user-decisions.md):决定必须绑定资产、版本、范围和证据;范围、风险、摘要或失效条件变化后重新确认。
42
45
  - checkpoint 是唯一机器状态源,业务 Ticket 只保存追踪引用,不复制阶段状态。
43
46
  - 每次批准、重发和消费者流转前执行 `context_reconciliation`,核验 `CONTEXT.md` 的全文与引用术语双摘要。
@@ -1,10 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import { readFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { assertAssetTransactionIdle, assertCurrentAssetReference } from "../../../../scripts/lib/asset-transactions.mjs";
3
5
  import process from "node:process";
4
6
  import { parseArgs } from "node:util";
5
7
  import { parseDocument } from "../../../../scripts/vendor/yaml.mjs";
6
8
  import { loadApprovalRecord, resolveApprovalRef, validateApprovalRecord } from "../../../../scripts/lib/approval-record.mjs";
7
9
  import { verifyContextSnapshot } from "../../../../scripts/lib/context-contract.mjs";
10
+ import { parseAsset, validateAssetStructure } from '../../../../scripts/lib/structured-assets.mjs';
8
11
 
9
12
  import { extractTraceability } from "../../../../scripts/lib/strategic-handoff-rules.mjs";
10
13
 
@@ -49,6 +52,7 @@ function requireArray(object, field, path, errors, min = 0, itemKind = "string")
49
52
  }
50
53
 
51
54
  function validate(data, contextRoot) {
55
+ validateAssetStructure(data, 'domain-strategy');
52
56
  const errors = [];
53
57
  if (data?.traceability_version !== undefined) { try { extractTraceability(data); } catch(error) { errors.push(error.message); } }
54
58
  if (!data || typeof data !== "object" || Array.isArray(data)) return ["合同必须是对象"];
@@ -210,11 +214,11 @@ const file = positionals[0];
210
214
  if (!file) fail(["用法: validate-domain-strategy.mjs <contract.yaml> [--root <project-root>]"]);
211
215
  else {
212
216
  try {
217
+ assertAssetTransactionIdle(values.root);
218
+ assertCurrentAssetReference(values.root, path.relative(path.resolve(values.root), path.resolve(file)));
213
219
  const source = await readFile(file, "utf8");
214
- const document = parseDocument(source, { maxAliasCount: 0, uniqueKeys: true });
215
- if (document.errors.length) fail([document.errors[0].message]);
216
- else {
217
- const errors = validate(document.toJS({ maxAliasCount: 0 }), values.root);
220
+ {
221
+ const errors = validate(parseAsset(source, file), values.root);
218
222
  if (errors.length) fail(errors);
219
223
  else process.stdout.write(JSON.stringify({ result: "completed", contract: file }, null, 2) + "\n");
220
224
  }
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { readFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { assertAssetTransactionIdle, assertCurrentAssetReference } from "../../../../scripts/lib/asset-transactions.mjs";
3
5
  import { createHash } from "node:crypto";
4
6
  import { resolve } from "node:path";
5
7
  import process from "node:process";
@@ -7,6 +9,7 @@ import { parseArgs } from "node:util";
7
9
  import { parseDocument } from "../../../../scripts/vendor/yaml.mjs";
8
10
  import { loadApprovalRecord, resolveApprovalRef, validateApprovalRecord } from "../../../../scripts/lib/approval-record.mjs";
9
11
  import { verifyContextSnapshot } from "../../../../scripts/lib/context-contract.mjs";
12
+ import { parseAsset, validateAssetStructure } from '../../../../scripts/lib/structured-assets.mjs';
10
13
 
11
14
  const required = ["schema_version", "stage_decision_id", "package_version", "status", "problem_statement", "target_users", "mvp", "non_goals", "success_criteria", "test_seams", "confirmed_decisions", "assumptions", "constraints", "unresolved_items", "context_snapshot", "domain_strategy_ref", "impact_assessment", "downstream_mapping", "evidence_refs", "approval"];
12
15
  const idPattern = /^stage-decision\.[a-z0-9][a-z0-9-]*$/;
@@ -25,6 +28,7 @@ function canonical(value) { if (Array.isArray(value)) return value.map(canonical
25
28
  function digest(value) { return `sha256:${createHash("sha256").update(JSON.stringify(canonical(value))).digest("hex")}`; }
26
29
 
27
30
  async function validate(data, contextRoot) {
31
+ validateAssetStructure(data, 'stage-decision-package');
28
32
  const errors = [];
29
33
  if (!data || typeof data !== "object" || Array.isArray(data)) return ["合同必须是对象"];
30
34
  if (data.schema_version === 1) return ["migration-required: stage decision package v1 必须迁移到 v2 context_snapshot"];
@@ -143,11 +147,11 @@ const file = positionals[0];
143
147
  if (!file) fail(["用法: validate-stage-decision-package.mjs <package.yaml> [--root <project-root>]"]);
144
148
  else {
145
149
  try {
150
+ assertAssetTransactionIdle(values.root);
151
+ assertCurrentAssetReference(values.root, path.relative(path.resolve(values.root), path.resolve(file)));
146
152
  const source = await readFile(file, "utf8");
147
- const document = parseDocument(source, { maxAliasCount: 0, uniqueKeys: true });
148
- if (document.errors.length) fail([document.errors[0].message]);
149
- else {
150
- const errors = await validate(document.toJS({ maxAliasCount: 0 }), values.root);
153
+ {
154
+ const errors = await validate(parseAsset(source, file), values.root);
151
155
  if (errors.length) fail(errors);
152
156
  else process.stdout.write(JSON.stringify({ result: "completed", contract: file }, null, 2) + "\n");
153
157
  }
@@ -200,6 +200,18 @@ try {
200
200
  await writeFile(packageFile, packageSource);
201
201
  const packagePass = run(packageValidator, packageFile, temporaryRoot);
202
202
  if (packagePass.status !== 0) throw new Error(`valid v3 stage decision package should pass: ${packagePass.stderr}`);
203
+ for (const format of ['yaml', 'json']) {
204
+ for (const [name, mutate, pattern] of [
205
+ ['record-note', p => {p.test_seams[0].note='undeclared';}, /Additional properties/],
206
+ ['mapping-note', p => {p.downstream_mapping[0].note='undeclared';}, /Additional properties/],
207
+ ['duplicate-source', p => p.test_seams[0].source_refs.push(p.test_seams[0].source_refs[0]), /non-unique/]
208
+ ]) {
209
+ const value = parseYaml(packageSource); mutate(value);
210
+ const file = join(temporaryRoot, `${name}.${format}`);
211
+ await writeFile(file, JSON.stringify(value, null, 2));
212
+ expectBlocked(run(packageValidator, file, temporaryRoot), pattern, `${name} ${format}`);
213
+ }
214
+ }
203
215
 
204
216
  const bundleFile = join(temporaryRoot, "plan-review-bundle.json");
205
217
  const bundledDomainValue = parseYaml(domainSource);
@@ -265,7 +277,7 @@ try {
265
277
  const wrongArrayValue = parseYaml(packageSource);
266
278
  wrongArrayValue.target_users = [42];
267
279
  await writeFile(packageWrongArray, JSON.stringify(wrongArrayValue, null, 2));
268
- expectBlocked(run(packageValidator, packageWrongArray, temporaryRoot), /target_users\[0\]/, "package non-string array item");
280
+ expectBlocked(run(packageValidator, packageWrongArray, temporaryRoot), /target_users(?:\[0\]|\.0)/, "package non-string array item");
269
281
  } finally {
270
282
  await rm(temporaryRoot, { recursive: true, force: true });
271
283
  }
@@ -0,0 +1,26 @@
1
+ ---
2
+ name: yss-harness-upgrade
3
+ description: 升级既有 YSS 模板实例,规划同家族版本迁移、定制合并、持久归档、验证与恢复;用于 create-yss-spec 及 design/backend/frontend CLI 实例升级,不处理业务代码或数据库迁移。
4
+ ---
5
+
6
+ # YSS 实例升级
7
+
8
+ 先读取根 `yss-project.yaml`、`CONTEXT.md` 和 [统一协议](../../../.template-spec/process/harness-upgrade.md)。目标必须是已登记的同家族 `project-instance`;身份/schema 不支持时只诊断,不能重新 init 或猜测身份。模板源的技能维护由 `maintaining-skills` 处理。
9
+
10
+ ## 工作顺序
11
+
12
+ 1. 确认目标绝对路径、用户授权范围、实际 CLI 路径及版本、模板 metadata、工作区已有改动。依据 [家族适配](references/cli-families.md) 选定 CLI。未指定目标版本时查询同家族稳定版本并固定精确包版本和摘要,执行期间不再引用 `latest`。CLI 程序升级和项目实例迁移分别记录,不自动安装全局版本。
13
+ 2. 用固定 CLI 的 `migrate status` 检查中断事务。有中断时先 `migrate recover` 预览,按既有授权执行 `--apply`,然后重新规划。不得删除状态目录绕过恢复。
14
+ 3. `migrate plan --target-dir <绝对路径> --output <项目外全新计划.json>`。旧治理布局迁移显式附加 `--migrate-layout`;退出分发文件清理显式附加 `--prune`。计划阶段不修改项目;检查计划中的版本、变化路径、保留项和归档位置。
15
+ 4. 冲突时在项目外起草候选差异和 `--resolutions` 文件。只使用计划报告的可信受管冲突;`preserve` 保留原文,`merge` 提供候选内容并绑定原文件及新模板摘要。向用户展示新的语义取舍,复用已有范围授权;未获授权的新决定保持等待。不能使用 `--force` 批量覆盖,不能先修改项目来消除冲突。
16
+ 5. 用相同固定 CLI 执行 `migrate apply --plan <计划>`。执行器重新生成候选并验证计划、归档、身份和输入摘要;`STALE_PLAN` 时重新规划,不能手改摘要续跑。迁移不自动 commit、stash、reset、push 或发布。
17
+ 6. 查看回执和实例验证结果,执行相同选项的新计划验证无重复迁移。核对业务文件与既有工作区改动保持,说明保留的定制和现有失败。输出实际版本、归档/回执位置、验证证据和剩余问题;结构检查不能代替真实 CLI 或 Agent 行为验证。
18
+
19
+ ## 恢复与边界
20
+
21
+ - `migrate recover` 恢复未完成事务;`migrate rollback` 预览最近一次成功迁移的整体回退。两者仅加 `--apply` 才写入。回退遇到后续修改或损坏归档必须保留现场,不覆盖新工作。
22
+ - Spec、Ticket、合同、人工回复、批准和正式验证默认保留。路径迁移不产生新批准;语义或批准绑定变化交回原生命周期,不补造旧状态。
23
+ - 不执行跨家族转换、任意历史版本跳转、业务重构或数据库迁移。未知基线不能由 Agent 猜测;归档不能作为扩大写入授权的理由。
24
+ - 旧实例缺少本技能时,从已固定 CLI 包的 `resources/upgrade-skill/` 在项目外读取入口及相邻协议。不得先同步目标项目来安装升级技能。
25
+
26
+ 本技能只编排,不复制迁移执行器。研究新技术事实时用 `yss-research`;结论输出按 `lifecycle-document-output` 使用 `i-have-adhd`。不批准 Slice、不设置 `ready-for-agent`、不宣称可发布。