create-yss-spec 3.3.0 → 3.3.1

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 (197) hide show
  1. package/package.json +3 -2
  2. package/src/command-runner.mjs +107 -0
  3. package/src/self-update.js +7 -4
  4. package/src/template/instance-runtime.js +1 -1
  5. package/template/.agents/skills/.strategic-design-skills-manifest.json +2 -2
  6. package/template/.agents/skills/competitive-intelligence/SKILL.md +4 -4
  7. package/template/.agents/skills/domain-modeling/CONTEXT-FORMAT.md +1 -1
  8. package/template/.agents/skills/grill-with-docs/SKILL.md +1 -1
  9. package/template/.agents/skills/setup-matt-pocock-skills/issue-tracker-local.md +1 -1
  10. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +7 -7
  11. package/template/.agents/skills/yss-frontend-scaffold-generator/SKILL.md +1 -1
  12. package/template/.agents/skills/yss-frontend-scaffold-generator/scripts/generate_scaffold.mjs +22 -8
  13. package/template/.agents/skills/yss-frontend-scaffold-generator/scripts/run_scaffold_verification.mjs +5 -6
  14. package/template/.agents/skills/yss-frontend-scaffold-generator/scripts/scaffold-generator.test.mjs +19 -1
  15. package/template/.agents/skills/yss-product-lifecycle/SKILL.md +7 -4
  16. package/template/.agents/skills/yss-product-lifecycle/references/artifact-dependencies.md +1 -1
  17. package/template/.agents/skills/yss-product-lifecycle/references/matt-yss-adapter.md +3 -3
  18. package/template/.agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml +57 -6
  19. package/template/.agents/skills/yss-product-lifecycle/references/orchestration.md +11 -1
  20. package/template/.agents/skills/yss-research/SKILL.md +1 -1
  21. package/template/.agents/skills/yss-stage-decision/SKILL.md +5 -3
  22. package/template/.agents/skills/yss-stage-decision/references/stage-decision-package-contract.md +1 -1
  23. package/template/.claude/skills/competitive-intelligence/SKILL.md +4 -4
  24. package/template/.claude/skills/domain-modeling/CONTEXT-FORMAT.md +1 -1
  25. package/template/.claude/skills/grill-with-docs/SKILL.md +1 -1
  26. package/template/.claude/skills/setup-matt-pocock-skills/issue-tracker-local.md +1 -1
  27. package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +7 -7
  28. package/template/.claude/skills/yss-frontend-scaffold-generator/SKILL.md +1 -1
  29. package/template/.claude/skills/yss-frontend-scaffold-generator/scripts/generate_scaffold.mjs +22 -8
  30. package/template/.claude/skills/yss-frontend-scaffold-generator/scripts/run_scaffold_verification.mjs +5 -6
  31. package/template/.claude/skills/yss-frontend-scaffold-generator/scripts/scaffold-generator.test.mjs +19 -1
  32. package/template/.claude/skills/yss-product-lifecycle/SKILL.md +7 -4
  33. package/template/.claude/skills/yss-product-lifecycle/references/artifact-dependencies.md +1 -1
  34. package/template/.claude/skills/yss-product-lifecycle/references/matt-yss-adapter.md +3 -3
  35. package/template/.claude/skills/yss-product-lifecycle/references/orchestration-contract.yaml +57 -6
  36. package/template/.claude/skills/yss-product-lifecycle/references/orchestration.md +11 -1
  37. package/template/.claude/skills/yss-research/SKILL.md +1 -1
  38. package/template/.claude/skills/yss-stage-decision/SKILL.md +5 -3
  39. package/template/.claude/skills/yss-stage-decision/references/stage-decision-package-contract.md +1 -1
  40. package/template/.codex/skills/competitive-intelligence/SKILL.md +4 -4
  41. package/template/.codex/skills/domain-modeling/CONTEXT-FORMAT.md +1 -1
  42. package/template/.codex/skills/grill-with-docs/SKILL.md +1 -1
  43. package/template/.codex/skills/setup-matt-pocock-skills/issue-tracker-local.md +1 -1
  44. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +7 -7
  45. package/template/.codex/skills/yss-frontend-scaffold-generator/SKILL.md +1 -1
  46. package/template/.codex/skills/yss-frontend-scaffold-generator/scripts/generate_scaffold.mjs +22 -8
  47. package/template/.codex/skills/yss-frontend-scaffold-generator/scripts/run_scaffold_verification.mjs +5 -6
  48. package/template/.codex/skills/yss-frontend-scaffold-generator/scripts/scaffold-generator.test.mjs +19 -1
  49. package/template/.codex/skills/yss-product-lifecycle/SKILL.md +7 -4
  50. package/template/.codex/skills/yss-product-lifecycle/references/artifact-dependencies.md +1 -1
  51. package/template/.codex/skills/yss-product-lifecycle/references/matt-yss-adapter.md +3 -3
  52. package/template/.codex/skills/yss-product-lifecycle/references/orchestration-contract.yaml +57 -6
  53. package/template/.codex/skills/yss-product-lifecycle/references/orchestration.md +11 -1
  54. package/template/.codex/skills/yss-research/SKILL.md +1 -1
  55. package/template/.codex/skills/yss-stage-decision/SKILL.md +5 -3
  56. package/template/.codex/skills/yss-stage-decision/references/stage-decision-package-contract.md +1 -1
  57. package/template/.cursor/skills/competitive-intelligence/SKILL.md +4 -4
  58. package/template/.cursor/skills/domain-modeling/CONTEXT-FORMAT.md +1 -1
  59. package/template/.cursor/skills/grill-with-docs/SKILL.md +1 -1
  60. package/template/.cursor/skills/setup-matt-pocock-skills/issue-tracker-local.md +1 -1
  61. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +7 -7
  62. package/template/.cursor/skills/yss-frontend-scaffold-generator/SKILL.md +1 -1
  63. package/template/.cursor/skills/yss-frontend-scaffold-generator/scripts/generate_scaffold.mjs +22 -8
  64. package/template/.cursor/skills/yss-frontend-scaffold-generator/scripts/run_scaffold_verification.mjs +5 -6
  65. package/template/.cursor/skills/yss-frontend-scaffold-generator/scripts/scaffold-generator.test.mjs +19 -1
  66. package/template/.cursor/skills/yss-product-lifecycle/SKILL.md +7 -4
  67. package/template/.cursor/skills/yss-product-lifecycle/references/artifact-dependencies.md +1 -1
  68. package/template/.cursor/skills/yss-product-lifecycle/references/matt-yss-adapter.md +3 -3
  69. package/template/.cursor/skills/yss-product-lifecycle/references/orchestration-contract.yaml +57 -6
  70. package/template/.cursor/skills/yss-product-lifecycle/references/orchestration.md +11 -1
  71. package/template/.cursor/skills/yss-research/SKILL.md +1 -1
  72. package/template/.cursor/skills/yss-stage-decision/SKILL.md +5 -3
  73. package/template/.cursor/skills/yss-stage-decision/references/stage-decision-package-contract.md +1 -1
  74. package/template/.pi/skills/competitive-intelligence/SKILL.md +4 -4
  75. package/template/.pi/skills/domain-modeling/CONTEXT-FORMAT.md +1 -1
  76. package/template/.pi/skills/grill-with-docs/SKILL.md +1 -1
  77. package/template/.pi/skills/setup-matt-pocock-skills/issue-tracker-local.md +1 -1
  78. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +7 -7
  79. package/template/.pi/skills/yss-frontend-scaffold-generator/SKILL.md +1 -1
  80. package/template/.pi/skills/yss-frontend-scaffold-generator/scripts/generate_scaffold.mjs +22 -8
  81. package/template/.pi/skills/yss-frontend-scaffold-generator/scripts/run_scaffold_verification.mjs +5 -6
  82. package/template/.pi/skills/yss-frontend-scaffold-generator/scripts/scaffold-generator.test.mjs +19 -1
  83. package/template/.pi/skills/yss-product-lifecycle/SKILL.md +7 -4
  84. package/template/.pi/skills/yss-product-lifecycle/references/artifact-dependencies.md +1 -1
  85. package/template/.pi/skills/yss-product-lifecycle/references/matt-yss-adapter.md +3 -3
  86. package/template/.pi/skills/yss-product-lifecycle/references/orchestration-contract.yaml +57 -6
  87. package/template/.pi/skills/yss-product-lifecycle/references/orchestration.md +11 -1
  88. package/template/.pi/skills/yss-research/SKILL.md +1 -1
  89. package/template/.pi/skills/yss-stage-decision/SKILL.md +5 -3
  90. package/template/.pi/skills/yss-stage-decision/references/stage-decision-package-contract.md +1 -1
  91. package/template/.qoder/skills/competitive-intelligence/SKILL.md +4 -4
  92. package/template/.qoder/skills/domain-modeling/CONTEXT-FORMAT.md +1 -1
  93. package/template/.qoder/skills/grill-with-docs/SKILL.md +1 -1
  94. package/template/.qoder/skills/setup-matt-pocock-skills/issue-tracker-local.md +1 -1
  95. package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +7 -7
  96. package/template/.qoder/skills/yss-frontend-scaffold-generator/SKILL.md +1 -1
  97. package/template/.qoder/skills/yss-frontend-scaffold-generator/scripts/generate_scaffold.mjs +22 -8
  98. package/template/.qoder/skills/yss-frontend-scaffold-generator/scripts/run_scaffold_verification.mjs +5 -6
  99. package/template/.qoder/skills/yss-frontend-scaffold-generator/scripts/scaffold-generator.test.mjs +19 -1
  100. package/template/.qoder/skills/yss-product-lifecycle/SKILL.md +7 -4
  101. package/template/.qoder/skills/yss-product-lifecycle/references/artifact-dependencies.md +1 -1
  102. package/template/.qoder/skills/yss-product-lifecycle/references/matt-yss-adapter.md +3 -3
  103. package/template/.qoder/skills/yss-product-lifecycle/references/orchestration-contract.yaml +57 -6
  104. package/template/.qoder/skills/yss-product-lifecycle/references/orchestration.md +11 -1
  105. package/template/.qoder/skills/yss-research/SKILL.md +1 -1
  106. package/template/.qoder/skills/yss-stage-decision/SKILL.md +5 -3
  107. package/template/.qoder/skills/yss-stage-decision/references/stage-decision-package-contract.md +1 -1
  108. package/template/.trae/skills/competitive-intelligence/SKILL.md +4 -4
  109. package/template/.trae/skills/domain-modeling/CONTEXT-FORMAT.md +1 -1
  110. package/template/.trae/skills/grill-with-docs/SKILL.md +1 -1
  111. package/template/.trae/skills/setup-matt-pocock-skills/issue-tracker-local.md +1 -1
  112. package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +7 -7
  113. package/template/.trae/skills/yss-frontend-scaffold-generator/SKILL.md +1 -1
  114. package/template/.trae/skills/yss-frontend-scaffold-generator/scripts/generate_scaffold.mjs +22 -8
  115. package/template/.trae/skills/yss-frontend-scaffold-generator/scripts/run_scaffold_verification.mjs +5 -6
  116. package/template/.trae/skills/yss-frontend-scaffold-generator/scripts/scaffold-generator.test.mjs +19 -1
  117. package/template/.trae/skills/yss-product-lifecycle/SKILL.md +7 -4
  118. package/template/.trae/skills/yss-product-lifecycle/references/artifact-dependencies.md +1 -1
  119. package/template/.trae/skills/yss-product-lifecycle/references/matt-yss-adapter.md +3 -3
  120. package/template/.trae/skills/yss-product-lifecycle/references/orchestration-contract.yaml +57 -6
  121. package/template/.trae/skills/yss-product-lifecycle/references/orchestration.md +11 -1
  122. package/template/.trae/skills/yss-research/SKILL.md +1 -1
  123. package/template/.trae/skills/yss-stage-decision/SKILL.md +5 -3
  124. package/template/.trae/skills/yss-stage-decision/references/stage-decision-package-contract.md +1 -1
  125. package/template/AGENTS.md +2 -2
  126. package/template/CONTEXT.md +3 -2
  127. package/template/README.md +3 -3
  128. package/template/docs/agents/digital-human-roles.md +1 -1
  129. package/template/docs/agents/digital-human-roles.yaml +11 -11
  130. package/template/docs/agents/issue-tracker.md +1 -1
  131. package/template/docs/agents/strategic-design-skills-integration.md +2 -0
  132. package/template/docs/architecture/README.md +1 -1
  133. package/template/docs/architecture/templates/business-architecture-template.md +1 -1
  134. package/template/docs/architecture/templates/functional-architecture-template.md +1 -1
  135. package/template/docs/design/README.md +1 -1
  136. package/template/docs/design/templates/interaction-spec-template.md +1 -1
  137. package/template/docs/design/templates/product-overview-design-template.md +1 -1
  138. package/template/docs/{discovery → plan}/IDEATION.md +5 -5
  139. package/template/docs/plan/README.md +9 -0
  140. package/template/docs/plan/entry-review.md +23 -0
  141. package/template/docs/plan/templates/plan-template.md +58 -0
  142. package/template/docs/process/harness-process-tailoring.md +1 -1
  143. package/template/docs/process/lifecycle-artifact-map.md +6 -6
  144. package/template/docs/process/lifecycle-registry-baseline.json +5 -1
  145. package/template/docs/process/lifecycle-registry.yaml +25 -20
  146. package/template/docs/process/plan-migration.md +13 -0
  147. package/template/docs/process/schemas/digital-human-task-package.schema.json +2 -0
  148. package/template/docs/process/schemas/lifecycle-checkpoint.schema.json +3 -0
  149. package/template/docs/process/schemas/lifecycle-registry.schema.json +1 -1
  150. package/template/docs/process/template-engineering-overview.md +2 -2
  151. package/template/docs/process/template-verification-profiles.yaml +2 -0
  152. package/template/docs/process/templates/context-reconciliation-template.yaml +2 -2
  153. package/template/docs/process/templates/lifecycle-checkpoint-template.yaml +1 -1
  154. package/template/docs/requirements/README.md +2 -2
  155. package/template/docs/templates/local-parent-ticket-template.md +1 -1
  156. package/template/docs/templates/subagent-task-package-template.md +1 -1
  157. package/template/docs/user-guide//347/224/250/346/210/267/346/211/213/345/206/214.md +1 -1
  158. package/template/scripts/cache-template-commit +2 -2
  159. package/template/scripts/fixtures/strategic-handoff/source-roles.json +7 -7
  160. package/template/scripts/fixtures/user-decision/plan-fixture.mjs +30 -0
  161. package/template/scripts/lib/backend-delivery.mjs +5 -2
  162. package/template/scripts/lib/command-runner.mjs +107 -0
  163. package/template/scripts/lib/context-reconciliation.mjs +34 -0
  164. package/template/scripts/lib/json-schema.mjs +42 -13
  165. package/template/scripts/lib/lifecycle-context-query.mjs +7 -3
  166. package/template/scripts/lib/lifecycle-transition.mjs +9 -3
  167. package/template/scripts/lib/plan-spec-entry.mjs +87 -0
  168. package/template/scripts/lib/scenario-checks.mjs +15 -12
  169. package/template/scripts/lib/skill-supply-chain.mjs +18 -2
  170. package/template/scripts/lib/strategic-handoff-io.mjs +5 -4
  171. package/template/scripts/lib/strategic-handoff.mjs +10 -9
  172. package/template/scripts/lib/task-package.mjs +2 -0
  173. package/template/scripts/lib/template-commit-cache.mjs +7 -5
  174. package/template/scripts/run-template-verification +9 -3
  175. package/template/scripts/sync-strategic-handoff-tools +9 -0
  176. package/template/scripts/verify-context-reconciliation +2 -48
  177. package/template/scripts/verify-context-reconciliation-scenarios +1 -1
  178. package/template/scripts/verify-lifecycle-checkpoint +2 -0
  179. package/template/scripts/verify-lifecycle-context-query-scenarios +28 -8
  180. package/template/scripts/verify-plan-spec-entry +11 -0
  181. package/template/scripts/verify-plan-spec-entry-scenarios +79 -0
  182. package/template/scripts/verify-script-performance-scenarios +56 -0
  183. package/template/scripts/verify-subagent-task-package-scenarios +9 -8
  184. package/template/scripts/verify-template-cache-scenarios +11 -1
  185. package/template/scripts/verify-user-decision-scenarios +4 -4
  186. package/template/skills-lock.json +17 -17
  187. package/template.manifest.json +2 -1
  188. package/template.snapshot.json +5 -5
  189. package/template/docs/discovery/README.md +0 -87
  190. package/template/docs/discovery/reports/.gitkeep +0 -0
  191. package/template/docs/discovery/reports/agent-governance-options.md +0 -71
  192. package/template/docs/discovery/reports/competitor-deep-dive/.gitkeep +0 -0
  193. package/template/docs/discovery/templates/discovery-template.md +0 -94
  194. /package/template/docs/{discovery → plan}/templates/competitive-analysis-template.md +0 -0
  195. /package/template/docs/{discovery → plan}/templates/competitive-matrix-template.md +0 -0
  196. /package/template/docs/{discovery → plan}/templates/market-analysis-template.md +0 -0
  197. /package/template/docs/{discovery → plan}/templates/user-pain-points-template.md +0 -0
@@ -1,56 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { existsSync, readFileSync } from "node:fs";
3
- import path from "node:path";
4
- import { spawnSync } from "node:child_process";
5
2
  import { parseArgs } from "node:util";
6
- import { fileURLToPath } from "node:url";
7
- import { parseDocument } from "./vendor/yaml.mjs";
8
- import { resolveContextTermRefs, verifyContextSnapshot } from "./lib/context-contract.mjs";
9
-
10
- const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
11
- const schemaPath = path.join(repoRoot, "docs/process/schemas/context-reconciliation.schema.json");
12
- const pythonValidator = String.raw`
13
- import json, sys
14
- from jsonschema import Draft202012Validator
15
- schema = json.load(open(sys.argv[1], encoding="utf-8"))
16
- value = json.load(sys.stdin)
17
- errors = sorted(Draft202012Validator(schema).iter_errors(value), key=lambda error: list(error.absolute_path))
18
- for error in errors:
19
- where = ".".join(str(part) for part in error.absolute_path) or "<root>"
20
- print(f"{where}: {error.message}", file=sys.stderr)
21
- sys.exit(1 if errors else 0)
22
- `;
23
-
24
- function parseFile(file) {
25
- const document = parseDocument(readFileSync(file, "utf8"), { maxAliasCount: 0, uniqueKeys: true });
26
- if (document.errors.length) throw new TypeError(document.errors[0].message);
27
- return document.toJS({ maxAliasCount: 0 });
28
- }
29
-
3
+ import { verifyContextReconciliation } from "./lib/context-reconciliation.mjs";
30
4
  try {
31
5
  const { values, positionals } = parseArgs({ options: { root: { type: "string", default: process.cwd() } }, allowPositionals: true, strict: true });
32
6
  if (positionals.length !== 1) throw new TypeError("用法: scripts/verify-context-reconciliation [--root <repo>] <record.yaml|json>");
33
- const root = path.resolve(values.root);
34
- const file = path.resolve(positionals[0]);
35
- const value = parseFile(file);
36
- const schemaResult = spawnSync("python3", ["-c", pythonValidator, schemaPath], { encoding: "utf8", input: JSON.stringify(value) });
37
- if (schemaResult.status !== 0) throw new TypeError(`${schemaResult.stdout}${schemaResult.stderr}`.trim());
38
- const manifest = parseFile(path.join(root, "yss-project.yaml"));
39
- if (value.repository_mode !== manifest.repository_mode) throw new TypeError("repository_mode 必须与根目录 yss-project.yaml 一致");
40
- const contract = verifyContextSnapshot(value.context_snapshot, { root });
41
- const changedRefs = [...value.changes.added, ...value.changes.updated, ...value.changes.deprecated];
42
- resolveContextTermRefs(contract, changedRefs);
43
- if (new Set(changedRefs).size !== changedRefs.length) throw new TypeError("changes 中的术语引用不得跨分类重复");
44
- if (value.repository_mode === "project-instance" && value.status !== "reconciled") throw new TypeError("project-instance 工作单元只有 status=reconciled 才允许批准或进入下一步");
45
- if (value.status === "reconciled" && value.unresolved_terms.length) throw new TypeError("reconciled 不得包含 unresolved_terms");
46
- if (value.status === "blocked" && value.unresolved_terms.length === 0) throw new TypeError("blocked 必须说明 unresolved_terms");
47
- if (value.status === "not-applicable" && (value.repository_mode !== "template-source" || !value.reason)) throw new TypeError("not-applicable 只适用于 template-source 且必须说明 reason");
48
- for (const ref of value.evidence_refs) {
49
- if (/^https?:\/\//.test(ref)) continue;
50
- const resolved = path.resolve(root, ref);
51
- if (!resolved.startsWith(`${root}${path.sep}`) && resolved !== root) throw new TypeError(`evidence_ref 越出仓库: ${ref}`);
52
- if (!existsSync(resolved)) throw new TypeError(`evidence_ref 不可读: ${ref}`);
53
- }
7
+ verifyContextReconciliation(positionals[0], { root: values.root });
54
8
  process.stdout.write("Context reconciliation 验证通过\n");
55
9
  } catch (error) {
56
10
  process.stderr.write(`Context reconciliation 验证失败: ${error.message}\n`);
@@ -40,7 +40,7 @@ function record(overrides = {}) {
40
40
  return {
41
41
  schema_version: 1,
42
42
  repository_mode: "project-instance",
43
- stage: "stage.discovery",
43
+ stage: "stage.plan",
44
44
  work_unit: "work-unit.domain-strategy-design",
45
45
  status: "reconciled",
46
46
  context_snapshot: snapshot,
@@ -6,6 +6,7 @@ import { parseArgs } from "node:util";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import { parseDocument } from "./vendor/yaml.mjs";
8
8
  import { assertCheckpointApprovals, assertCheckpointUserDecisions } from "./lib/approval-record.mjs";
9
+ import { assertPlanCheckpoint } from "./lib/plan-spec-entry.mjs";
9
10
 
10
11
  const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
11
12
  const schemaPath = path.join(root, "docs/process/schemas/lifecycle-checkpoint.schema.json");
@@ -32,6 +33,7 @@ try {
32
33
  const result = spawnSync("python3", ["-c", validator, schemaPath], { cwd: root, encoding: "utf8", input: JSON.stringify(value) });
33
34
  if (result.status !== 0) throw new TypeError(`${result.stdout}${result.stderr}`.trim());
34
35
  assertCheckpointApprovals(value, sourcePath);
36
+ assertPlanCheckpoint(value);
35
37
  assertCheckpointUserDecisions(value);
36
38
  if (value.repository_mode === "project-instance" && ["paused-human-gate", "completed"].includes(value.status)) {
37
39
  if (value.context_reconciliation?.status !== "reconciled" || !value.context_reconciliation?.ref) throw new TypeError("project-instance 在会签或完成前必须具有 reconciled context_reconciliation");
@@ -10,24 +10,44 @@ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
10
10
 
11
11
  const query = {
12
12
  mode: "route",
13
- stageId: "stage.discovery",
14
- workUnitId: "work-unit.discovery-requirements",
13
+ stageId: "stage.plan",
14
+ workUnitId: "work-unit.plan-requirements",
15
15
  include: ["grill_exit"],
16
16
  };
17
17
  const first = queryLifecycleContext(query);
18
18
  const second = queryLifecycleContext(query);
19
19
 
20
20
  assert.deepEqual(first, second, "同一查询必须产生确定性结果");
21
+ // 合并调用须保留原来三次查询的路由、阶段和技能信息。
22
+ const modeOnly = queryLifecycleContext({ mode: query.mode });
23
+ const stageOnly = queryLifecycleContext({ stageId: query.stageId });
24
+ const unitOnly = queryLifecycleContext({ workUnitId: query.workUnitId });
25
+ assert.deepEqual(first.execution.mode, modeOnly.execution.mode);
26
+ for (const key of ["stage", "gates", "artifacts", "evidence"]) {
27
+ assert.deepEqual(first.lifecycle[key], stageOnly.lifecycle[key]);
28
+ }
29
+ assert.deepEqual(first.lifecycle.work_unit, unitOnly.lifecycle.work_unit);
30
+ assert.deepEqual(first.execution.work_unit_route, unitOnly.execution.work_unit_route);
31
+ assert.deepEqual(first.execution.transition, unitOnly.execution.transition);
32
+ assert.deepEqual(first.skills, unitOnly.skills);
33
+ const efficient = queryLifecycleContext({ ...query, include: ["grill_exit", "execution_efficiency"] });
34
+ assert.deepEqual(efficient.lifecycle, first.lifecycle, "执行成本策略不得改变阶段门禁");
35
+ assert.deepEqual(efficient.skills, first.skills);
36
+ assert.deepEqual(efficient.execution.transition, first.execution.transition);
37
+ assert.equal(efficient.execution.selected.execution_efficiency.verification.skip_triggered_gates, "forbidden");
38
+ assert.equal("execution_efficiency" in first.execution.selected, false, "成本策略仍按需加载");
21
39
  assert.equal(first.query.mode, "route");
22
- assert.equal(first.lifecycle.stage.id, "stage.discovery");
23
- assert.ok(first.lifecycle.gates.every((gate) => gate.stage === "stage.discovery"));
24
- assert.ok(first.lifecycle.artifacts.every((artifact) => artifact.stage === "stage.discovery"));
25
- assert.equal(first.lifecycle.work_unit.id, "work-unit.discovery-requirements");
40
+ assert.equal(first.lifecycle.stage.id, "stage.plan");
41
+ assert.ok(first.lifecycle.gates.every((gate) => gate.stage === "stage.plan"));
42
+ assert.ok(first.lifecycle.artifacts.every((artifact) => artifact.stage === "stage.plan"));
43
+ assert.equal(first.lifecycle.work_unit.id, "work-unit.plan-requirements");
26
44
  assert.equal(first.execution.mode, "read_only");
27
45
  assert.equal(first.execution.work_unit_route.primary_skill, "yss-product-lifecycle");
28
46
  assert.ok(first.execution.selected.grill_exit.required.includes("frontier_empty"));
29
47
  assert.equal("review_input" in first.execution.selected, false, "不得返回未请求的合同子树");
30
48
  assert.ok(first.skills.some((skill) => skill.id === "grilling"));
49
+ assert.throws(() => queryLifecycleContext({ stageId: "stage.discovery" }), /未知阶段/);
50
+ assert.throws(() => queryLifecycleContext({ workUnitId: "work-unit.discovery-requirements" }), /未知工作单元/);
31
51
  assert.match(first.sources.lifecycle_registry.semantic_sha256, /^[0-9a-f]{64}$/);
32
52
  assert.match(first.sources.orchestration_contract.sha256, /^[0-9a-f]{64}$/);
33
53
 
@@ -43,8 +63,8 @@ for (const invalid of [
43
63
  const cli = spawnSync("node", [
44
64
  "scripts/query-lifecycle-context",
45
65
  "--mode", "route",
46
- "--stage", "stage.discovery",
47
- "--work-unit", "work-unit.discovery-requirements",
66
+ "--stage", "stage.plan",
67
+ "--work-unit", "work-unit.plan-requirements",
48
68
  "--include", "grill_exit",
49
69
  ], { cwd: ROOT, encoding: "utf8" });
50
70
  assert.equal(cli.status, 0, cli.stderr);
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ import { decisionIO } from './lib/user-decision.mjs';
3
+ import { assertPlanSpecEntry } from './lib/plan-spec-entry.mjs';
4
+ try {
5
+ if (process.argv.length !== 3) throw new TypeError('用法: node scripts/verify-plan-spec-entry <state.yaml|json>');
6
+ assertPlanSpecEntry(decisionIO().document(process.argv[2]));
7
+ process.stdout.write('Plan → Spec 入口检查通过(不授权实现)\n');
8
+ } catch (error) {
9
+ process.stderr.write(`${error.message}\n`);
10
+ process.exitCode = 1;
11
+ }
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env node
2
+ import assert from 'node:assert/strict';
3
+ import { validateNextRoute } from './lib/lifecycle-transition.mjs';
4
+ import { queryLifecycleContext } from './lib/lifecycle-context-query.mjs';
5
+ import { mkdtempSync, rmSync } from 'node:fs';
6
+ import { tmpdir } from 'node:os';
7
+ import path from 'node:path';
8
+ import { buildPlanFixture } from './fixtures/user-decision/plan-fixture.mjs';
9
+ import { assertPlanSpecEntry, assertPlanCheckpoint } from './lib/plan-spec-entry.mjs';
10
+ import { readFileSync } from 'node:fs';
11
+ import { decisionDigest } from './lib/user-decision.mjs';
12
+ import { buildDecisionFixture } from './fixtures/user-decision/build-fixture.mjs';
13
+ import { loadDigitalHumanRoles, countersignRuleForGate } from './lib/digital-human-roles.mjs';
14
+
15
+ for (const unit of ['plan-opportunity', 'plan-requirements', 'domain-strategy-design', 'stage-decision']) {
16
+ for (const state of [undefined, {}, { gates: {}, user_decisions: [] }]) {
17
+ assert.equal(validateNextRoute(`work-unit.${unit}`, 'work-unit.spec-synthesis', state).result, 'blocked', `${unit}: missing Plan review must block`);
18
+ }
19
+ }
20
+ const query = queryLifecycleContext({ workUnitId: 'work-unit.plan-requirements' });
21
+ assert.ok(query.execution.selected.planning);
22
+ assert.ok(query.execution.selected.grill_exit);
23
+ assert.ok(query.lifecycle.gates.length);
24
+ assert.ok(query.execution.plan_checks.every(check => check.status === 'pending'));
25
+ const specQuery = queryLifecycleContext({ stageId: 'stage.spec-architecture' });
26
+ assert.ok(specQuery.execution.selected.planning);
27
+ assert.ok(specQuery.execution.plan_checks.every(check => check.status === 'pending'));
28
+ assert.ok(specQuery.lifecycle.gates.some(gate => gate.stage === 'stage.plan'));
29
+ const temp = mkdtempSync(path.join(tmpdir(), 'yss-plan-entry-'));
30
+ try {
31
+ const mutations = [
32
+ ['缺检查项', f => delete f.review.checks.goals],
33
+ ['默认 pending', f => f.review.checks.goals.status = 'pending'],
34
+ ['检查项不能 N/A', f => f.review.checks.goals.status = 'not-applicable'],
35
+ ['缺证据', f => f.review.checks.goals.evidence_refs = []],
36
+ ['缺影响面', f => delete f.review.impacts.domain_strategy],
37
+ ['缺门禁', f => delete f.review.gates['gate.domain-strategy-approved']],
38
+ ['命中门禁不能 N/A', f => f.review.impacts.domain_strategy = true],
39
+ ['N/A 无原因', f => delete f.review.gates['gate.domain-strategy-approved'].reason],
40
+ ['关键问题延期', f => f.review.open_items.push({ id: 'q1', critical: true, runnable_blocker: false, status: 'deferred' })],
41
+ ['可执行阻塞延期', f => f.review.open_items.push({ id: 'q1', critical: false, runnable_blocker: true, status: 'deferred' })],
42
+ ['延期未交接', f => f.review.open_items.push({ id: 'q1', critical: false, runnable_blocker: false, status: 'deferred' })],
43
+ ['无回复', f => { f.approval.record.responses = []; f.approval.save(); }],
44
+ ['过期依据', f => f.write('plan.md', '变更后的范围')],
45
+ ['错误范围', f => f.state.feature_id = 'feature.other'],
46
+ ['调和未通过', f => f.review.context_reconciliation_ref = 'plan.md'],
47
+ ];
48
+ const valid = buildPlanFixture(path.join(temp, 'valid'));
49
+ assert.equal(assertPlanSpecEntry(valid.state, { root: valid.root }).result, 'allowed');
50
+ assert.equal(validateNextRoute('work-unit.plan-opportunity', 'work-unit.spec-synthesis', valid.state, { root: valid.root }).result, 'allowed');
51
+ valid.review.open_items = [{ id: 'q-detail', critical: false, runnable_blocker: false, status: 'deferred', noncritical_reason: '不影响范围或关键规则', owner: '测试负责人', resolution_point: 'Spec 评审前', downstream_recipient: '测试需求负责人', evidence_refs: ['plan.md'] }];
52
+ valid.reapprove();
53
+ assert.equal(assertPlanSpecEntry(valid.state, { root: valid.root }).result, 'allowed', '用户确认后的非关键延期可通过');
54
+ for (const [gateId, impact] of Object.entries({ 'gate.domain-strategy-approved': 'domain_strategy', 'gate.stage-decision-package-approved': 'stage_decision' })) {
55
+ const subjectRef = path.join(valid.root, 'plan.md');
56
+ const reply = buildDecisionFixture(path.join(valid.root, impact), { boundary: gateId, subjectRef });
57
+ const rule = countersignRuleForGate(loadDigitalHumanRoles().gate_policy, gateId);
58
+ const approvalRef = path.join(valid.root, `${impact}.json`);
59
+ valid.write(approvalRef, { schema_version: 1, gate_id: gateId, decision: 'approved', actor_kind: 'digital-human', role_id: rule.countersigners[0], runtime_id: 'runtime.skill-projection', principal_ref: 'test-only.reviewer', subject_ref: subjectRef, approval_scope: ['feature.demo'], user_decision_ref: reply.ref });
60
+ valid.review.impacts[impact] = true;
61
+ valid.review.gates[gateId] = { status: 'approved', approval_ref: approvalRef, subject_ref: subjectRef, approval_scope: ['feature.demo'], evidence_refs: [approvalRef, subjectRef] };
62
+ for (const ref of [approvalRef, subjectRef]) if (!valid.review.basis.some(asset => asset.ref === ref)) valid.review.basis.push({ ref, digest: decisionDigest(readFileSync(ref)) });
63
+ }
64
+ valid.reapprove();
65
+ assert.equal(assertPlanSpecEntry(valid.state, { root: valid.root }).result, 'allowed', '命中门禁、真实回复来源与当前资产绑定均通过');
66
+ valid.review.gates['gate.domain-strategy-approved'].approval_scope = ['feature.other']; valid.reapprove();
67
+ assert.throws(() => assertPlanSpecEntry(valid.state, { root: valid.root }), /会签资产或范围不匹配/);
68
+ for (const [name, mutate] of mutations) {
69
+ const f = buildPlanFixture(path.join(temp, name)); mutate(f); f.save();
70
+ assert.throws(() => assertPlanSpecEntry(f.state, { root: f.root }), undefined, name);
71
+ }
72
+ for (const checkpoint of [{ next_work_unit: 'work-unit.spec-synthesis' }, { stage: 'stage.spec-architecture' }, { stage_trace: { completed_work_unit: 'work-unit.spec-synthesis' } }]) {
73
+ assert.throws(() => assertPlanCheckpoint({ repository_mode: 'project-instance', ...checkpoint }), /plan-spec-entry-blocked/);
74
+ }
75
+ const stale = buildPlanFixture(path.join(temp, 'stale-review'));
76
+ stale.review.gates['gate.domain-strategy-approved'].reason += ' changed'; stale.save();
77
+ assert.throws(() => assertPlanSpecEntry(stale.state, { root: stale.root }), /user-decision-stale/);
78
+ } finally { rmSync(temp, { recursive: true, force: true }); }
79
+ process.stdout.write('Plan → Spec 无状态绕过与默认加载检查通过\n');
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env node
2
+ import assert from 'node:assert/strict';
3
+ import * as fs from 'node:fs';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import { spawn, spawnSync } from 'node:child_process';
7
+ import { once } from 'node:events';
8
+ import { pathToFileURL } from 'node:url';
9
+ import { validateJsonSchemas, validateJsonSchema } from './lib/json-schema.mjs';
10
+ import { runCommand, runCommandSync } from './lib/command-runner.mjs';
11
+ const temp=fs.mkdtempSync(path.join(os.tmpdir(),'yss-performance-scenarios-'));
12
+ try {
13
+ const schema=path.join(temp,'schema.json');
14
+ fs.writeFileSync(schema,JSON.stringify({$defs:{name:{type:'string',minLength:2}},type:'object',required:['name'],properties:{name:{$ref:'#/$defs/name'},email:{type:'string',format:'email'},items:{type:'array',items:{type:'integer'}}},additionalProperties:false}));
15
+ const values=[{name:'okay'},{name:'x'},{name:'okay',email:'wrong'},null,{name:'okay',items:['bad']},{name:'okay',unknown:1}];
16
+ for(const formatChecker of [true,false])for(const errorStyle of ['compact','verbose']) {
17
+ const jobs=values.map(value=>({value,schemaPath:schema,formatChecker,errorStyle}));
18
+ const results=validateJsonSchemas(jobs);
19
+ // Independent original Python behavior, not the new single-item wrapper.
20
+ const legacy=spawnSync('python3',['-c',`import json,sys\nfrom jsonschema import Draft202012Validator,FormatChecker\ns=json.load(open(sys.argv[1])); rows=[]\nfor v in json.load(sys.stdin):\n e=list(Draft202012Validator(s,format_checker=${formatChecker?'FormatChecker()':'None'}).iter_errors(v))\n ${errorStyle==='compact'?"e.sort(key=lambda x:list(x.absolute_path))":"pass"}\n message=${errorStyle==='compact'?`'\\n'.join(f"{'.'.join(str(p) for p in x.absolute_path) or '<root>'}: {x.message}" for x in e)`:`'\\n'.join(str(x) for x in e)`}\n rows.append({'valid':not e,'error':message})\njson.dump(rows,sys.stdout)`,schema],{input:JSON.stringify(values),encoding:'utf8'});
21
+ assert.equal(legacy.status,0,legacy.stderr);
22
+ assert.deepEqual(results,JSON.parse(legacy.stdout));
23
+ }
24
+ assert.deepEqual(validateJsonSchemas([]),[]);
25
+ assert.throws(()=>validateJsonSchema({},path.join(temp,'missing.json')),/缺少/);
26
+ fs.writeFileSync(schema,JSON.stringify({type:'number'}));
27
+ assert.throws(()=>validateJsonSchema({name:'okay'},schema),/not of type/,'schema 改变后必须重新读取');
28
+ const log=path.join(temp,'stdout.log');
29
+ const split=await runCommand(process.execPath,['-e',"process.stdout.write('ab');setTimeout(()=>process.stdout.write('aba!'),20)"],{stdoutFile:log,secrets:['ababa']});
30
+ assert.equal(split.stdout,'[REDACTED]!');assert.equal(fs.readFileSync(log,'utf8'),'[REDACTED]!');
31
+ const timed=await runCommand(process.execPath,['-e',"setInterval(()=>{},1000)"],{timeoutMs:100});
32
+ assert.equal(timed.status,124);assert.equal(timed.termination,'timeout');
33
+ const controller=new AbortController();
34
+ const cancelled=runCommand(process.execPath,['-e',"setInterval(()=>{},1000)"],{signal:controller.signal});
35
+ setTimeout(()=>controller.abort(),50);assert.equal((await cancelled).termination,'cancelled');
36
+ const missing=await runCommand(path.join(temp,'missing'),[]);assert.notEqual(missing.status,0);
37
+ assert.equal(runCommandSync(process.execPath,['-e',"process.stdout.write('sync')"]).stdout,'sync');
38
+ const pidFile=path.join(temp,'pid');
39
+ const tree=await runCommand(process.execPath,['-e',`const {spawn}=require('node:child_process');const fs=require('node:fs');const c=spawn(process.execPath,['-e','setInterval(()=>{},1000)'],{stdio:'ignore'});fs.writeFileSync(${JSON.stringify(pidFile)},String(c.pid));setInterval(()=>{},1000)`],{timeoutMs:250});
40
+ assert.equal(tree.termination,'timeout');
41
+ const pid=Number(fs.readFileSync(pidFile,'utf8'));
42
+ // A reparented zombie may briefly exist; it must never remain a running child.
43
+ const state=spawnSync('ps',['-o','stat=','-p',String(pid)],{encoding:'utf8'});
44
+ assert.ok(state.status!==0 || !state.stdout.trim() || state.stdout.trim().startsWith('Z'),state.stdout);
45
+ const orphanPid=path.join(temp,'orphan-pid'), scratch=path.join(temp,'owned-fetch');fs.mkdirSync(scratch);
46
+ const parent=spawn(process.execPath,['--input-type=module','-e',`import {runCommandSync} from ${JSON.stringify(pathToFileURL(path.join(import.meta.dirname,'lib/command-runner.mjs')).href)};runCommandSync(process.execPath,['-e',${JSON.stringify(`require('node:fs').writeFileSync(${JSON.stringify(orphanPid)},String(process.pid));setInterval(()=>{},1000)`)}],{cleanupPathsOnFailure:[${JSON.stringify(scratch)}]});`],{stdio:'ignore'});
47
+ const exited=once(parent,'exit');
48
+ for(let n=0;n<150&&!fs.existsSync(orphanPid);n++)await new Promise(resolve=>setTimeout(resolve,20));
49
+ assert.ok(fs.existsSync(orphanPid),'worker 子进程应启动');
50
+ parent.kill('SIGTERM');await exited;
51
+ for(let n=0;n<150&&fs.existsSync(scratch);n++)await new Promise(resolve=>setTimeout(resolve,20));
52
+ assert.equal(fs.existsSync(scratch),false,'同步调用者退出后 worker 应清理其临时资源');
53
+ const orphanState=spawnSync('ps',['-o','stat=','-p',fs.readFileSync(orphanPid,'utf8')],{encoding:'utf8'});
54
+ assert.ok(orphanState.status!==0 || !orphanState.stdout.trim() || orphanState.stdout.trim().startsWith('Z'),orphanState.stdout);
55
+ console.log('schema 批量差分、重新读取、日志脱敏、超时、取消与进程树清理通过');
56
+ } finally { fs.rmSync(temp,{recursive:true,force:true}); }
@@ -83,7 +83,7 @@ function packageBase(roleId, taskId, workUnitId, kind, contractFields, stageId)
83
83
  }
84
84
 
85
85
  function lifecyclePackage() {
86
- return packageBase("role.requirements-manager", "task-discovery", "work-unit.discovery-opportunity", "lifecycle-work-unit", { lifecycle_ref: "docs/process/lifecycle-registry.yaml", artifact_refs: ["artifact.discovery-record"] , gate_refs: [] , contract_id: "lifecycle-discovery", contract_version: 1 }, "stage.discovery");
86
+ return packageBase("role.requirements-manager", "task-discovery", "work-unit.plan-opportunity", "lifecycle-work-unit", { lifecycle_ref: "docs/process/lifecycle-registry.yaml", artifact_refs: ["artifact.plan-record"] , gate_refs: [] , contract_id: "lifecycle-discovery", contract_version: 1 }, "stage.plan");
87
87
  }
88
88
 
89
89
  function lifecycleStagePackage(roleId, taskId, workUnitId, stageId) {
@@ -134,7 +134,7 @@ function completedResult(pkg, overrides = {}) {
134
134
  violation: [],
135
135
  new_impacts: [],
136
136
  stale_candidates: [],
137
- next_route: "work-unit.spec-synthesis",
137
+ next_route: "work-unit.plan-requirements",
138
138
  blocking_signals: [],
139
139
  ...overrides
140
140
  };
@@ -160,18 +160,19 @@ try {
160
160
  maintenance.review_context = { implementation_actor_id: "role.lifecycle-orchestrator.instance-1" };
161
161
  const frontend = slicePackage("role.frontend-engineer", "task-frontend", "work-unit.slice-frontend", "apps/frontend/todo/");
162
162
  const backend = slicePackage("role.backend-engineer", "task-backend", "work-unit.slice-backend", "apps/backend/todo/");
163
- [discovery, spec, prototype, technical, tickets, review].forEach((pkg) => validateTaskPackage(pkg));
163
+ [discovery, prototype, technical, tickets, review].forEach((pkg) => validateTaskPackage(pkg));
164
+ assert.throws(() => validateTaskPackage(spec), /plan-spec-entry-blocked/, "直接派发 Spec 缺 Plan 审阅须阻断");
164
165
  validateTaskPackage(maintenance);
165
166
  validateTaskPackage(frontend);
166
167
  assert.throws(() => validateTaskPackage({ ...frontend, user_decisions: [] }), /user-decision-scope-mismatch/, "Worker 派发前必须有当前实施范围批准");
167
168
  validateTaskPackage(backend);
168
169
  validateTaskPackageSet([frontend, backend]);
169
170
 
170
- const completedDiscovery = lifecyclePackage();
171
- completedDiscovery.workflow_status = "resolved";
172
- completedDiscovery.verification_results = [{ command: "scripts/verify-template", exit_code: 0, executed_at: "now", evidence_ref: evidenceRef }];
173
- completedDiscovery.result = completedResult(completedDiscovery);
174
- validateTaskPackage(completedDiscovery);
171
+ const completedPlan = lifecyclePackage();
172
+ completedPlan.workflow_status = "resolved";
173
+ completedPlan.verification_results = [{ command: "scripts/verify-template", exit_code: 0, executed_at: "now", evidence_ref: evidenceRef }];
174
+ completedPlan.result = completedResult(completedPlan);
175
+ validateTaskPackage(completedPlan);
175
176
 
176
177
  const validFile = path.join(ROOT, ".template-source/evidence/.task-package-valid.json");
177
178
  writeFileSync(validFile, JSON.stringify(discovery));
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import assert from "node:assert/strict";
3
- import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
+ import { chmodSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
4
4
  import { tmpdir } from "node:os";
5
5
  import path from "node:path";
6
6
  import { spawnSync } from "node:child_process";
@@ -40,6 +40,16 @@ try {
40
40
  assert.throws(() => acquireTemplateCommit({ repository: remote, commit, cacheRoot, allowLocal: true }), /缓存不完整/);
41
41
  assert.throws(() => acquireTemplateCommit({ repository: path.join(root, "offline"), commit, cacheRoot: path.join(root, "offline-cache"), allowLocal: true }), /执行失败|does not appear to be a git repository/);
42
42
  assert.throws(() => acquireTemplateCommit({ repository: remote, commit: "main", cacheRoot, allowLocal: true }), /40 位 commit/);
43
+ const realGit = spawnSync("which", ["git"], { encoding: "utf8" }).stdout.trim();
44
+ const bin = path.join(root, "bin"), timeoutCache = path.join(root, "timeout-cache"), previousPath = process.env.PATH;
45
+ mkdirSync(bin);
46
+ writeFileSync(path.join(bin, "git"), `#!/usr/bin/env node\nconst {spawnSync}=require('node:child_process');const args=process.argv.slice(2);if(args.includes('fetch'))setTimeout(()=>{},10000);else process.exit(spawnSync(${JSON.stringify(realGit)},args,{stdio:'inherit'}).status ?? 1);\n`);
47
+ chmodSync(path.join(bin, "git"), 0o755);
48
+ try {
49
+ process.env.PATH = bin + path.delimiter + previousPath;
50
+ assert.throws(() => acquireTemplateCommit({ repository: remote, commit, cacheRoot: timeoutCache, allowLocal: true, timeoutMs: 100 }), /timeout/);
51
+ assert.deepEqual(readdirSync(timeoutCache), [], "超时 fetch 不遗留临时缓存或伪成功条目");
52
+ } finally { process.env.PATH = previousPath; }
43
53
  } finally {
44
54
  rmSync(root, { recursive: true, force: true });
45
55
  }
@@ -88,10 +88,10 @@ check("历史数字人记录可读但不能关闭关键门禁", () => {
88
88
  const f = fixture("approval"); Object.assign(record, { subject_ref: f.requirement.subject_ref, approval_scope: f.requirement.scope, user_decision_ref: f.ref }); validateApprovalRecord(record, { requireApproved: true });
89
89
  });
90
90
  check("等待期间可保存状态但不能流转依赖工作", () => {
91
- const f = fixture("workflow", { boundary: "discovery-conclusion" }); const state = { user_decisions: [f.requirement] };
92
- assert.equal(validateNextRoute("work-unit.discovery-requirements", "work-unit.spec-synthesis", state).result, "allowed");
93
- f.record.responses = []; f.save(); assert.equal(validateNextRoute("work-unit.discovery-requirements", "work-unit.spec-synthesis", state).result, "blocked");
94
- assert.equal(validateNextRoute("work-unit.discovery-opportunity", "work-unit.discovery-requirements", state).result, "allowed");
91
+ const f = fixture("workflow", { boundary: "plan-conclusion" }); const state = { user_decisions: [f.requirement] };
92
+ assert.equal(validateNextRoute("work-unit.plan-requirements", "work-unit.spec-synthesis", state).result, "blocked", "仅用户确认不能代替完整 Plan 入口审阅");
93
+ f.record.responses = []; f.save(); assert.equal(validateNextRoute("work-unit.plan-requirements", "work-unit.spec-synthesis", state).result, "blocked");
94
+ assert.equal(validateNextRoute("work-unit.plan-opportunity", "work-unit.plan-requirements", state).result, "allowed");
95
95
  assertCheckpointUserDecisions({ repository_mode: "project-instance", status: "paused-human-gate", human_review: { required_decisions: [f.requirement] } });
96
96
  assert.throws(() => assertCheckpointUserDecisions({ repository_mode: "project-instance", status: "running", human_review: { required_decisions: [f.requirement] } }), /user-decision-response-required/);
97
97
  });
@@ -21,7 +21,7 @@
21
21
  "revision": "c4d958f18cc947cb91cbe1bd48783fa4601cc18f"
22
22
  },
23
23
  "iloveZzz/yss-harness-design-agent": {
24
- "revision": "8a67e0f5a56aa5085d18c2b42bb540c8f36c2fab"
24
+ "revision": "3a358684c5684ada433dca4fd97ee187050ca6d8"
25
25
  }
26
26
  },
27
27
  "skills": {
@@ -99,7 +99,7 @@
99
99
  "source": "competitive-intelligence",
100
100
  "sourceType": "local",
101
101
  "skillPath": "sales/skills/competitive-intelligence/SKILL.md",
102
- "effectiveHash": "d4b83600889b6045182232f0c11a9d6a563030b5f60faa4385a7886843ef0109",
102
+ "effectiveHash": "df1473dfd96b1a3ba63ef929917973b613988edf62193b5b82d734de2e8418cd",
103
103
  "targets": [
104
104
  ".agents/skills",
105
105
  ".claude/skills",
@@ -164,7 +164,7 @@
164
164
  "source": "mattpocock/skills",
165
165
  "sourceType": "github",
166
166
  "skillPath": "skills/engineering/domain-modeling/SKILL.md",
167
- "effectiveHash": "02989ac6fd21562ba87af7a88b5d34f11661f86a3a42014e366613ecdb81bd81",
167
+ "effectiveHash": "7e691aeef60ee61c423f224acd3c4481a8764d3d336c8c49585b36d1a9bca9be",
168
168
  "sourceRevision": "0ab1b63a410a03d3627979a109c8695de27af954",
169
169
  "upstreamHash": "2bc3bb0a64ea9c6f5c3bcf78fc17f0fcdc7c062edc547ef006441de0b576d248",
170
170
  "adaptationRef": "docs/process/MATT-POCOCK-ENGINEERING-SKILLS.md",
@@ -304,7 +304,7 @@
304
304
  "source": "mattpocock/skills",
305
305
  "sourceType": "github",
306
306
  "skillPath": "skills/engineering/grill-with-docs/SKILL.md",
307
- "effectiveHash": "c0f724b8cb4be22d10d8e8de3dfd49eb024cf131a3bcf5e7b89e109ad3d80b32",
307
+ "effectiveHash": "2006f65f386e79ff37c2c3294efe146e2e546845cf52e1571340dcb2a81a0df3",
308
308
  "sourceRevision": "0ab1b63a410a03d3627979a109c8695de27af954",
309
309
  "upstreamHash": "6b1292031c4f38a11309b9dad9be1141c1921c6edf86d1ae6da8b048fa71a349",
310
310
  "adaptationRef": "docs/process/MATT-POCOCK-ENGINEERING-SKILLS.md",
@@ -559,7 +559,7 @@
559
559
  "sourceType": "git-submodule",
560
560
  "skillPath": ".agents/skills/prototype-review/SKILL.md",
561
561
  "effectiveHash": "51021f119697dbff644d70c3d6ba3314147c47f412c6f56e4b5540ea3bf554a1",
562
- "sourceRevision": "8a67e0f5a56aa5085d18c2b42bb540c8f36c2fab",
562
+ "sourceRevision": "3a358684c5684ada433dca4fd97ee187050ca6d8",
563
563
  "upstreamHash": "2e77b3093a758bb692fb3bda453c5c020d0523f875975e17e2731365f67432e1",
564
564
  "adaptationRef": "docs/agents/strategic-design-skills-integration.md",
565
565
  "targets": [
@@ -594,7 +594,7 @@
594
594
  "source": "mattpocock/skills",
595
595
  "sourceType": "github",
596
596
  "skillPath": "skills/engineering/setup-matt-pocock-skills/SKILL.md",
597
- "effectiveHash": "c1463ecc928789308855e9995e9787cf311128a8567f8ed671b83d696a9b4cc9",
597
+ "effectiveHash": "1f63e63e8036a08ac6d0626b4dbbd16d8fbe1ae171cae31b36e82c606552396e",
598
598
  "sourceRevision": "0ab1b63a410a03d3627979a109c8695de27af954",
599
599
  "upstreamHash": "80cd6b5be4f67913f1b10f5acd97fccaeb6401682ae7ff3a971460cb73d86cbe",
600
600
  "adaptationRef": "docs/process/MATT-POCOCK-ENGINEERING-SKILLS.md",
@@ -824,7 +824,7 @@
824
824
  "sourceType": "git-submodule",
825
825
  "skillPath": ".agents/skills/yss-antd-design/SKILL.md",
826
826
  "effectiveHash": "917099caa9b76ca3cfd1d0f81f61b28111c1c54ed93e92891f81012dcd0662e2",
827
- "sourceRevision": "8a67e0f5a56aa5085d18c2b42bb540c8f36c2fab",
827
+ "sourceRevision": "3a358684c5684ada433dca4fd97ee187050ca6d8",
828
828
  "upstreamHash": "08ce99d45586d5ea19e25fdd9e9a6ede16e66f18992b70b87e0cd23f92343b16",
829
829
  "adaptationRef": "docs/agents/strategic-design-skills-integration.md",
830
830
  "targets": [
@@ -842,7 +842,7 @@
842
842
  "sourceType": "git-submodule",
843
843
  "skillPath": ".agents/skills/yss-antdv-next-design/SKILL.md",
844
844
  "effectiveHash": "8c55e3a815ace34fbf591e931a58970b3d58cb657e00432ca6c72938d307563b",
845
- "sourceRevision": "8a67e0f5a56aa5085d18c2b42bb540c8f36c2fab",
845
+ "sourceRevision": "3a358684c5684ada433dca4fd97ee187050ca6d8",
846
846
  "upstreamHash": "2942ed52dd58a38e51f09eb11254809334aeaed08f21cd081f09f35e6647a68c",
847
847
  "adaptationRef": "docs/agents/strategic-design-skills-integration.md",
848
848
  "targets": [
@@ -937,7 +937,7 @@
937
937
  "source": "project",
938
938
  "sourceType": "local",
939
939
  "skillPath": ".agents/skills/yss-ddd-scaffold-generator/SKILL.md",
940
- "effectiveHash": "96e1c82c538a96af293273906db5ddefcf9943e8b76bb47b05d08772f16258cb",
940
+ "effectiveHash": "ab1bd70b73a148d57a8e3f88e9a94be57b81f59bbfb81849e6e1dba16bfc7e16",
941
941
  "targets": [
942
942
  ".agents/skills",
943
943
  ".claude/skills",
@@ -953,7 +953,7 @@
953
953
  "sourceType": "git-submodule",
954
954
  "skillPath": ".agents/skills/yss-design-system/SKILL.md",
955
955
  "effectiveHash": "e69597b8b6a5ce4de5bd40f67630443b8176202f6b028b2ce146a2a1d062816f",
956
- "sourceRevision": "8a67e0f5a56aa5085d18c2b42bb540c8f36c2fab",
956
+ "sourceRevision": "3a358684c5684ada433dca4fd97ee187050ca6d8",
957
957
  "upstreamHash": "90aaf06b2514353152464a4d032076fa9d5ef56272d8ad526f5544f3c22a2b00",
958
958
  "adaptationRef": "docs/agents/strategic-design-skills-integration.md",
959
959
  "targets": [
@@ -1078,7 +1078,7 @@
1078
1078
  "source": "project",
1079
1079
  "sourceType": "local",
1080
1080
  "skillPath": ".agents/skills/yss-frontend-scaffold-generator/SKILL.md",
1081
- "effectiveHash": "968c213c90a30d719e51050a6122ef729bc03ae97d2841a5d47824d68a5d27ff",
1081
+ "effectiveHash": "2f829e4e2a9cb42e123e8de37fe812bc65eef1308758e077b52f0698de9b68ab",
1082
1082
  "targets": [
1083
1083
  ".agents/skills",
1084
1084
  ".claude/skills",
@@ -1228,7 +1228,7 @@
1228
1228
  "source": "project",
1229
1229
  "sourceType": "local",
1230
1230
  "skillPath": ".agents/skills/yss-product-lifecycle/SKILL.md",
1231
- "effectiveHash": "d317bd2799da0442745c8126e62f7919a0b3de03f2c3cc2e1a7ca0433dc69645",
1231
+ "effectiveHash": "a54a583c92621aede69e2f7db99f7373e0724b3187acd44c6d20c0144ca9943b",
1232
1232
  "targets": [
1233
1233
  ".agents/skills",
1234
1234
  ".claude/skills",
@@ -1244,7 +1244,7 @@
1244
1244
  "sourceType": "git-submodule",
1245
1245
  "skillPath": ".agents/skills/yss-prototype-stage/SKILL.md",
1246
1246
  "effectiveHash": "0ecf3125a73d15e4c9b6b665f92535180df82ccb2255fa863ec59f3ff5d460aa",
1247
- "sourceRevision": "8a67e0f5a56aa5085d18c2b42bb540c8f36c2fab",
1247
+ "sourceRevision": "3a358684c5684ada433dca4fd97ee187050ca6d8",
1248
1248
  "upstreamHash": "e8f2f3a507da6bf7be0582e4a4dd9659381da8e291755070698a1f51a4e33808",
1249
1249
  "adaptationRef": "docs/agents/strategic-design-skills-integration.md",
1250
1250
  "targets": [
@@ -1276,7 +1276,7 @@
1276
1276
  "source": "mattpocock/skills",
1277
1277
  "sourceType": "github",
1278
1278
  "skillPath": "skills/engineering/research/SKILL.md",
1279
- "effectiveHash": "a0c69c6cff59bdb6d36b03d24cceb29698e76cd6aae88919bf864284737c3aaa",
1279
+ "effectiveHash": "0d8f94e6379ad56897a7821b4803a3ba72910e617aab730e47db719e6eb02807",
1280
1280
  "sourceRevision": "0ab1b63a410a03d3627979a109c8695de27af954",
1281
1281
  "upstreamHash": "75b7f9bbd7ccff6755a006f8b25d319a46ecbf6bd4b4c3323965ed9cb65e78ff",
1282
1282
  "adaptationRef": "docs/process/MATT-POCOCK-ENGINEERING-SKILLS.md",
@@ -1339,9 +1339,9 @@
1339
1339
  "source": "iloveZzz/yss-harness-design-agent",
1340
1340
  "sourceType": "git-submodule",
1341
1341
  "skillPath": ".agents/skills/yss-stage-decision/SKILL.md",
1342
- "effectiveHash": "63e222ec2bc572c8d66fc52143ba3862650d243f57e1e9c71214b533eba37c23",
1343
- "sourceRevision": "8a67e0f5a56aa5085d18c2b42bb540c8f36c2fab",
1344
- "upstreamHash": "1eadf230f0e17bf6d209c1df1249acef3520ab29c26a80c476a2a557a224c10a",
1342
+ "effectiveHash": "ad5180d162c6f9484aac556ed114e1447f93715d2d7e3c4bc1a53430adae65f1",
1343
+ "sourceRevision": "3a358684c5684ada433dca4fd97ee187050ca6d8",
1344
+ "upstreamHash": "2ec63f66aaf799d693cec0f0b5d71b8c4917e4e2822e0ddd519bc9b711fa3c95",
1345
1345
  "adaptationRef": "docs/agents/strategic-design-skills-integration.md",
1346
1346
  "targets": [
1347
1347
  ".agents/skills",
@@ -56,6 +56,7 @@
56
56
  ".pi/settings.json",
57
57
  ".cursor/environment.json",
58
58
  "docs/.scratch",
59
+ "docs/discovery",
59
60
  "docs/reviews",
60
61
  "docs/adr"
61
62
  ],
@@ -65,7 +66,7 @@
65
66
  "yss-project.yaml"
66
67
  ],
67
68
  "exampleDocPaths": [
68
- "docs/discovery/IDEATION.md"
69
+ "docs/plan/IDEATION.md"
69
70
  ],
70
71
  "initExcludeRootEntries": [],
71
72
  "initExcludeRootFiles": [
@@ -3,15 +3,15 @@
3
3
  "templateName": "yss-spec-project-template",
4
4
  "templateSource": "github:iloveZzz/yss-spec-project-template",
5
5
  "templateRepository": "https://github.com/iloveZzz/yss-spec-project-template.git",
6
- "requestedRef": "6f5c5d80adcdb743fb06199f70a7ab0b0a640ce5",
7
- "templateCommit": "6f5c5d80adcdb743fb06199f70a7ab0b0a640ce5",
8
- "manifestHash": "f7520dfdfa388b19ea47cb893c2daab8936019783aa84589f13921e459702280",
6
+ "requestedRef": "ae2a3bdeca25249aed96d0a4d289b356d38417f9",
7
+ "templateCommit": "ae2a3bdeca25249aed96d0a4d289b356d38417f9",
8
+ "manifestHash": "e1e9f7d3e00c43b3e6cc889d961c94d684d08b852d8635c0a2f6b463b24432e6",
9
9
  "encodedPaths": {
10
10
  ".codex/skills/data-analytics/.gitignore": ".codex/skills/data-analytics/__yss_dotfile__.gitignore",
11
11
  ".codex/skills/product-design/.npmignore": ".codex/skills/product-design/__yss_dotfile__.npmignore",
12
12
  ".codex/skills/product-design/templates/prototype/.npmrc": ".codex/skills/product-design/templates/prototype/__yss_dotfile__.npmrc",
13
13
  ".gitignore": "__yss_dotfile__.gitignore"
14
14
  },
15
- "snapshotHash": "2a676bf4ed0d3151bab51ad900181cbb8fdd628385572e11dd398bcbaceab61b",
16
- "generatedAt": "2026-09-09T02:16:12.516Z"
15
+ "snapshotHash": "b15d75307273b8d82f7e08b1b81a7d47ff88aa76cb7e05638ff5489620fe1888",
16
+ "generatedAt": "2026-09-10T02:41:41.548Z"
17
17
  }