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.
- package/package.json +3 -2
- package/src/command-runner.mjs +107 -0
- package/src/self-update.js +7 -4
- package/src/template/instance-runtime.js +1 -1
- package/template/.agents/skills/.strategic-design-skills-manifest.json +2 -2
- package/template/.agents/skills/competitive-intelligence/SKILL.md +4 -4
- package/template/.agents/skills/domain-modeling/CONTEXT-FORMAT.md +1 -1
- package/template/.agents/skills/grill-with-docs/SKILL.md +1 -1
- package/template/.agents/skills/setup-matt-pocock-skills/issue-tracker-local.md +1 -1
- package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +7 -7
- package/template/.agents/skills/yss-frontend-scaffold-generator/SKILL.md +1 -1
- package/template/.agents/skills/yss-frontend-scaffold-generator/scripts/generate_scaffold.mjs +22 -8
- package/template/.agents/skills/yss-frontend-scaffold-generator/scripts/run_scaffold_verification.mjs +5 -6
- package/template/.agents/skills/yss-frontend-scaffold-generator/scripts/scaffold-generator.test.mjs +19 -1
- package/template/.agents/skills/yss-product-lifecycle/SKILL.md +7 -4
- package/template/.agents/skills/yss-product-lifecycle/references/artifact-dependencies.md +1 -1
- package/template/.agents/skills/yss-product-lifecycle/references/matt-yss-adapter.md +3 -3
- package/template/.agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml +57 -6
- package/template/.agents/skills/yss-product-lifecycle/references/orchestration.md +11 -1
- package/template/.agents/skills/yss-research/SKILL.md +1 -1
- package/template/.agents/skills/yss-stage-decision/SKILL.md +5 -3
- package/template/.agents/skills/yss-stage-decision/references/stage-decision-package-contract.md +1 -1
- package/template/.claude/skills/competitive-intelligence/SKILL.md +4 -4
- package/template/.claude/skills/domain-modeling/CONTEXT-FORMAT.md +1 -1
- package/template/.claude/skills/grill-with-docs/SKILL.md +1 -1
- package/template/.claude/skills/setup-matt-pocock-skills/issue-tracker-local.md +1 -1
- package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +7 -7
- package/template/.claude/skills/yss-frontend-scaffold-generator/SKILL.md +1 -1
- package/template/.claude/skills/yss-frontend-scaffold-generator/scripts/generate_scaffold.mjs +22 -8
- package/template/.claude/skills/yss-frontend-scaffold-generator/scripts/run_scaffold_verification.mjs +5 -6
- package/template/.claude/skills/yss-frontend-scaffold-generator/scripts/scaffold-generator.test.mjs +19 -1
- package/template/.claude/skills/yss-product-lifecycle/SKILL.md +7 -4
- package/template/.claude/skills/yss-product-lifecycle/references/artifact-dependencies.md +1 -1
- package/template/.claude/skills/yss-product-lifecycle/references/matt-yss-adapter.md +3 -3
- package/template/.claude/skills/yss-product-lifecycle/references/orchestration-contract.yaml +57 -6
- package/template/.claude/skills/yss-product-lifecycle/references/orchestration.md +11 -1
- package/template/.claude/skills/yss-research/SKILL.md +1 -1
- package/template/.claude/skills/yss-stage-decision/SKILL.md +5 -3
- package/template/.claude/skills/yss-stage-decision/references/stage-decision-package-contract.md +1 -1
- package/template/.codex/skills/competitive-intelligence/SKILL.md +4 -4
- package/template/.codex/skills/domain-modeling/CONTEXT-FORMAT.md +1 -1
- package/template/.codex/skills/grill-with-docs/SKILL.md +1 -1
- package/template/.codex/skills/setup-matt-pocock-skills/issue-tracker-local.md +1 -1
- package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +7 -7
- package/template/.codex/skills/yss-frontend-scaffold-generator/SKILL.md +1 -1
- package/template/.codex/skills/yss-frontend-scaffold-generator/scripts/generate_scaffold.mjs +22 -8
- package/template/.codex/skills/yss-frontend-scaffold-generator/scripts/run_scaffold_verification.mjs +5 -6
- package/template/.codex/skills/yss-frontend-scaffold-generator/scripts/scaffold-generator.test.mjs +19 -1
- package/template/.codex/skills/yss-product-lifecycle/SKILL.md +7 -4
- package/template/.codex/skills/yss-product-lifecycle/references/artifact-dependencies.md +1 -1
- package/template/.codex/skills/yss-product-lifecycle/references/matt-yss-adapter.md +3 -3
- package/template/.codex/skills/yss-product-lifecycle/references/orchestration-contract.yaml +57 -6
- package/template/.codex/skills/yss-product-lifecycle/references/orchestration.md +11 -1
- package/template/.codex/skills/yss-research/SKILL.md +1 -1
- package/template/.codex/skills/yss-stage-decision/SKILL.md +5 -3
- package/template/.codex/skills/yss-stage-decision/references/stage-decision-package-contract.md +1 -1
- package/template/.cursor/skills/competitive-intelligence/SKILL.md +4 -4
- package/template/.cursor/skills/domain-modeling/CONTEXT-FORMAT.md +1 -1
- package/template/.cursor/skills/grill-with-docs/SKILL.md +1 -1
- package/template/.cursor/skills/setup-matt-pocock-skills/issue-tracker-local.md +1 -1
- package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +7 -7
- package/template/.cursor/skills/yss-frontend-scaffold-generator/SKILL.md +1 -1
- package/template/.cursor/skills/yss-frontend-scaffold-generator/scripts/generate_scaffold.mjs +22 -8
- package/template/.cursor/skills/yss-frontend-scaffold-generator/scripts/run_scaffold_verification.mjs +5 -6
- package/template/.cursor/skills/yss-frontend-scaffold-generator/scripts/scaffold-generator.test.mjs +19 -1
- package/template/.cursor/skills/yss-product-lifecycle/SKILL.md +7 -4
- package/template/.cursor/skills/yss-product-lifecycle/references/artifact-dependencies.md +1 -1
- package/template/.cursor/skills/yss-product-lifecycle/references/matt-yss-adapter.md +3 -3
- package/template/.cursor/skills/yss-product-lifecycle/references/orchestration-contract.yaml +57 -6
- package/template/.cursor/skills/yss-product-lifecycle/references/orchestration.md +11 -1
- package/template/.cursor/skills/yss-research/SKILL.md +1 -1
- package/template/.cursor/skills/yss-stage-decision/SKILL.md +5 -3
- package/template/.cursor/skills/yss-stage-decision/references/stage-decision-package-contract.md +1 -1
- package/template/.pi/skills/competitive-intelligence/SKILL.md +4 -4
- package/template/.pi/skills/domain-modeling/CONTEXT-FORMAT.md +1 -1
- package/template/.pi/skills/grill-with-docs/SKILL.md +1 -1
- package/template/.pi/skills/setup-matt-pocock-skills/issue-tracker-local.md +1 -1
- package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +7 -7
- package/template/.pi/skills/yss-frontend-scaffold-generator/SKILL.md +1 -1
- package/template/.pi/skills/yss-frontend-scaffold-generator/scripts/generate_scaffold.mjs +22 -8
- package/template/.pi/skills/yss-frontend-scaffold-generator/scripts/run_scaffold_verification.mjs +5 -6
- package/template/.pi/skills/yss-frontend-scaffold-generator/scripts/scaffold-generator.test.mjs +19 -1
- package/template/.pi/skills/yss-product-lifecycle/SKILL.md +7 -4
- package/template/.pi/skills/yss-product-lifecycle/references/artifact-dependencies.md +1 -1
- package/template/.pi/skills/yss-product-lifecycle/references/matt-yss-adapter.md +3 -3
- package/template/.pi/skills/yss-product-lifecycle/references/orchestration-contract.yaml +57 -6
- package/template/.pi/skills/yss-product-lifecycle/references/orchestration.md +11 -1
- package/template/.pi/skills/yss-research/SKILL.md +1 -1
- package/template/.pi/skills/yss-stage-decision/SKILL.md +5 -3
- package/template/.pi/skills/yss-stage-decision/references/stage-decision-package-contract.md +1 -1
- package/template/.qoder/skills/competitive-intelligence/SKILL.md +4 -4
- package/template/.qoder/skills/domain-modeling/CONTEXT-FORMAT.md +1 -1
- package/template/.qoder/skills/grill-with-docs/SKILL.md +1 -1
- package/template/.qoder/skills/setup-matt-pocock-skills/issue-tracker-local.md +1 -1
- package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +7 -7
- package/template/.qoder/skills/yss-frontend-scaffold-generator/SKILL.md +1 -1
- package/template/.qoder/skills/yss-frontend-scaffold-generator/scripts/generate_scaffold.mjs +22 -8
- package/template/.qoder/skills/yss-frontend-scaffold-generator/scripts/run_scaffold_verification.mjs +5 -6
- package/template/.qoder/skills/yss-frontend-scaffold-generator/scripts/scaffold-generator.test.mjs +19 -1
- package/template/.qoder/skills/yss-product-lifecycle/SKILL.md +7 -4
- package/template/.qoder/skills/yss-product-lifecycle/references/artifact-dependencies.md +1 -1
- package/template/.qoder/skills/yss-product-lifecycle/references/matt-yss-adapter.md +3 -3
- package/template/.qoder/skills/yss-product-lifecycle/references/orchestration-contract.yaml +57 -6
- package/template/.qoder/skills/yss-product-lifecycle/references/orchestration.md +11 -1
- package/template/.qoder/skills/yss-research/SKILL.md +1 -1
- package/template/.qoder/skills/yss-stage-decision/SKILL.md +5 -3
- package/template/.qoder/skills/yss-stage-decision/references/stage-decision-package-contract.md +1 -1
- package/template/.trae/skills/competitive-intelligence/SKILL.md +4 -4
- package/template/.trae/skills/domain-modeling/CONTEXT-FORMAT.md +1 -1
- package/template/.trae/skills/grill-with-docs/SKILL.md +1 -1
- package/template/.trae/skills/setup-matt-pocock-skills/issue-tracker-local.md +1 -1
- package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +7 -7
- package/template/.trae/skills/yss-frontend-scaffold-generator/SKILL.md +1 -1
- package/template/.trae/skills/yss-frontend-scaffold-generator/scripts/generate_scaffold.mjs +22 -8
- package/template/.trae/skills/yss-frontend-scaffold-generator/scripts/run_scaffold_verification.mjs +5 -6
- package/template/.trae/skills/yss-frontend-scaffold-generator/scripts/scaffold-generator.test.mjs +19 -1
- package/template/.trae/skills/yss-product-lifecycle/SKILL.md +7 -4
- package/template/.trae/skills/yss-product-lifecycle/references/artifact-dependencies.md +1 -1
- package/template/.trae/skills/yss-product-lifecycle/references/matt-yss-adapter.md +3 -3
- package/template/.trae/skills/yss-product-lifecycle/references/orchestration-contract.yaml +57 -6
- package/template/.trae/skills/yss-product-lifecycle/references/orchestration.md +11 -1
- package/template/.trae/skills/yss-research/SKILL.md +1 -1
- package/template/.trae/skills/yss-stage-decision/SKILL.md +5 -3
- package/template/.trae/skills/yss-stage-decision/references/stage-decision-package-contract.md +1 -1
- package/template/AGENTS.md +2 -2
- package/template/CONTEXT.md +3 -2
- package/template/README.md +3 -3
- package/template/docs/agents/digital-human-roles.md +1 -1
- package/template/docs/agents/digital-human-roles.yaml +11 -11
- package/template/docs/agents/issue-tracker.md +1 -1
- package/template/docs/agents/strategic-design-skills-integration.md +2 -0
- package/template/docs/architecture/README.md +1 -1
- package/template/docs/architecture/templates/business-architecture-template.md +1 -1
- package/template/docs/architecture/templates/functional-architecture-template.md +1 -1
- package/template/docs/design/README.md +1 -1
- package/template/docs/design/templates/interaction-spec-template.md +1 -1
- package/template/docs/design/templates/product-overview-design-template.md +1 -1
- package/template/docs/{discovery → plan}/IDEATION.md +5 -5
- package/template/docs/plan/README.md +9 -0
- package/template/docs/plan/entry-review.md +23 -0
- package/template/docs/plan/templates/plan-template.md +58 -0
- package/template/docs/process/harness-process-tailoring.md +1 -1
- package/template/docs/process/lifecycle-artifact-map.md +6 -6
- package/template/docs/process/lifecycle-registry-baseline.json +5 -1
- package/template/docs/process/lifecycle-registry.yaml +25 -20
- package/template/docs/process/plan-migration.md +13 -0
- package/template/docs/process/schemas/digital-human-task-package.schema.json +2 -0
- package/template/docs/process/schemas/lifecycle-checkpoint.schema.json +3 -0
- package/template/docs/process/schemas/lifecycle-registry.schema.json +1 -1
- package/template/docs/process/template-engineering-overview.md +2 -2
- package/template/docs/process/template-verification-profiles.yaml +2 -0
- package/template/docs/process/templates/context-reconciliation-template.yaml +2 -2
- package/template/docs/process/templates/lifecycle-checkpoint-template.yaml +1 -1
- package/template/docs/requirements/README.md +2 -2
- package/template/docs/templates/local-parent-ticket-template.md +1 -1
- package/template/docs/templates/subagent-task-package-template.md +1 -1
- package/template/docs/user-guide//347/224/250/346/210/267/346/211/213/345/206/214.md +1 -1
- package/template/scripts/cache-template-commit +2 -2
- package/template/scripts/fixtures/strategic-handoff/source-roles.json +7 -7
- package/template/scripts/fixtures/user-decision/plan-fixture.mjs +30 -0
- package/template/scripts/lib/backend-delivery.mjs +5 -2
- package/template/scripts/lib/command-runner.mjs +107 -0
- package/template/scripts/lib/context-reconciliation.mjs +34 -0
- package/template/scripts/lib/json-schema.mjs +42 -13
- package/template/scripts/lib/lifecycle-context-query.mjs +7 -3
- package/template/scripts/lib/lifecycle-transition.mjs +9 -3
- package/template/scripts/lib/plan-spec-entry.mjs +87 -0
- package/template/scripts/lib/scenario-checks.mjs +15 -12
- package/template/scripts/lib/skill-supply-chain.mjs +18 -2
- package/template/scripts/lib/strategic-handoff-io.mjs +5 -4
- package/template/scripts/lib/strategic-handoff.mjs +10 -9
- package/template/scripts/lib/task-package.mjs +2 -0
- package/template/scripts/lib/template-commit-cache.mjs +7 -5
- package/template/scripts/run-template-verification +9 -3
- package/template/scripts/sync-strategic-handoff-tools +9 -0
- package/template/scripts/verify-context-reconciliation +2 -48
- package/template/scripts/verify-context-reconciliation-scenarios +1 -1
- package/template/scripts/verify-lifecycle-checkpoint +2 -0
- package/template/scripts/verify-lifecycle-context-query-scenarios +28 -8
- package/template/scripts/verify-plan-spec-entry +11 -0
- package/template/scripts/verify-plan-spec-entry-scenarios +79 -0
- package/template/scripts/verify-script-performance-scenarios +56 -0
- package/template/scripts/verify-subagent-task-package-scenarios +9 -8
- package/template/scripts/verify-template-cache-scenarios +11 -1
- package/template/scripts/verify-user-decision-scenarios +4 -4
- package/template/skills-lock.json +17 -17
- package/template.manifest.json +2 -1
- package/template.snapshot.json +5 -5
- package/template/docs/discovery/README.md +0 -87
- package/template/docs/discovery/reports/.gitkeep +0 -0
- package/template/docs/discovery/reports/agent-governance-options.md +0 -71
- package/template/docs/discovery/reports/competitor-deep-dive/.gitkeep +0 -0
- package/template/docs/discovery/templates/discovery-template.md +0 -94
- /package/template/docs/{discovery → plan}/templates/competitive-analysis-template.md +0 -0
- /package/template/docs/{discovery → plan}/templates/competitive-matrix-template.md +0 -0
- /package/template/docs/{discovery → plan}/templates/market-analysis-template.md +0 -0
- /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 {
|
|
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
|
-
|
|
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.
|
|
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.
|
|
14
|
-
workUnitId: "work-unit.
|
|
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.
|
|
23
|
-
assert.ok(first.lifecycle.gates.every((gate) => gate.stage === "stage.
|
|
24
|
-
assert.ok(first.lifecycle.artifacts.every((artifact) => artifact.stage === "stage.
|
|
25
|
-
assert.equal(first.lifecycle.work_unit.id, "work-unit.
|
|
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.
|
|
47
|
-
"--work-unit", "work-unit.
|
|
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.
|
|
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.
|
|
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,
|
|
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
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
validateTaskPackage(
|
|
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: "
|
|
92
|
-
assert.equal(validateNextRoute("work-unit.
|
|
93
|
-
f.record.responses = []; f.save(); assert.equal(validateNextRoute("work-unit.
|
|
94
|
-
assert.equal(validateNextRoute("work-unit.
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
1343
|
-
"sourceRevision": "
|
|
1344
|
-
"upstreamHash": "
|
|
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",
|
package/template.manifest.json
CHANGED
|
@@ -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/
|
|
69
|
+
"docs/plan/IDEATION.md"
|
|
69
70
|
],
|
|
70
71
|
"initExcludeRootEntries": [],
|
|
71
72
|
"initExcludeRootFiles": [
|
package/template.snapshot.json
CHANGED
|
@@ -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": "
|
|
7
|
-
"templateCommit": "
|
|
8
|
-
"manifestHash": "
|
|
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": "
|
|
16
|
-
"generatedAt": "2026-09-
|
|
15
|
+
"snapshotHash": "b15d75307273b8d82f7e08b1b81a7d47ff88aa76cb7e05638ff5489620fe1888",
|
|
16
|
+
"generatedAt": "2026-09-10T02:41:41.548Z"
|
|
17
17
|
}
|