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,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema_version": 1,
|
|
3
3
|
"registry_id": "yss.lifecycle",
|
|
4
|
-
"semantic_sha256": "
|
|
4
|
+
"semantic_sha256": "96fcbcb858eed45ba228d87c75804f28ccb464b1963c0d23be348ade094bac01",
|
|
5
5
|
"published_ids": [
|
|
6
6
|
"artifact.architecture-review",
|
|
7
7
|
"artifact.data-architecture",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"artifact.openapi-draft",
|
|
20
20
|
"artifact.openapi-freeze-record",
|
|
21
21
|
"artifact.parent-ticket",
|
|
22
|
+
"artifact.plan-record",
|
|
22
23
|
"artifact.product-overview",
|
|
23
24
|
"artifact.project-scaffold-contract",
|
|
24
25
|
"artifact.prototype-confirmation",
|
|
@@ -76,6 +77,7 @@
|
|
|
76
77
|
"gate.user-confirmation",
|
|
77
78
|
"stage.discovery",
|
|
78
79
|
"stage.entry-triage",
|
|
80
|
+
"stage.plan",
|
|
79
81
|
"stage.product-design",
|
|
80
82
|
"stage.spec-architecture",
|
|
81
83
|
"stage.system-data-engineering",
|
|
@@ -94,6 +96,8 @@
|
|
|
94
96
|
"work-unit.independent-review",
|
|
95
97
|
"work-unit.intensity-aware-review",
|
|
96
98
|
"work-unit.intensity-aware-verification",
|
|
99
|
+
"work-unit.plan-opportunity",
|
|
100
|
+
"work-unit.plan-requirements",
|
|
97
101
|
"work-unit.prototype-design",
|
|
98
102
|
"work-unit.release-and-retrospective",
|
|
99
103
|
"work-unit.release-and-rollback",
|
|
@@ -8,17 +8,22 @@ id_policy:
|
|
|
8
8
|
pattern: '^(stage|gate|artifact|work-unit|evidence)\.[a-z0-9][a-z0-9-]*$'
|
|
9
9
|
published_ids_immutable: true
|
|
10
10
|
baseline: docs/process/lifecycle-registry-baseline.json
|
|
11
|
-
deprecated_ids: [artifact.high-fidelity-html-prototype, work-unit.fresh-verification, work-unit.independent-review]
|
|
11
|
+
deprecated_ids: [artifact.high-fidelity-html-prototype, work-unit.fresh-verification, work-unit.independent-review, stage.discovery, artifact.discovery-record, work-unit.discovery-opportunity, work-unit.discovery-requirements]
|
|
12
12
|
|
|
13
13
|
stages:
|
|
14
14
|
- id: stage.entry-triage
|
|
15
15
|
name: 入口分诊
|
|
16
16
|
goal: 确认仓库身份、问题范围和影响面。
|
|
17
17
|
exit_criteria: yss-project.yaml 合法,影响面和最近可信阶段可解释。
|
|
18
|
-
- id: stage.
|
|
19
|
-
name:
|
|
20
|
-
goal:
|
|
21
|
-
exit_criteria:
|
|
18
|
+
- id: stage.plan
|
|
19
|
+
name: Plan(战略规划)
|
|
20
|
+
goal: 确认目标、业务边界、关键规则、MVP / 非目标、优先级和交接责任,为 Spec 提供战略输入;按影响面探索并复用仍有效的结论。
|
|
21
|
+
exit_criteria: 用户确认规划结论,命中的战略与阶段决策门禁通过;影响业务边界、关键规则或 MVP 的问题已解决,其他未决项有责任人和解决时点;下游可进入 Spec,不代表可实现。
|
|
22
|
+
spec_entry:
|
|
23
|
+
required_checks: [goals, scope_and_priority, boundaries_and_rules, open_questions, handoff, context_reconciliation, grill_exit]
|
|
24
|
+
gate_impacts:
|
|
25
|
+
gate.domain-strategy-approved: domain_strategy
|
|
26
|
+
gate.stage-decision-package-approved: stage_decision
|
|
22
27
|
- id: stage.spec-architecture
|
|
23
28
|
name: Spec / 功能架构
|
|
24
29
|
goal: 固化解决方案和功能边界。
|
|
@@ -130,15 +135,15 @@ gates:
|
|
|
130
135
|
evidence: [evidence.repository-identity-check]
|
|
131
136
|
- id: gate.domain-strategy-approved
|
|
132
137
|
name: DDD 战略设计批准
|
|
133
|
-
stage: stage.
|
|
138
|
+
stage: stage.plan
|
|
134
139
|
trigger: 存在 DDD 战略设计影响或需要确定领域边界、统一语言和核心规则。
|
|
135
140
|
public_name: 业务边界与规则确认
|
|
136
141
|
public_trigger: 需要确定业务板块、业务责任区、统一业务词汇、协作关系或关键规则。
|
|
137
142
|
evidence: [evidence.domain-strategy-review, evidence.approval-record]
|
|
138
143
|
- id: gate.stage-decision-package-approved
|
|
139
144
|
name: 阶段决策包批准
|
|
140
|
-
stage: stage.
|
|
141
|
-
trigger:
|
|
145
|
+
stage: stage.plan
|
|
146
|
+
trigger: Plan 到 Spec 入口需要稳定的阶段决策合同。
|
|
142
147
|
evidence: [evidence.stage-decision-package, evidence.approval-record]
|
|
143
148
|
requires_gates: [gate.domain-strategy-approved]
|
|
144
149
|
- id: gate.spec-baseline-approved
|
|
@@ -221,18 +226,18 @@ artifacts:
|
|
|
221
226
|
trigger: 每次变更。
|
|
222
227
|
- id: artifact.domain-strategy
|
|
223
228
|
name: DDD 战略设计
|
|
224
|
-
stage: stage.
|
|
229
|
+
stage: stage.plan
|
|
225
230
|
trigger: 新产品/模块、跨上下文功能、统一语言冲突、服务边界或核心规则变化。
|
|
226
231
|
public_name: 业务边界与规则设计
|
|
227
232
|
public_trigger: 新产品/模块、跨责任区协作、业务词汇冲突、责任边界或关键规则变化。
|
|
228
233
|
- id: artifact.stage-decision-package
|
|
229
234
|
name: 阶段决策包
|
|
230
|
-
stage: stage.
|
|
231
|
-
trigger:
|
|
235
|
+
stage: stage.plan
|
|
236
|
+
trigger: Plan 到 Spec 入口需要结构化上游决策。
|
|
232
237
|
public_name: 方案决策包
|
|
233
|
-
- id: artifact.
|
|
234
|
-
name:
|
|
235
|
-
stage: stage.
|
|
238
|
+
- id: artifact.plan-record
|
|
239
|
+
name: Plan 记录
|
|
240
|
+
stage: stage.plan
|
|
236
241
|
trigger: 新问题或边界不清。
|
|
237
242
|
- id: artifact.spec
|
|
238
243
|
name: Spec
|
|
@@ -384,13 +389,13 @@ work_units:
|
|
|
384
389
|
input: 已审查 commit。
|
|
385
390
|
output: release note、观察信号、回滚点。
|
|
386
391
|
completion: 两仓库顺序和恢复动作明确。
|
|
387
|
-
- id: work-unit.
|
|
392
|
+
- id: work-unit.plan-opportunity
|
|
388
393
|
name: 机会调研
|
|
389
394
|
scope: project-instance
|
|
390
395
|
input: 用户问题、市场/竞品事实需求和现有上下文。
|
|
391
|
-
output:
|
|
396
|
+
output: Plan 机会结论、证据、替代方案和关键假设。
|
|
392
397
|
completion: 机会继续/停止建议可审查;事实已 research 或记录为假设。
|
|
393
|
-
- id: work-unit.
|
|
398
|
+
- id: work-unit.plan-requirements
|
|
394
399
|
name: 需求分析
|
|
395
400
|
scope: project-instance
|
|
396
401
|
input: 机会结论、用户反馈和领域词汇。
|
|
@@ -408,16 +413,16 @@ work_units:
|
|
|
408
413
|
- id: work-unit.stage-decision
|
|
409
414
|
name: 阶段决策包综合
|
|
410
415
|
scope: project-instance
|
|
411
|
-
input:
|
|
416
|
+
input: Plan、DDD 战略设计以及产品经理负责的商业约束输入。
|
|
412
417
|
output: 带版本、digest、证据和下游映射的阶段决策包。
|
|
413
418
|
completion: 必填字段、引用、影响面和下游消费验证通过;批准门禁完成。
|
|
414
419
|
public_name: 方案决策包综合
|
|
415
|
-
public_input:
|
|
420
|
+
public_input: Plan、业务边界与规则设计以及产品经理负责的商业约束输入。
|
|
416
421
|
public_output: 带版本、digest、证据和下游映射的方案决策包。
|
|
417
422
|
- id: work-unit.spec-synthesis
|
|
418
423
|
name: Spec 综合
|
|
419
424
|
scope: project-instance
|
|
420
|
-
input: 已确认的
|
|
425
|
+
input: 已确认的 Plan 记录和测试 seam。
|
|
421
426
|
output: Spec、产品总体设计和功能架构。
|
|
422
427
|
completion: Spec 内容完整并进入 ready-for-human;下游推进仍需 gate.spec-baseline-approved。
|
|
423
428
|
- id: work-unit.prototype-design
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Plan 阶段切换说明
|
|
2
|
+
|
|
3
|
+
新流程仅使用 `stage.plan`、`artifact.plan-record`、`work-unit.plan-opportunity` 和 `work-unit.plan-requirements`。不提供 Discovery 阶段别名、旧标识解析或兼容入口;旧 ID 在注册表中仅作为弃用清单保留,以防重新分配,不可执行。
|
|
4
|
+
|
|
5
|
+
新工作从 `docs/plan/templates/plan-template.md` 起草,工作目录为 `docs/.scratch/<feature>/plan/`。问题、机会和事实探索是 Plan 内按影响面执行的活动。
|
|
6
|
+
|
|
7
|
+
历史冻结文档、批准记录和摘要保持原字节。旧状态不能直接恢复执行;如需继续,按当前 Plan 模板重新整理输入并核查当前退出条件,不把旧批准自动视为新流程批准。已有事实和结论可引用,但须记录当前版本、适用范围和复用理由。
|
|
8
|
+
|
|
9
|
+
综合模板和战略设计模板共同采用 Plan。它为 Spec 提供战略输入;后续 Spec、页面原型、契约、Ticket 及交接边界仍由各自 profile 和生命周期注册表定义。Plan 不等同于实现合同中的 Context Plan。
|
|
10
|
+
|
|
11
|
+
影响业务边界、关键规则或 MVP 的未决项必须在进入 Spec 前解决;其他细节须经用户确认延期,并具备不影响当前决定的依据、责任人、解决时点和接收方。既有 runnable blocker 不得降为普通延期项。
|
|
12
|
+
|
|
13
|
+
Plan 是借鉴 AI-DLC 前置规划职责的本地名称,不宣称是 AWS 官方阶段名称或完整 Inception。
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
"additionalProperties": false,
|
|
7
7
|
"required": ["schema_version", "task_id", "work_unit_id", "actor_id", "role_id", "runtime_id", "execution_state", "workflow_status", "skill_source", "contract", "inputs", "objective", "allowed_write_paths", "forbidden_actions", "expected_outputs", "expected_evidence_files", "verification_commands", "verification_results", "downstream_consumers", "convergence"],
|
|
8
8
|
"properties": {
|
|
9
|
+
"plan_review_ref": { "type": "string", "minLength": 1 },
|
|
10
|
+
"plan_user_decision_ref": { "type": "string", "minLength": 1 },
|
|
9
11
|
"schema_version": { "const": 1 },
|
|
10
12
|
"frontend_delivery": {
|
|
11
13
|
"type": "object", "additionalProperties": false, "required": ["acceptance_ref"],
|
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
"additionalProperties": false,
|
|
7
7
|
"required": ["schema_version", "repository_mode", "mode", "status", "stage", "artifacts", "gates", "context_reconciliation", "next_work_unit", "ticket_sync", "verification", "human_review", "git_checkpoint", "blockers", "rollback"],
|
|
8
8
|
"properties": {
|
|
9
|
+
"plan_review_ref": { "type": "string", "minLength": 1 },
|
|
10
|
+
"plan_user_decision_ref": { "type": "string", "minLength": 1 },
|
|
11
|
+
"feature_id": { "type": "string", "minLength": 1 },
|
|
9
12
|
"schema_version": { "const": 1 },
|
|
10
13
|
"repository_mode": { "enum": ["template-source", "project-instance"] },
|
|
11
14
|
"mode": { "enum": ["route", "orchestrate", "resume", "audit"] },
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"$defs": {
|
|
29
29
|
"id": { "type": "string", "pattern": "^(stage|gate|artifact|work-unit|evidence)\\.[a-z0-9][a-z0-9-]*$" },
|
|
30
|
-
"stages": { "type": "array", "minItems": 1, "items": { "type": "object", "additionalProperties": false, "required": ["id", "name", "goal", "exit_criteria"], "properties": { "id": { "$ref": "#/$defs/id" }, "name": { "type": "string", "minLength": 1 }, "goal": { "type": "string", "minLength": 1 }, "exit_criteria": { "type": "string", "minLength": 1 }, "public_name": { "type": "string", "minLength": 1 }, "public_goal": { "type": "string", "minLength": 1 }, "public_exit_criteria": { "type": "string", "minLength": 1 } } } },
|
|
30
|
+
"stages": { "type": "array", "minItems": 1, "items": { "type": "object", "additionalProperties": false, "required": ["id", "name", "goal", "exit_criteria"], "properties": { "spec_entry": {"type":"object","required":["required_checks","gate_impacts"],"additionalProperties":false,"properties":{"required_checks":{"type":"array","minItems":1,"uniqueItems":true,"items":{"type":"string","minLength":1}},"gate_impacts":{"type":"object","minProperties":1,"additionalProperties":{"type":"string","minLength":1}}}}, "id": { "$ref": "#/$defs/id" }, "name": { "type": "string", "minLength": 1 }, "goal": { "type": "string", "minLength": 1 }, "exit_criteria": { "type": "string", "minLength": 1 }, "public_name": { "type": "string", "minLength": 1 }, "public_goal": { "type": "string", "minLength": 1 }, "public_exit_criteria": { "type": "string", "minLength": 1 } } } },
|
|
31
31
|
"objects": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["id", "name", "stage", "trigger"], "properties": { "id": { "$ref": "#/$defs/id" }, "name": { "type": "string", "minLength": 1 }, "stage": { "type": "string", "pattern": "^stage\\." }, "trigger": { "type": "string", "minLength": 1 }, "public_name": { "type": "string", "minLength": 1 }, "public_trigger": { "type": "string", "minLength": 1 }, "evidence": { "type": "array", "minItems": 1, "items": { "type": "string", "pattern": "^evidence\\." } }, "requires_gates": { "type": "array", "uniqueItems": true, "items": { "type": "string", "pattern": "^gate\\." } } } } },
|
|
32
32
|
"workUnits": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["id", "name", "scope", "input", "output", "completion"], "properties": { "id": { "$ref": "#/$defs/id" }, "name": { "type": "string", "minLength": 1 }, "scope": { "enum": ["template-source", "project-instance"] }, "input": { "type": "string", "minLength": 1 }, "output": { "type": "string", "minLength": 1 }, "completion": { "type": "string", "minLength": 1 }, "public_name": { "type": "string", "minLength": 1 }, "public_input": { "type": "string", "minLength": 1 }, "public_output": { "type": "string", "minLength": 1 }, "public_completion": { "type": "string", "minLength": 1 } } } },
|
|
33
33
|
"evidence": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["id", "name", "description"], "properties": { "id": { "$ref": "#/$defs/id" }, "name": { "type": "string", "minLength": 1 }, "description": { "type": "string", "minLength": 1 }, "public_name": { "type": "string", "minLength": 1 }, "public_description": { "type": "string", "minLength": 1 } } } }
|
|
@@ -22,7 +22,7 @@ YSS 模板工程是一套可版本化的研发治理系统。它用仓库身份
|
|
|
22
22
|
| 身份 | 用途 | 允许的主要工作 | 边界 |
|
|
23
23
|
|---|---|---|---|
|
|
24
24
|
| `template-source` | 维护通用模板与治理规则 | 更新单一事实来源、同步技能投影、构建 CLI 快照、验证与审查 | 不生成具体产品的 Spec、原型、OpenAPI 或垂直切片 Ticket |
|
|
25
|
-
| `project-instance` | 承载具体产品或模块的研发管理资产 | 执行
|
|
25
|
+
| `project-instance` | 承载具体产品或模块的研发管理资产 | 执行 Plan、Spec、设计、工程契约、Ticket、实现、验证和发布流程 | 不反向成为通用模板的权威来源 |
|
|
26
26
|
|
|
27
27
|
身份只由 `yss-project.yaml` 声明。目录结构、Git remote 和占位符都不能替代该清单。
|
|
28
28
|
|
|
@@ -32,7 +32,7 @@ YSS 模板工程是一套可版本化的研发治理系统。它用仓库身份
|
|
|
32
32
|
|
|
33
33
|
| 产品线 | 模板源 | 实例化 CLI | 生命周期边界 |
|
|
34
34
|
|---|---|---|---|
|
|
35
|
-
| 全产品生命周期 | `yss-spec-project-template` | `create-yss-spec` | 从
|
|
35
|
+
| 全产品生命周期 | `yss-spec-project-template` | `create-yss-spec` | 从 Plan 到发布与复盘 |
|
|
36
36
|
| DDD 战略设计交接 | `yss-harness-design-agent` | `create-yss-harness-design` | 从机会、需求和战略设计到 Strategic Design Handoff |
|
|
37
37
|
| 开发落地 | `yss-harness-dev-agent` | `create-yss-harness-dev` | 从已批准 Spec 或战略设计交接包到 Tactical Design、Slice Contract、实现与验证 |
|
|
38
38
|
|
|
@@ -318,6 +318,7 @@ groups:
|
|
|
318
318
|
- { run: "scripts/verify-context-contract-scenarios" }
|
|
319
319
|
- { run: "node scripts/verify-business-language-scenarios" }
|
|
320
320
|
- { run: "scripts/verify-context-reconciliation-scenarios" }
|
|
321
|
+
- { run: "node scripts/verify-script-performance-scenarios" }
|
|
321
322
|
- { run: "scripts/verify-grill-with-docs-context-scenarios" }
|
|
322
323
|
- { run: "scripts/verify-strategic-context-import-scenarios" }
|
|
323
324
|
- { run: "scripts/verify-lifecycle-registry" }
|
|
@@ -330,6 +331,7 @@ groups:
|
|
|
330
331
|
- { run: "scripts/verify-scaffold-architecture-decision-scenarios" }
|
|
331
332
|
- { run: "scripts/verify-lifecycle-scenarios" }
|
|
332
333
|
- { run: "scripts/verify-lifecycle-transition-scenarios" }
|
|
334
|
+
- { run: "node scripts/verify-plan-spec-entry-scenarios" }
|
|
333
335
|
- { run: "scripts/verify-frontend-scaffold-generator-scenarios" }
|
|
334
336
|
maintenance:
|
|
335
337
|
commands:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 需求定义 (Requirements) — 从想法到可执行定义
|
|
2
2
|
|
|
3
|
-
> **Phase: Requirements | 输入:
|
|
3
|
+
> **Phase: Requirements | 输入: Plan | 输出: Spec + Stories + 验收条件**
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -79,7 +79,7 @@ delegate_task(
|
|
|
79
79
|
## AI-Human Loop
|
|
80
80
|
|
|
81
81
|
```
|
|
82
|
-
|
|
82
|
+
Plan报告 → AI生成Spec → PM审核
|
|
83
83
|
→ AI补充边界 → PM确认 → AI拆解Stories
|
|
84
84
|
→ 团队估算 → AI标注风险 → TechLead确认 → 进入Architecture
|
|
85
85
|
```
|
|
@@ -37,7 +37,7 @@ Status: ready-for-human
|
|
|
37
37
|
|
|
38
38
|
## 资产与证据
|
|
39
39
|
|
|
40
|
-
-
|
|
40
|
+
- Plan:`docs/.scratch/<feature>/plan/`
|
|
41
41
|
- Spec Delta:`docs/.scratch/<feature>/spec-delta/`
|
|
42
42
|
- Design:`docs/.scratch/<feature>/design/`
|
|
43
43
|
- API:`docs/.scratch/<feature>/api/`
|
|
@@ -67,7 +67,7 @@ init 只创建不存在或空目录;attach / sync 默认只预览,写入要
|
|
|
67
67
|
|
|
68
68
|
## 按路线推进
|
|
69
69
|
|
|
70
|
-
综合模板按本仓生命周期从分诊、
|
|
70
|
+
综合模板按本仓生命周期从分诊、Plan、Spec/功能架构、产品设计、工程契约、Ticket、切片推进到验证与发布。战略模板在业务交接停止;通用研发从批准上游接入;专职两端按交付合同接力。
|
|
71
71
|
|
|
72
72
|
每一步都让 Agent 说明:消费了哪些当前输入、准备了什么、缺谁确认、怎样检查结果。具体阶段和门禁从本仓注册表读取,不把别的模板阶段编号复制进本仓。
|
|
73
73
|
|
|
@@ -3,9 +3,9 @@ import { parseArgs } from "node:util";
|
|
|
3
3
|
import { acquireTemplateCommit } from "./lib/template-commit-cache.mjs";
|
|
4
4
|
|
|
5
5
|
try {
|
|
6
|
-
const { values } = parseArgs({ options: { repository: { type: "string" }, commit: { type: "string" }, "cache-root": { type: "string" } }, strict: true });
|
|
6
|
+
const { values } = parseArgs({ options: { repository: { type: "string" }, commit: { type: "string" }, "cache-root": { type: "string" }, "timeout-ms": { type: "string" } }, strict: true });
|
|
7
7
|
if (!values.repository || !values.commit) throw new TypeError("用法: scripts/cache-template-commit --repository <remote-url> --commit <40位commit> [--cache-root <path>]");
|
|
8
|
-
const result = acquireTemplateCommit({ repository: values.repository, commit: values.commit, cacheRoot: values["cache-root"] });
|
|
8
|
+
const result = acquireTemplateCommit({ repository: values.repository, commit: values.commit, cacheRoot: values["cache-root"], timeoutMs: Number(values["timeout-ms"] || 0) });
|
|
9
9
|
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
10
10
|
} catch (error) {
|
|
11
11
|
process.stderr.write(`模板 commit 缓存失败: ${error.message}\n`);
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
],
|
|
74
74
|
"stages": [
|
|
75
75
|
"stage.entry-triage",
|
|
76
|
-
"stage.
|
|
76
|
+
"stage.plan",
|
|
77
77
|
"stage.spec-architecture",
|
|
78
78
|
"stage.product-design",
|
|
79
79
|
"stage.system-data-engineering",
|
|
@@ -103,11 +103,11 @@
|
|
|
103
103
|
"contract_version": 1,
|
|
104
104
|
"title": "YSS 需求经理",
|
|
105
105
|
"stages": [
|
|
106
|
-
"stage.
|
|
106
|
+
"stage.plan",
|
|
107
107
|
"stage.spec-architecture"
|
|
108
108
|
],
|
|
109
109
|
"draft_artifact_ids": [
|
|
110
|
-
"artifact.
|
|
110
|
+
"artifact.plan-record",
|
|
111
111
|
"artifact.spec"
|
|
112
112
|
],
|
|
113
113
|
"core_skills": [
|
|
@@ -137,7 +137,7 @@
|
|
|
137
137
|
"contract_version": 1,
|
|
138
138
|
"title": "YSS 产品经理",
|
|
139
139
|
"stages": [
|
|
140
|
-
"stage.
|
|
140
|
+
"stage.plan",
|
|
141
141
|
"stage.spec-architecture",
|
|
142
142
|
"stage.product-design",
|
|
143
143
|
"stage.verification-release-retrospective"
|
|
@@ -177,7 +177,7 @@
|
|
|
177
177
|
"title": "YSS 项目经理",
|
|
178
178
|
"stages": [
|
|
179
179
|
"stage.entry-triage",
|
|
180
|
-
"stage.
|
|
180
|
+
"stage.plan",
|
|
181
181
|
"stage.spec-architecture",
|
|
182
182
|
"stage.product-design",
|
|
183
183
|
"stage.system-data-engineering",
|
|
@@ -346,9 +346,9 @@
|
|
|
346
346
|
],
|
|
347
347
|
"stage_groups": [
|
|
348
348
|
{
|
|
349
|
-
"id": "group.
|
|
349
|
+
"id": "group.plan",
|
|
350
350
|
"name": "Discovery",
|
|
351
|
-
"stage": "stage.
|
|
351
|
+
"stage": "stage.plan",
|
|
352
352
|
"members": [
|
|
353
353
|
"role.lifecycle-orchestrator",
|
|
354
354
|
"role.requirements-manager",
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Synthetic test evidence only; never import to produce real project approval.
|
|
2
|
+
import { mkdirSync, writeFileSync, readFileSync } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { ROOT } from '../../lib/lifecycle-registry.mjs';
|
|
5
|
+
import { parseContextContract, resolveContextTermRefs } from '../../lib/context-contract.mjs';
|
|
6
|
+
import { decisionDigest } from '../../lib/user-decision.mjs';
|
|
7
|
+
import { planEntryPolicy } from '../../lib/plan-spec-entry.mjs';
|
|
8
|
+
import { buildDecisionFixture } from './build-fixture.mjs';
|
|
9
|
+
|
|
10
|
+
export function buildPlanFixture(root) {
|
|
11
|
+
mkdirSync(path.join(root, 'docs/process'), { recursive: true });
|
|
12
|
+
const write = (ref, value) => writeFileSync(path.resolve(root, ref), typeof value === 'string' ? value : JSON.stringify(value, null, 2));
|
|
13
|
+
write('yss-project.yaml', 'schema_version: 1\nrepository_mode: project-instance\n');
|
|
14
|
+
for (const ref of ['CONTEXT.md', 'docs/process/lifecycle-registry.yaml']) write(ref, readFileSync(path.join(ROOT, ref), 'utf8'));
|
|
15
|
+
write('plan.md', '测试规划:小范围修订,不改变业务边界或关键规则。\n');
|
|
16
|
+
const c = parseContextContract({ root });
|
|
17
|
+
write('reconciliation.json', { schema_version: 1, repository_mode: 'project-instance', stage: 'stage.plan', work_unit: 'work-unit.plan-requirements', status: 'reconciled', context_snapshot: { context_ref: c.context_ref, context_schema_version: c.context_schema_version, document_digest: c.document_digest, referenced_terms_digest: resolveContextTermRefs(c, []).referenced_terms_digest, term_refs: [] }, changes: { added: [], updated: [], deprecated: [] }, unresolved_terms: [], evidence_refs: ['CONTEXT.md'] });
|
|
18
|
+
const policy = planEntryPolicy({ root });
|
|
19
|
+
const review = { schema_version: 1, kind: 'plan-entry-review', feature_id: 'feature.demo', plan_ref: 'plan.md', context_reconciliation_ref: 'reconciliation.json', basis: ['plan.md', 'CONTEXT.md', 'docs/process/lifecycle-registry.yaml', 'reconciliation.json'].map(ref => ({ ref, digest: decisionDigest(readFileSync(path.join(root, ref))) })), checks: Object.fromEntries(policy.required_checks.map(id => [id, { status: 'passed', evidence_refs: ['plan.md'] }])), open_items: [], impacts: { domain_strategy: false, stage_decision: false }, gates: Object.fromEntries(Object.keys(policy.gate_impacts).map(id => [id, { status: 'not-applicable', reason: '本次仅修订已确认范围内的细节,不改变边界、规则或阶段合同', evidence_refs: ['plan.md'] }])) };
|
|
20
|
+
const reviewRef = path.join(root, 'review.json');
|
|
21
|
+
const save = () => write(reviewRef, review);
|
|
22
|
+
save();
|
|
23
|
+
const approval = buildDecisionFixture(path.join(root, 'approval'), { boundary: 'plan-conclusion', subjectRef: reviewRef });
|
|
24
|
+
const reapprove = () => {
|
|
25
|
+
save();
|
|
26
|
+
approval.record.request.items[0].subject.digest = decisionDigest(readFileSync(reviewRef));
|
|
27
|
+
approval.present(); approval.record.responses = []; approval.respond(); approval.save();
|
|
28
|
+
};
|
|
29
|
+
return { root, review, save, write, approval, reapprove, state: { feature_id: review.feature_id, plan_review_ref: reviewRef, plan_user_decision_ref: approval.ref, user_decisions: [approval.requirement] } };
|
|
30
|
+
}
|
|
@@ -81,10 +81,13 @@ function validManifest(manifest,root) {
|
|
|
81
81
|
ensure(manifest.files.reduce((total,file)=>total+file.size_bytes,0)<=512*1024*1024,'后端包大小超限');
|
|
82
82
|
const originals=manifest.files.filter(x=>x.original_ref).map(x=>x.original_ref);
|
|
83
83
|
ensure(new Set(originals.map(x=>x.toLowerCase())).size===originals.length,'后端包源路径重复');
|
|
84
|
+
const captured = new Map();
|
|
84
85
|
for(const file of manifest.files) {
|
|
85
86
|
const bytes=readFileSync(safe(root,file.path));
|
|
86
87
|
ensure(bytes.length===file.size_bytes&&hash(bytes)===file.sha256,`后端包文件摘要不一致: ${file.path}`);
|
|
88
|
+
captured.set(file.path, bytes);
|
|
87
89
|
}
|
|
90
|
+
return captured;
|
|
88
91
|
}
|
|
89
92
|
|
|
90
93
|
export async function openBackendDelivery(input, action) {
|
|
@@ -93,9 +96,9 @@ export async function openBackendDelivery(input, action) {
|
|
|
93
96
|
ensure(existsSync(input)&&!lstatSync(input).isSymbolicLink(),'后端交付包不存在或是 symlink');
|
|
94
97
|
let root=path.resolve(input);
|
|
95
98
|
if(lstatSync(root).isFile()) { root=path.join(temporary,'unpacked');mkdirSync(root);archive('unpack',path.resolve(input),root); }
|
|
96
|
-
const manifest=read(safe(root,'manifest.json'));validManifest(manifest,root);
|
|
99
|
+
const manifest=read(safe(root,'manifest.json'));const captured=validManifest(manifest,root);
|
|
97
100
|
const source=path.join(temporary,'source');mkdirSync(source);
|
|
98
|
-
for(const file of manifest.files)if(file.original_ref)write(source,file.original_ref,
|
|
101
|
+
for(const file of manifest.files)if(file.original_ref)write(source,file.original_ref,captured.get(file.path));
|
|
99
102
|
const inspected=await inspectBackendDelivery(source,manifest.delivery_ref);
|
|
100
103
|
ensure(manifest.delivery_id===inspected.delivery.delivery_id&&manifest.version===inspected.delivery.version,'后端包身份不一致');
|
|
101
104
|
return await action({root,source,manifest,...inspected});
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
2
|
+
import { appendFileSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { StringDecoder } from "node:string_decoder";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
function killTree(child, signal) {
|
|
7
|
+
if (!child.pid) return;
|
|
8
|
+
try {
|
|
9
|
+
if (process.platform === "win32") spawnSync("taskkill", ["/PID", String(child.pid), "/T", "/F"], { stdio: "ignore" });
|
|
10
|
+
else process.kill(-child.pid, signal);
|
|
11
|
+
} catch (error) { if (error.code !== "ESRCH") throw error; }
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Bounded lifetime for external tools; stdout is never mixed with progress. */
|
|
15
|
+
export function runCommand(command, args, { cwd, env = process.env, timeoutMs = 0, signal, stdoutFile, stderrFile, secrets = [], progress = false } = {}) {
|
|
16
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs < 0) throw new TypeError("timeoutMs 必须是非负毫秒数");
|
|
17
|
+
if (signal?.aborted) return Promise.resolve({ status: 1, stdout: "", stderr: "command cancelled", termination: "cancelled", duration_ms: 0 });
|
|
18
|
+
return new Promise(resolve => {
|
|
19
|
+
const started = performance.now();
|
|
20
|
+
const argumentSecrets = args.flatMap(value => { try { const url = new URL(value); return url.password ? [url.password, decodeURIComponent(url.password)] : []; } catch { return []; } });
|
|
21
|
+
const secretNames = new Map(Object.entries(env).filter(([key]) => /(?:PASSWORD|TOKEN|SECRET)$/.test(key) || key === "MAVEN_REPO_USERNAME").map(([key,value]) => [value,key]));
|
|
22
|
+
const replacement = secret => secretNames.has(secret) ? `[REDACTED:${secretNames.get(secret)}]` : "[REDACTED]";
|
|
23
|
+
const sensitive = [...secrets, ...argumentSecrets, ...secretNames.keys()].filter(value => typeof value === "string" && value.length > 0).sort((a, b) => b.length - a.length);
|
|
24
|
+
const redact = text => sensitive.reduce((value, secret) => value.replaceAll(secret, replacement(secret)), text);
|
|
25
|
+
if (progress) process.stderr.write(`[开始] ${redact(command + " " + args.join(" "))}\n`);
|
|
26
|
+
for (const file of [stdoutFile, stderrFile]) if (file) writeFileSync(file, "");
|
|
27
|
+
const child = spawn(command, args, { cwd, env, detached: process.platform !== "win32", stdio: ["ignore", "pipe", "pipe"] });
|
|
28
|
+
let termination = null, escalation, timer, errorMessage;
|
|
29
|
+
const makeStream = file => {
|
|
30
|
+
const decoder = new StringDecoder("utf8");
|
|
31
|
+
let pending = "", captured = "";
|
|
32
|
+
const emit = text => { captured += text; if (file) appendFileSync(file, text); };
|
|
33
|
+
function consume(final = false) {
|
|
34
|
+
let output = "", cursor = 0;
|
|
35
|
+
while (cursor < pending.length) {
|
|
36
|
+
const match = sensitive.find(secret => pending.startsWith(secret, cursor));
|
|
37
|
+
if (match) { output += replacement(match); cursor += match.length; continue; }
|
|
38
|
+
if (!final && sensitive.some(secret => secret.startsWith(pending.slice(cursor)))) break;
|
|
39
|
+
output += pending[cursor++];
|
|
40
|
+
}
|
|
41
|
+
pending = pending.slice(cursor);
|
|
42
|
+
emit(output);
|
|
43
|
+
}
|
|
44
|
+
return { write(bytes) { pending += decoder.write(bytes); consume(); }, end() { pending += decoder.end(); consume(true); return captured; } };
|
|
45
|
+
};
|
|
46
|
+
const stdout = makeStream(stdoutFile), stderr = makeStream(stderrFile);
|
|
47
|
+
function cancel(reason) {
|
|
48
|
+
if (termination) return;
|
|
49
|
+
termination = reason;
|
|
50
|
+
killTree(child, "SIGTERM");
|
|
51
|
+
escalation = setTimeout(() => killTree(child, "SIGKILL"), 1000);
|
|
52
|
+
}
|
|
53
|
+
const abort = () => cancel("cancelled");
|
|
54
|
+
const onInterrupt = () => cancel("cancelled");
|
|
55
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
56
|
+
process.on("SIGINT", onInterrupt); process.on("SIGTERM", onInterrupt);
|
|
57
|
+
if (timeoutMs) timer = setTimeout(() => cancel("timeout"), timeoutMs);
|
|
58
|
+
child.stdout.on("data", bytes => stdout.write(bytes));
|
|
59
|
+
child.stderr.on("data", bytes => stderr.write(bytes));
|
|
60
|
+
child.on("error", error => { errorMessage = redact(error.message); });
|
|
61
|
+
child.on("close", (code, exitSignal) => {
|
|
62
|
+
clearTimeout(timer); clearTimeout(escalation);
|
|
63
|
+
// A group leader can exit before its descendants close redirected streams.
|
|
64
|
+
if (termination) killTree(child, "SIGKILL");
|
|
65
|
+
signal?.removeEventListener("abort", abort);
|
|
66
|
+
process.off("SIGINT", onInterrupt); process.off("SIGTERM", onInterrupt);
|
|
67
|
+
const duration_ms = performance.now() - started;
|
|
68
|
+
const status = termination === "timeout" ? 124 : termination ? 1 : code ?? 1;
|
|
69
|
+
if (progress) process.stderr.write(`[${status === 0 ? "完成" : "失败"}] ${command} (${Math.round(duration_ms)}ms, exit=${status}${termination ? `, ${termination}` : ""})\n`);
|
|
70
|
+
resolve({ status, stdout: stdout.end(), stderr: stderr.end() + (errorMessage || ""), signal: exitSignal, termination, duration_ms });
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Synchronous API compatibility for existing generators/cache consumers. The worker
|
|
76
|
+
// owns the process group and streams logs; it never updates project metadata.
|
|
77
|
+
export function runCommandSync(command, args, options = {}) {
|
|
78
|
+
const result = spawnSync(process.execPath, [fileURLToPath(import.meta.url), "--worker"], {
|
|
79
|
+
input: JSON.stringify({ command, args, options }), encoding: "utf8", maxBuffer: 64 * 1024 * 1024,
|
|
80
|
+
stdio: ["pipe", "pipe", "inherit"], env: options.env ?? process.env,
|
|
81
|
+
});
|
|
82
|
+
if (result.error || result.status !== 0) throw new TypeError(result.error?.message || "command worker failed");
|
|
83
|
+
return JSON.parse(result.stdout);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (process.argv[1] === fileURLToPath(import.meta.url) && process.argv[2] === "--worker") {
|
|
87
|
+
const parent = process.ppid;
|
|
88
|
+
let input = "";
|
|
89
|
+
for await (const bytes of process.stdin) input += bytes;
|
|
90
|
+
const { command, args, options } = JSON.parse(input);
|
|
91
|
+
const controller = new AbortController();
|
|
92
|
+
const watchParent = setInterval(() => {
|
|
93
|
+
if (process.ppid !== parent) controller.abort();
|
|
94
|
+
else {
|
|
95
|
+
try { process.kill(parent, 0); } catch (error) { if (error.code === "ESRCH") controller.abort(); }
|
|
96
|
+
}
|
|
97
|
+
}, 100);
|
|
98
|
+
try {
|
|
99
|
+
const result = await runCommand(command, args, { ...options, signal: controller.signal });
|
|
100
|
+
if (result.status !== 0) {
|
|
101
|
+
// Only caller-created temporary resources are supplied here. The worker
|
|
102
|
+
// also owns this cleanup when a terminated synchronous caller cannot run finally.
|
|
103
|
+
for (const directory of options.cleanupPathsOnFailure || []) rmSync(directory, { recursive: true, force: true });
|
|
104
|
+
}
|
|
105
|
+
if (process.ppid === parent) process.stdout.write(JSON.stringify(result));
|
|
106
|
+
} finally { clearInterval(watchParent); }
|
|
107
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { parseDocument } from "../vendor/yaml.mjs";
|
|
4
|
+
import { resolveContextTermRefs, verifyContextSnapshot } from "./context-contract.mjs";
|
|
5
|
+
import { validateJsonSchema } from "./json-schema.mjs";
|
|
6
|
+
const schemaPath = path.resolve(import.meta.dirname, "../../docs/process/schemas/context-reconciliation.schema.json");
|
|
7
|
+
export function parseReconciliationFile(file) {
|
|
8
|
+
const document = parseDocument(readFileSync(file, "utf8"), { maxAliasCount: 0, uniqueKeys: true });
|
|
9
|
+
if (document.errors.length) throw new TypeError(document.errors[0].message);
|
|
10
|
+
return document.toJS({ maxAliasCount: 0 });
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function verifyContextReconciliation(file, { root = process.cwd() } = {}) {
|
|
14
|
+
root = path.resolve(root);
|
|
15
|
+
const value = parseReconciliationFile(path.resolve(file));
|
|
16
|
+
validateJsonSchema(value, schemaPath, { formatChecker: false });
|
|
17
|
+
const manifest = parseReconciliationFile(path.join(root, "yss-project.yaml"));
|
|
18
|
+
if (value.repository_mode !== manifest.repository_mode) throw new TypeError("repository_mode 必须与根目录 yss-project.yaml 一致");
|
|
19
|
+
const contract = verifyContextSnapshot(value.context_snapshot, { root });
|
|
20
|
+
const changedRefs = [...value.changes.added, ...value.changes.updated, ...value.changes.deprecated];
|
|
21
|
+
resolveContextTermRefs(contract, changedRefs);
|
|
22
|
+
if (new Set(changedRefs).size !== changedRefs.length) throw new TypeError("changes 中的术语引用不得跨分类重复");
|
|
23
|
+
if (value.repository_mode === "project-instance" && value.status !== "reconciled") throw new TypeError("project-instance 工作单元只有 status=reconciled 才允许批准或进入下一步");
|
|
24
|
+
if (value.status === "reconciled" && value.unresolved_terms.length) throw new TypeError("reconciled 不得包含 unresolved_terms");
|
|
25
|
+
if (value.status === "blocked" && value.unresolved_terms.length === 0) throw new TypeError("blocked 必须说明 unresolved_terms");
|
|
26
|
+
if (value.status === "not-applicable" && (value.repository_mode !== "template-source" || !value.reason)) throw new TypeError("not-applicable 只适用于 template-source 且必须说明 reason");
|
|
27
|
+
for (const ref of value.evidence_refs) {
|
|
28
|
+
if (/^https?:\/\//.test(ref)) continue;
|
|
29
|
+
const resolved = path.resolve(root, ref);
|
|
30
|
+
if (!resolved.startsWith(`${root}${path.sep}`) && resolved !== root) throw new TypeError(`evidence_ref 越出仓库: ${ref}`);
|
|
31
|
+
if (!existsSync(resolved)) throw new TypeError(`evidence_ref 不可读: ${ref}`);
|
|
32
|
+
}
|
|
33
|
+
return contract;
|
|
34
|
+
}
|