create-yss-spec 3.4.7 → 3.4.8
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 +1 -1
- package/template/.agents/skills/.strategic-design-skills-manifest.json +1 -1
- package/template/.agents/skills/code-review/references/yss-review-standards.md +22 -0
- package/template/.agents/skills/lombok/SKILL.md +1 -1
- package/template/.agents/skills/mapstruct/SKILL.md +3 -1
- package/template/.agents/skills/yss-application/references/application-layer-guide.md +7 -0
- package/template/.agents/skills/yss-backend-spec-review/SKILL.md +38 -0
- package/template/.agents/skills/yss-backend-spec-review/references/check-inputs.md +21 -0
- package/template/.agents/skills/yss-backend-spec-review/references/remediation-workflow.md +48 -0
- package/template/.agents/skills/yss-backend-spec-review/tests/routing.test.mjs +19 -0
- package/template/.agents/skills/yss-ddd-scaffold-generator/SKILL.md +5 -3
- package/template/.agents/skills/yss-ddd-scaffold-generator/assets/templates/java/architecture-rules-test.java.template +60 -23
- package/template/.agents/skills/yss-ddd-scaffold-generator/assets/templates/pom/bootstrap-pom.xml.template +6 -0
- package/template/.agents/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +1 -1
- package/template/.agents/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +4 -2
- package/template/.agents/skills/yss-ddd-scaffold-generator/references/engineering-baseline.md +7 -1
- package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +6 -10
- package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +12 -16
- package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +36 -87
- package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +4 -3
- package/template/.agents/skills/yss-domain/references/domain-layer-guide.md +8 -1
- package/template/.agents/skills/yss-implementation-contract-compiler/references/boundaries.md +5 -0
- package/template/.agents/skills/yss-layered-mvc-scaffold-generator/assets/templates/architecture-rules-test.java.template +66 -0
- package/template/.agents/skills/yss-layered-mvc-scaffold-generator/scripts/generate_scaffold.mjs +4 -2
- package/template/.agents/skills/yss-mybatis/SKILL.md +4 -0
- package/template/.agents/skills/yss-repository/references/profiles/target-domain-model.md +10 -2
- package/template/.agents/skills/yss-web-controller/references/web-adapter-layer-guide.md +8 -0
- package/template/.codex/skills/code-review/references/yss-review-standards.md +22 -0
- package/template/.codex/skills/lombok/SKILL.md +1 -1
- package/template/.codex/skills/mapstruct/SKILL.md +3 -1
- package/template/.codex/skills/yss-application/references/application-layer-guide.md +7 -0
- package/template/.codex/skills/yss-backend-spec-review/SKILL.md +38 -0
- package/template/.codex/skills/yss-backend-spec-review/references/check-inputs.md +21 -0
- package/template/.codex/skills/yss-backend-spec-review/references/remediation-workflow.md +48 -0
- package/template/.codex/skills/yss-backend-spec-review/tests/routing.test.mjs +19 -0
- package/template/.codex/skills/yss-ddd-scaffold-generator/SKILL.md +5 -3
- package/template/.codex/skills/yss-ddd-scaffold-generator/assets/templates/java/architecture-rules-test.java.template +60 -23
- package/template/.codex/skills/yss-ddd-scaffold-generator/assets/templates/pom/bootstrap-pom.xml.template +6 -0
- package/template/.codex/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +1 -1
- package/template/.codex/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +4 -2
- package/template/.codex/skills/yss-ddd-scaffold-generator/references/engineering-baseline.md +7 -1
- package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +6 -10
- package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +12 -16
- package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +36 -87
- package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +4 -3
- package/template/.codex/skills/yss-domain/references/domain-layer-guide.md +8 -1
- package/template/.codex/skills/yss-implementation-contract-compiler/references/boundaries.md +5 -0
- package/template/.codex/skills/yss-layered-mvc-scaffold-generator/assets/templates/architecture-rules-test.java.template +66 -0
- package/template/.codex/skills/yss-layered-mvc-scaffold-generator/scripts/generate_scaffold.mjs +4 -2
- package/template/.codex/skills/yss-mybatis/SKILL.md +4 -0
- package/template/.codex/skills/yss-repository/references/profiles/target-domain-model.md +10 -2
- package/template/.codex/skills/yss-web-controller/references/web-adapter-layer-guide.md +8 -0
- package/template/.cursor/skills/code-review/references/yss-review-standards.md +22 -0
- package/template/.cursor/skills/lombok/SKILL.md +1 -1
- package/template/.cursor/skills/mapstruct/SKILL.md +3 -1
- package/template/.cursor/skills/yss-application/references/application-layer-guide.md +7 -0
- package/template/.cursor/skills/yss-backend-spec-review/SKILL.md +38 -0
- package/template/.cursor/skills/yss-backend-spec-review/references/check-inputs.md +21 -0
- package/template/.cursor/skills/yss-backend-spec-review/references/remediation-workflow.md +48 -0
- package/template/.cursor/skills/yss-backend-spec-review/tests/routing.test.mjs +19 -0
- package/template/.cursor/skills/yss-ddd-scaffold-generator/SKILL.md +5 -3
- package/template/.cursor/skills/yss-ddd-scaffold-generator/assets/templates/java/architecture-rules-test.java.template +60 -23
- package/template/.cursor/skills/yss-ddd-scaffold-generator/assets/templates/pom/bootstrap-pom.xml.template +6 -0
- package/template/.cursor/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +1 -1
- package/template/.cursor/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +4 -2
- package/template/.cursor/skills/yss-ddd-scaffold-generator/references/engineering-baseline.md +7 -1
- package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +6 -10
- package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +12 -16
- package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +36 -87
- package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +4 -3
- package/template/.cursor/skills/yss-domain/references/domain-layer-guide.md +8 -1
- package/template/.cursor/skills/yss-implementation-contract-compiler/references/boundaries.md +5 -0
- package/template/.cursor/skills/yss-layered-mvc-scaffold-generator/assets/templates/architecture-rules-test.java.template +66 -0
- package/template/.cursor/skills/yss-layered-mvc-scaffold-generator/scripts/generate_scaffold.mjs +4 -2
- package/template/.cursor/skills/yss-mybatis/SKILL.md +4 -0
- package/template/.cursor/skills/yss-repository/references/profiles/target-domain-model.md +10 -2
- package/template/.cursor/skills/yss-web-controller/references/web-adapter-layer-guide.md +8 -0
- package/template/.pi/skills/code-review/references/yss-review-standards.md +22 -0
- package/template/.pi/skills/lombok/SKILL.md +1 -1
- package/template/.pi/skills/mapstruct/SKILL.md +3 -1
- package/template/.pi/skills/yss-application/references/application-layer-guide.md +7 -0
- package/template/.pi/skills/yss-backend-spec-review/SKILL.md +38 -0
- package/template/.pi/skills/yss-backend-spec-review/references/check-inputs.md +21 -0
- package/template/.pi/skills/yss-backend-spec-review/references/remediation-workflow.md +48 -0
- package/template/.pi/skills/yss-backend-spec-review/tests/routing.test.mjs +19 -0
- package/template/.pi/skills/yss-ddd-scaffold-generator/SKILL.md +5 -3
- package/template/.pi/skills/yss-ddd-scaffold-generator/assets/templates/java/architecture-rules-test.java.template +60 -23
- package/template/.pi/skills/yss-ddd-scaffold-generator/assets/templates/pom/bootstrap-pom.xml.template +6 -0
- package/template/.pi/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +1 -1
- package/template/.pi/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +4 -2
- package/template/.pi/skills/yss-ddd-scaffold-generator/references/engineering-baseline.md +7 -1
- package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +6 -10
- package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +12 -16
- package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +36 -87
- package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +4 -3
- package/template/.pi/skills/yss-domain/references/domain-layer-guide.md +8 -1
- package/template/.pi/skills/yss-implementation-contract-compiler/references/boundaries.md +5 -0
- package/template/.pi/skills/yss-layered-mvc-scaffold-generator/assets/templates/architecture-rules-test.java.template +66 -0
- package/template/.pi/skills/yss-layered-mvc-scaffold-generator/scripts/generate_scaffold.mjs +4 -2
- package/template/.pi/skills/yss-mybatis/SKILL.md +4 -0
- package/template/.pi/skills/yss-repository/references/profiles/target-domain-model.md +10 -2
- package/template/.pi/skills/yss-web-controller/references/web-adapter-layer-guide.md +8 -0
- package/template/docs/agents/yss-skill-registry.yaml +13 -0
- package/template/docs/engineering/backend-platforms.md +14 -0
- package/template/docs/process/schemas/digital-human-task-package.schema.json +657 -100
- package/template/docs/process/schemas/slice-implementation-contract-v3.schema.json +41 -0
- package/template/scripts/lib/backend-platform-provenance.mjs +1 -1
- package/template/scripts/lib/backend-platform-verification.mjs +2 -2
- package/template/scripts/lib/backend-platform.mjs +62 -0
- package/template/scripts/lib/backend-review.mjs +102 -0
- package/template/scripts/lib/first-slice-artifacts.mjs +47 -0
- package/template/scripts/lib/first-slice-tests.mjs +24 -0
- package/template/scripts/lib/lifecycle-transition.mjs +6 -0
- package/template/scripts/lib/maintenance-candidate.mjs +160 -0
- package/template/scripts/lib/slice-execution-preflight.mjs +43 -0
- package/template/scripts/slice-contract +7 -3
- package/template/scripts/verify-backend-platform-matrix +8 -2
- package/template/skills-lock.json +28 -16
- package/template/yss-public-skills.json +4 -2
- package/template.manifest.json +0 -1
- package/template.snapshot.json +5 -5
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { compileDefaultImplementationContract } from '../../../../scripts/lib/implementation-contract-compiler.mjs';
|
|
4
|
+
|
|
5
|
+
test('existing-project audit compiles without inventing a platform or loading an implementer as Reviewer', () => {
|
|
6
|
+
const result = compileDefaultImplementationContract({ recipeIds: ['backend.spec-conformance'] });
|
|
7
|
+
assert.equal(result.status, 'draft');
|
|
8
|
+
assert.deepEqual([...result.required_skills].sort(), ['alibaba-java-code-style', 'yss-backend-spec-review']);
|
|
9
|
+
assert.equal(result.architecture_identity, undefined);
|
|
10
|
+
assert.equal(result.component_bindings, undefined);
|
|
11
|
+
assert.ok(result.non_expanding_dependencies.some(row => row.skill === 'code-review' && row.type === 'review-only'));
|
|
12
|
+
assert.ok(result.non_expanding_dependencies.some(row => row.skill === 'yss-implementation-contract-compiler' && row.type === 'coordination-only'));
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('ordinary code-style capability does not silently start full-project remediation', () => {
|
|
16
|
+
const result = compileDefaultImplementationContract({ requiredCapabilities: ['quality.java-code-style'] });
|
|
17
|
+
assert.deepEqual(result.required_skills, ['alibaba-java-code-style']);
|
|
18
|
+
assert.equal(result.required_skills.includes('yss-backend-spec-review'), false);
|
|
19
|
+
});
|
|
@@ -41,7 +41,7 @@ description: 用于生成完整的 YSS DDD 多模块后端脚手架。当用户
|
|
|
41
41
|
2. 优先运行 `node scripts/generate_and_verify_scaffold.mjs`,在同一个受控工作流中生成骨架并执行真实 Maven 验证。`generate_scaffold.mjs` 只是底层生成原语,单独返回 0 不代表脚手架完成。
|
|
42
42
|
3. 检查生成的模块名、POM、机械启动入口、基础配置文件和包路径。
|
|
43
43
|
4. 受控工作流必须在生成项目根目录实际执行 `./mvnw validate`、`./mvnw test` 和 `./mvnw package`;三条命令全部返回 0 后才能报告完成。
|
|
44
|
-
5. 三条 Wrapper 命令通过只得到 `empty-scaffold-verified`。如需声称已满足下游技能的首切片就绪条件,必须使用 `node scripts/run_first_slice_verification.mjs` 校验批准且版本当前的 Slice Implementation Contract
|
|
44
|
+
5. 三条 Wrapper 命令通过只得到 `empty-scaffold-verified`。如需声称已满足下游技能的首切片就绪条件,必须使用 `node scripts/run_first_slice_verification.mjs` 校验批准且版本当前的 Slice Implementation Contract、完整分层产物、当前合同 freshness 与根 Wrapper;只有验证器成功更新 Manifest 后才得到 `first-slice-verified`。
|
|
45
45
|
|
|
46
46
|
受控验证命令由本 skill 的 `node scripts/run_scaffold_verification.mjs` 固定执行;验证器先检查 `.yss/scaffold-generation.json` 的合同元数据和 Wrapper、Java、项目级 Maven settings/profile、仓库凭据是否就绪,再在指定 evidence 目录写入每条命令的 stdout/stderr、`exit_code`、`failure_category`、耗时、执行时间和 `scaffold-verification.json`。仓库或凭据失败归为 `repository-access`,与 `project-model`、`compilation`、`bootstrap-entrypoint`、`test-failure`、`packaging` 分开;任何一条命令失败或未执行都必须阻断。
|
|
47
47
|
|
|
@@ -72,7 +72,9 @@ node scripts/generate_and_verify_scaffold.mjs \
|
|
|
72
72
|
```bash
|
|
73
73
|
node scripts/run_first_slice_verification.mjs \
|
|
74
74
|
--project-root /path/to/implementation-repo/my-service \
|
|
75
|
-
--slice-contract-file /path/to/approved-slice-contract.
|
|
75
|
+
--slice-contract-file /path/to/approved-slice-contract.yaml \
|
|
76
|
+
--contract-root /path/to/contract-repository \
|
|
77
|
+
--approval-ref docs/approved-checkpoint.yaml \
|
|
76
78
|
--evidence-dir /path/to/evidence/first-slice
|
|
77
79
|
```
|
|
78
80
|
|
|
@@ -104,7 +106,7 @@ node scripts/run_first_slice_verification.mjs \
|
|
|
104
106
|
- 新脚手架只接受统一 Project Scaffold Contract schema v4,并必须以原始字节摘要绑定批准且当前的 Technical Design、Data Architecture Decision v1、API Contract Decision v1 和真实工程合同批准记录。API `required` 必须闭包绑定同一 OpenAPI YAML 字节的 Validation、独立 Review、Freeze 与工程批准;`not-applicable` 必须绑定评估、明确原因和证据且禁止空占位资产。历史 schema v3/v2 仅用于 Manifest 只读验证和补齐 API 对账后的恢复审计,不用于新生成。
|
|
105
107
|
- DDD 固定 `target-domain-model`、`mybatis-plus`、H2 验证、`web` DTO、`yss-internal`;平台与 Validation 命名空间由共享清单约束。普通 MyBatis、独立 client module、client-in-domain 和其他旧架构仍为 `unsupported`。
|
|
106
108
|
- 运行生成器必须传入 `--contract-file`;生成器会校验合同 `status=approved`、`current_version`、`primary_skill`、`controlled-generation`、实际输出路径和固定三条验证命令,不接受仅凭任意字符串引用的放行。
|
|
107
|
-
- 生成项目必须写入 Manifest v4 `.yss/scaffold-generation.json`,记录技术、数据与 API 设计门禁、架构选择及 digest、生成器、合同 digest、Target Profile、模块闭包、模板 digest、下游完整
|
|
109
|
+
- 生成项目必须写入 Manifest v4 `.yss/scaffold-generation.json`,记录技术、数据与 API 设计门禁、架构选择及 digest、生成器、合同 digest、Target Profile、模块闭包、模板 digest、下游完整 当前合同 freshness、generator-owned 文件 hash、严格 `generation_policy` 和完成等级;清单缺失或不一致时不得交给后续 实现合同编译器。
|
|
108
110
|
- `first-slice-verified` 只能由 `run_first_slice_verification.mjs` 写入。手工改 Manifest、只生成 Controller、只通过局部模块测试或仅有结构扫描都不能升级完成等级。
|
|
109
111
|
- 严禁把领域规则、状态机、权限、事务、复杂查询、错误映射、业务字段或用户可见行为塞进脚手架生成步骤。`./mvnw validate`、输出目录存在或“生成成功”都不等于生命周期批准、架构放行或 `ready-for-agent`。
|
|
110
112
|
- 生命周期脚手架生成必须关闭 `--with-example`,不得把 User CRUD 或业务字段当作样板。生成器严格 `initialize-only`:非空目标、`--force`、旧项目迁移和当前模板升级一律 `unsupported`。未来若支持同一 Target Profile 内的模板升级,必须另行设计和批准,当前不预留可执行承诺。
|
|
@@ -1,36 +1,73 @@
|
|
|
1
1
|
package {{base_package}};
|
|
2
2
|
|
|
3
|
+
import com.tngtech.archunit.core.domain.JavaClass;
|
|
4
|
+
import com.tngtech.archunit.core.domain.JavaClasses;
|
|
5
|
+
import com.tngtech.archunit.core.domain.Dependency;
|
|
3
6
|
import com.tngtech.archunit.core.importer.ImportOption;
|
|
4
7
|
import com.tngtech.archunit.junit.AnalyzeClasses;
|
|
5
8
|
import com.tngtech.archunit.junit.ArchTest;
|
|
6
|
-
import com.tngtech.archunit.lang.ArchRule;
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Target DDD 的最小可执行边界。规则允许空包,首个业务切片加入后立即生效。
|
|
12
|
-
*/
|
|
10
|
+
/** 空骨架可无业务类型;首切片由批准合同传入职责包和必须加载的实际类型。 */
|
|
13
11
|
@AnalyzeClasses(packages = "{{base_package}}", importOptions = ImportOption.DoNotIncludeTests.class)
|
|
14
12
|
class ArchitectureRulesTest {
|
|
13
|
+
private static boolean inPackage(JavaClass type, String prefixes) {
|
|
14
|
+
for (String prefix : prefixes.split(",")) {
|
|
15
|
+
if (!prefix.isEmpty() && (type.getPackageName().equals(prefix)
|
|
16
|
+
|| type.getPackageName().startsWith(prefix + "."))) return true;
|
|
17
|
+
}
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
.should().dependOnClassesThat().resideInAnyPackage(
|
|
20
|
-
"..application..", "..infrastructure..", "..repository..", "..rest..")
|
|
21
|
-
.allowEmptyShould(true);
|
|
21
|
+
private static boolean role(JavaClass type, String name, String defaults) {
|
|
22
|
+
return inPackage(type, defaults) || inPackage(type, System.getProperty("yss.arch." + name, ""));
|
|
23
|
+
}
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
private static boolean controller(JavaClass type) {
|
|
26
|
+
return type.isAnnotatedWith("org.springframework.web.bind.annotation.RestController")
|
|
27
|
+
|| type.isAnnotatedWith("org.springframework.stereotype.Controller")
|
|
28
|
+
|| type.isMetaAnnotatedWith("org.springframework.stereotype.Controller");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private static void reject(boolean forbidden, Dependency dependency) {
|
|
32
|
+
if (forbidden) throw new AssertionError(dependency.getDescription());
|
|
33
|
+
}
|
|
29
34
|
|
|
30
35
|
@ArchTest
|
|
31
|
-
static
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
.
|
|
36
|
+
static void boundaries(JavaClasses classes) {
|
|
37
|
+
for (JavaClass origin : classes) {
|
|
38
|
+
boolean domain = role(origin, "domain", "{{base_package}}.domain");
|
|
39
|
+
boolean application = role(origin, "application", "{{base_package}}.application");
|
|
40
|
+
boolean infrastructure = role(origin, "infrastructure", "{{base_package}}.infrastructure");
|
|
41
|
+
for (Dependency dependency : origin.getDirectDependenciesFromSelf()) {
|
|
42
|
+
JavaClass target = dependency.getTargetClass();
|
|
43
|
+
boolean targetWeb = controller(target) || role(target, "web",
|
|
44
|
+
"{{base_package}}.rest,{{base_package}}.adapter.web");
|
|
45
|
+
boolean targetInfrastructure = role(target, "infrastructure", "{{base_package}}.infrastructure");
|
|
46
|
+
boolean targetDomain = role(target, "domain", "{{base_package}}.domain");
|
|
47
|
+
reject(domain && (targetWeb || targetInfrastructure
|
|
48
|
+
|| role(target, "application", "{{base_package}}.application")
|
|
49
|
+
|| inPackage(target, "org.springframework,org.mybatis,com.baomidou,org.apache.ibatis,"
|
|
50
|
+
+ "com.fasterxml.jackson,io.swagger,javax.persistence,jakarta.persistence,"
|
|
51
|
+
+ "javax.validation,jakarta.validation,javax.servlet,jakarta.servlet,com.yss.cloud.dto,com.yss.cloud.exception")), dependency);
|
|
52
|
+
reject(application && (targetWeb || targetInfrastructure), dependency);
|
|
53
|
+
reject(infrastructure && targetWeb, dependency);
|
|
54
|
+
// Exception translators may translate Domain errors. Controllers may not invoke Domain directly.
|
|
55
|
+
reject(controller(origin) && (targetInfrastructure || targetDomain), dependency);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (Boolean.getBoolean("yss.firstSlice")) {
|
|
59
|
+
if (Boolean.getBoolean("yss.arch.requireController")) {
|
|
60
|
+
boolean foundController = false;
|
|
61
|
+
for (JavaClass type : classes) if (controller(type)) foundController = true;
|
|
62
|
+
if (!foundController) throw new AssertionError("Web slice did not load an actual Controller");
|
|
63
|
+
}
|
|
64
|
+
String required = System.getProperty("yss.arch.requiredTypes", "");
|
|
65
|
+
if (required.isEmpty()) throw new AssertionError("first slice requires contract-bound types");
|
|
66
|
+
for (String name : required.split(",")) {
|
|
67
|
+
boolean found = false;
|
|
68
|
+
for (JavaClass type : classes) if (type.getName().equals(name)) found = true;
|
|
69
|
+
if (!found) throw new AssertionError("architecture scope did not load " + name);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
36
73
|
}
|
|
@@ -57,6 +57,12 @@
|
|
|
57
57
|
<artifactId>h2</artifactId>
|
|
58
58
|
<scope>test</scope>
|
|
59
59
|
</dependency>
|
|
60
|
+
<dependency>
|
|
61
|
+
<groupId>org.projectlombok</groupId>
|
|
62
|
+
<artifactId>lombok</artifactId>
|
|
63
|
+
<version>${lombok.version}</version>
|
|
64
|
+
<scope>test</scope>
|
|
65
|
+
</dependency>
|
|
60
66
|
</dependencies>
|
|
61
67
|
|
|
62
68
|
<profiles>
|
|
@@ -111,6 +111,6 @@ Domain Error -> Application propagation/translation -> Web Exception Translator
|
|
|
111
111
|
|
|
112
112
|
- `generated`:文件已原子生成,尚未完成 Wrapper 验证。
|
|
113
113
|
- `empty-scaffold-verified`:根目录 `./mvnw validate/test/package` 全部通过。
|
|
114
|
-
- `first-slice-verified`:批准且版本当前的 golden first slice
|
|
114
|
+
- `first-slice-verified`:批准且版本当前的 golden first slice 已具备合同声明的职责类型、适用分层与实际执行的行为测试(分页、事务、组件不适用时不生成空实现);当前批准合同与技能依据无漂移,且根 Wrapper 全部通过。该状态只能由 `scripts/run_first_slice_verification.mjs` 写入。
|
|
115
115
|
|
|
116
116
|
只有首切片验证器可以把 Manifest 从 `empty-scaffold-verified` 升级到最后一级;手工写值、局部测试或结构扫描均无效。只有最后一级可以声明已满足下游 YSS skills 的首切片就绪条件。
|
|
@@ -118,11 +118,13 @@ metadata-service/
|
|
|
118
118
|
```bash
|
|
119
119
|
node .agents/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs \
|
|
120
120
|
--project-root /path/to/implementation-repo/metadata-service \
|
|
121
|
-
--slice-contract-file /path/to/approved-slice-contract.
|
|
121
|
+
--slice-contract-file /path/to/approved-slice-contract.yaml \
|
|
122
|
+
--contract-root /path/to/contract-repository \
|
|
123
|
+
--approval-ref docs/approved-checkpoint.yaml \
|
|
122
124
|
--evidence-dir /path/to/evidence/first-slice
|
|
123
125
|
```
|
|
124
126
|
|
|
125
|
-
|
|
127
|
+
只有该验证器确认合同、全层产物、当前合同 freshness 与根 Wrapper 全部通过,并更新 Manifest 后,才能标记 `first-slice-verified`。
|
|
126
128
|
|
|
127
129
|
目标目录必须不存在。`--force`、旧项目迁移和当前模板升级均为 `unsupported`。旧项目继续按原工程维护;现代化改造必须单独立项、先评估再逐切片迁移。
|
|
128
130
|
|
package/template/.codex/skills/yss-ddd-scaffold-generator/references/engineering-baseline.md
CHANGED
|
@@ -40,7 +40,7 @@ infrastructure-+
|
|
|
40
40
|
- Maven 内部仓库通过命名 profile `yss-internal` 与环境变量注入;共享模板不写内部 URL 或凭据。
|
|
41
41
|
- Wrapper 必须带 checksum;DEBUG 和 MyBatis stdout SQL 只允许在 `application-local.yml`。
|
|
42
42
|
- `./mvnw validate`、`./mvnw test`、`./mvnw package` 全部成功只得到 `empty-scaffold-verified`。
|
|
43
|
-
- 只有批准且版本当前的 golden first slice 真实组合 Domain/Application/Infrastructure/Web
|
|
43
|
+
- 只有批准且版本当前的 golden first slice 真实组合 Domain/Application/Infrastructure/Web、通过当前批准合同的技能/编译依据 freshness 校验与根 Wrapper,才能由 `run_first_slice_verification.mjs` 写入 `first-slice-verified`。
|
|
44
44
|
|
|
45
45
|
## 下游权威技能
|
|
46
46
|
|
|
@@ -51,3 +51,9 @@ infrastructure-+
|
|
|
51
51
|
- 横切实现约束:`yss-validation` + `mapstruct` + `lombok` + `alibaba-java-code-style`
|
|
52
52
|
|
|
53
53
|
业务代码必须回到 `yss-implementation-contract-compiler`,消费批准且版本当前的 Slice Implementation Contract,并采用 `behavior-tdd`;脚手架只生成机械工程资产。
|
|
54
|
+
|
|
55
|
+
## 生成来源与当前基线
|
|
56
|
+
|
|
57
|
+
Manifest 的平台与文件摘要保留生成时来源;后续批准的平台迁移以当前工程基线、仓库登记和迁移证据重新绑定,不覆盖生成历史伪造当前性。历史 Manifest 与当前合同无法对账时停止晋级。
|
|
58
|
+
|
|
59
|
+
空骨架允许未出现业务类型;首切片按批准 backend.first_slice.artifacts 的职责、具体源码类型和测试证据核验,不以固定目录或后缀作为合规证明。进入业务验证时启用 yss.firstSlice=true,必须实际发现 Controller(命中 Web 时)及声明的业务层。
|
package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs
CHANGED
|
@@ -228,16 +228,12 @@ test("golden first slice 组合 Domain/Application/Infrastructure/Web 且不发
|
|
|
228
228
|
assert.match(queryAdapter, /implements QualityRuleQueryPort/);
|
|
229
229
|
});
|
|
230
230
|
|
|
231
|
-
test("golden
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
await chmod(path.join(project, "mvnw"), 0o755);
|
|
238
|
-
const result = await runFirstSliceVerification(project, path.join(root, "first-slice-evidence"), sliceContract, process.env);
|
|
239
|
-
assert.equal(result.status, "passed", JSON.stringify(result, null, 2));
|
|
240
|
-
assert.equal(JSON.parse(await readFile(path.join(project, ".yss/scaffold-generation.json"), "utf8")).completion_level, "first-slice-verified");
|
|
231
|
+
test("golden 布局与可用凭据不能替代完整批准的 Slice 合同", async (t) => {
|
|
232
|
+
const { root, project, sliceContract } = await prepareGoldenProject(t);
|
|
233
|
+
const result = await runFirstSliceVerification(project, path.join(root, "first-slice-evidence"), sliceContract, process.env, { contractRoot: root });
|
|
234
|
+
assert.equal(result.status, "failed");
|
|
235
|
+
assert.ok(result.contract_failures.includes("slice-execution-not-authorized"));
|
|
236
|
+
assert.equal(result.commands.length, 0);
|
|
241
237
|
});
|
|
242
238
|
|
|
243
239
|
test("真实 YSS exception starter 的自动配置、优先级、三类映射和序列化均受保护", { timeout: 300_000 }, async (t) => {
|
package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs
CHANGED
|
@@ -126,19 +126,18 @@ const environment = { ...process.env, JAVA_HOME: "/fixture/java", YSS_MAVEN_REPO
|
|
|
126
126
|
|
|
127
127
|
test("refuses first-slice promotion when a required persistence seam is absent", async () => {
|
|
128
128
|
const data = await fixture({ omitGatewayImpl: true });
|
|
129
|
-
const report = await runFirstSliceVerification(data.project, data.evidence, data.contractPath, environment);
|
|
129
|
+
const report = await runFirstSliceVerification(data.project, data.evidence, data.contractPath, environment, { contractRoot: data.root });
|
|
130
130
|
assert.equal(report.status, "failed");
|
|
131
|
-
assert.ok(report.missing_artifacts.includes("
|
|
131
|
+
assert.ok(report.missing_artifacts.includes("backend.first_slice.artifacts-required"));
|
|
132
132
|
const manifest = JSON.parse(await readFile(data.manifestPath, "utf8"));
|
|
133
133
|
assert.equal(manifest.completion_level, "empty-scaffold-verified");
|
|
134
134
|
});
|
|
135
135
|
|
|
136
|
-
test("
|
|
136
|
+
test("missing generation provenance does not substitute for current approved contract", async () => {
|
|
137
137
|
const data = await fixture({ omitReadiness: true });
|
|
138
|
-
const report = await runFirstSliceVerification(data.project, data.evidence, data.contractPath, environment);
|
|
138
|
+
const report = await runFirstSliceVerification(data.project, data.evidence, data.contractPath, environment, { contractRoot: data.root });
|
|
139
139
|
assert.equal(report.status, "failed");
|
|
140
|
-
assert.ok(report.
|
|
141
|
-
assert.ok(report.downstream_skill_drift.includes("compiler_contract:digest-missing"));
|
|
140
|
+
assert.ok(report.contract_failures.includes("slice-execution-not-authorized"));
|
|
142
141
|
const manifest = JSON.parse(await readFile(data.manifestPath, "utf8"));
|
|
143
142
|
assert.equal(manifest.completion_level, "empty-scaffold-verified");
|
|
144
143
|
});
|
|
@@ -148,20 +147,17 @@ test("refuses first-slice promotion when a work unit references a stale contract
|
|
|
148
147
|
const contract = JSON.parse(await readFile(data.contractPath, "utf8"));
|
|
149
148
|
contract.work_units[0].contract_version = 0;
|
|
150
149
|
await writeFile(data.contractPath, JSON.stringify(contract));
|
|
151
|
-
const report = await runFirstSliceVerification(data.project, data.evidence, data.contractPath, environment);
|
|
150
|
+
const report = await runFirstSliceVerification(data.project, data.evidence, data.contractPath, environment, { contractRoot: data.root });
|
|
152
151
|
assert.equal(report.status, "failed");
|
|
153
152
|
assert.ok(report.contract_failures.includes("work-unit-contract-version-mismatch:slice-backend"));
|
|
154
153
|
});
|
|
155
154
|
|
|
156
|
-
test("
|
|
155
|
+
test("fake wrapper success and an approved status cannot promote an incomplete v2 contract", async () => {
|
|
157
156
|
const data = await fixture();
|
|
158
|
-
const report = await runFirstSliceVerification(data.project, data.evidence, data.contractPath, environment);
|
|
159
|
-
assert.equal(report.status, "
|
|
160
|
-
assert.
|
|
161
|
-
assert.deepEqual(report.commands
|
|
157
|
+
const report = await runFirstSliceVerification(data.project, data.evidence, data.contractPath, environment, { contractRoot: data.root });
|
|
158
|
+
assert.equal(report.status, "failed");
|
|
159
|
+
assert.ok(report.contract_failures.includes("slice-execution-not-authorized"));
|
|
160
|
+
assert.deepEqual(report.commands, []);
|
|
162
161
|
const manifest = JSON.parse(await readFile(data.manifestPath, "utf8"));
|
|
163
|
-
assert.equal(manifest.completion_level, "
|
|
164
|
-
assert.equal(manifest.first_slice_contract.contract_id, "slice-1");
|
|
165
|
-
assert.equal(manifest.first_slice_contract.slice_id, "quality-rule-first-slice");
|
|
166
|
-
assert.equal(JSON.parse(await readFile(path.join(data.evidence, "first-slice-verification.json"), "utf8")).status, "passed");
|
|
162
|
+
assert.equal(manifest.completion_level, "empty-scaffold-verified");
|
|
167
163
|
});
|
package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs
CHANGED
|
@@ -8,33 +8,15 @@ import { fileURLToPath } from "node:url";
|
|
|
8
8
|
|
|
9
9
|
import { run as runScaffoldVerification } from "./run_scaffold_verification.mjs";
|
|
10
10
|
import { assertArchitectureAgreement } from "../../../../scripts/lib/backend-architecture.mjs";
|
|
11
|
-
import {
|
|
11
|
+
import { inspectSliceContract } from "../../../../scripts/lib/slice-execution-preflight.mjs";
|
|
12
|
+
import { selectSliceWorkUnit } from "../../../../scripts/lib/slice-contract.mjs";
|
|
13
|
+
import { verifyFirstSliceTests } from "../../../../scripts/lib/first-slice-tests.mjs";
|
|
14
|
+
import { inspectFirstSliceArtifacts } from "../../../../scripts/lib/first-slice-artifacts.mjs";
|
|
12
15
|
|
|
13
16
|
const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
14
17
|
const REPOSITORY_ROOT = path.resolve(SCRIPT_DIR, "../../../..");
|
|
15
18
|
const REQUIRED_SKILLS = ["yss-domain", "yss-application", "yss-repository", "yss-mybatis", "yss-web-controller", "yss-dto", "yss-exception", "yss-validation", "mapstruct", "lombok", "alibaba-java-code-style"];
|
|
16
19
|
const REQUIRED_LAYERS = ["domain", "application", "infrastructure", "web"];
|
|
17
|
-
const ARTIFACT_CHECKS = [
|
|
18
|
-
["domain-model", /-domain\/src\/main\/java\/.+\/domain\/.+\/model\/.+\.java$/],
|
|
19
|
-
["domain-gateway", /-domain\/src\/main\/java\/.+\/domain\/.+\/gateway\/.+Gateway\.java$/],
|
|
20
|
-
["domain-behavior-test", /-domain\/src\/test\/java\/.+(?:Test|Tests)\.java$/],
|
|
21
|
-
["application-command-or-query", /-application\/src\/main\/java\/.+\/application\/(?:command|query)\/.+\.java$/],
|
|
22
|
-
["application-result", /-application\/src\/main\/java\/.+\/application\/result\/.+Result\.java$/],
|
|
23
|
-
["application-service", /-application\/src\/main\/java\/.+\/application\/service\/.+Service\.java$/],
|
|
24
|
-
["application-service-implementation", /-application\/src\/main\/java\/.+\/application\/service\/impl\/.+ServiceImpl\.java$/],
|
|
25
|
-
["application-behavior-test", /-application\/src\/test\/java\/.+(?:Test|Tests)\.java$/],
|
|
26
|
-
["infrastructure-po", /-infrastructure\/src\/main\/java\/.+\/infrastructure\/persistence\/po\/.+PO\.java$/],
|
|
27
|
-
["infrastructure-repository", /-infrastructure\/src\/main\/java\/.+\/infrastructure\/persistence\/repository\/.+Repository\.java$/],
|
|
28
|
-
["infrastructure-convertor", /-infrastructure\/src\/main\/java\/.+\/infrastructure\/persistence\/convertor\/.+Convertor\.java$/],
|
|
29
|
-
["infrastructure-gateway-implementation", /-infrastructure\/src\/main\/java\/.+\/infrastructure\/persistence\/gateway\/.+GatewayImpl\.java$/],
|
|
30
|
-
["infrastructure-query-adapter", /-infrastructure\/src\/main\/java\/.+\/infrastructure\/query\/adapter\/.+QueryAdapter\.java$/],
|
|
31
|
-
["infrastructure-integration-test", /-infrastructure\/src\/test\/java\/.+(?:Test|Tests)\.java$/],
|
|
32
|
-
["web-controller", /-adapter\/.+-web\/src\/main\/java\/.+\/rest\/.+Controller\.java$/],
|
|
33
|
-
["web-convertor", /-adapter\/.+-web\/src\/main\/java\/.+\/rest\/convertor\/.+WebConvertor\.java$/],
|
|
34
|
-
["web-request", /-adapter\/.+-web\/src\/main\/java\/.+\/rest\/dto\/request\/.+Request\.java$/],
|
|
35
|
-
["web-response", /-adapter\/.+-web\/src\/main\/java\/.+\/rest\/dto\/response\/.+Response\.java$/],
|
|
36
|
-
["web-contract-test", /-adapter\/.+-web\/src\/test\/java\/.+(?:Test|Tests)\.java$/]
|
|
37
|
-
];
|
|
38
20
|
|
|
39
21
|
const isoNow = () => new Date().toISOString();
|
|
40
22
|
const sha256 = (content) => createHash("sha256").update(content).digest("hex");
|
|
@@ -62,59 +44,7 @@ async function writeJsonAtomic(target, value) {
|
|
|
62
44
|
await writeFile(temporary, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
63
45
|
await rename(temporary, target);
|
|
64
46
|
}
|
|
65
|
-
function
|
|
66
|
-
const reasons = [];
|
|
67
|
-
if (contract.schema_version !== 2) reasons.push("slice-contract-schema");
|
|
68
|
-
if (contract.status !== "approved") reasons.push("slice-contract-not-approved");
|
|
69
|
-
for (const field of ["contract_id", "contract_version", "slice_id"]) if (contract[field] === undefined || contract[field] === null || contract[field] === "") reasons.push(`slice-contract-${field}-missing`);
|
|
70
|
-
if (contract.readiness?.blockers?.length) reasons.push("slice-contract-has-blockers");
|
|
71
|
-
if (contract.readiness?.stale_inputs?.length) reasons.push("slice-contract-has-stale-inputs");
|
|
72
|
-
const actualSkills = new Set([...(contract.common?.required_skills ?? []), ...(contract.backend?.required_skills ?? [])]);
|
|
73
|
-
for (const skill of requiredSkills) if (!actualSkills.has(skill)) reasons.push(`required-skill-missing:${skill}`);
|
|
74
|
-
const actualLayers = new Set(contract.backend?.affected_layers ?? []);
|
|
75
|
-
for (const layer of requiredLayers) if (!actualLayers.has(layer)) reasons.push(`required-layer-missing:${layer}`);
|
|
76
|
-
if (!Array.isArray(contract.work_units) || !contract.work_units.length) reasons.push("slice-contract-work-units-missing");
|
|
77
|
-
else for (const [index, unit] of contract.work_units.entries()) {
|
|
78
|
-
const id = unit.id || `index-${index}`;
|
|
79
|
-
if (unit.contract_id !== contract.contract_id) reasons.push(`work-unit-contract-id-mismatch:${id}`);
|
|
80
|
-
if (unit.contract_version !== contract.contract_version) reasons.push(`work-unit-contract-version-mismatch:${id}`);
|
|
81
|
-
if (!unit.work_unit?.primary_skill) reasons.push(`work-unit-primary-skill-missing:${id}`);
|
|
82
|
-
}
|
|
83
|
-
return reasons;
|
|
84
|
-
}
|
|
85
|
-
async function downstreamDrift(manifest, requiredSkills = REQUIRED_SKILLS) {
|
|
86
|
-
const drift = [];
|
|
87
|
-
const recordedSkills = manifest.readiness?.downstream_skills ?? {};
|
|
88
|
-
for (const skill of requiredSkills) {
|
|
89
|
-
const expected = recordedSkills[skill];
|
|
90
|
-
if (typeof expected !== "string" || !/^[a-f0-9]{64}$/.test(expected)) {
|
|
91
|
-
drift.push(`${skill}:digest-missing`);
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
const root = path.join(REPOSITORY_ROOT, ".agents", "skills", skill);
|
|
95
|
-
if (!await isFile(path.join(root, "SKILL.md"))) drift.push(`${skill}:unavailable`);
|
|
96
|
-
else if (await treeDigest(root) !== expected) drift.push(`${skill}:digest-mismatch`);
|
|
97
|
-
}
|
|
98
|
-
const contracts = manifest.readiness?.contracts ?? {};
|
|
99
|
-
const contractFiles = {
|
|
100
|
-
compiler_contract: path.join(REPOSITORY_ROOT, ".agents", "skills", "yss-implementation-contract-compiler", "references", "compiler-contract.yaml")
|
|
101
|
-
};
|
|
102
|
-
if (manifest.architecture_family === "layered-mvc") {
|
|
103
|
-
contractFiles.architecture_profiles = path.join(REPOSITORY_ROOT, "docs/agents/backend-architecture-profiles.md");
|
|
104
|
-
} else contractFiles.engineering_baseline = path.join(REPOSITORY_ROOT, ".agents", "skills", "yss-ddd-scaffold-generator", "references", "engineering-baseline.md");
|
|
105
|
-
for (const [name, target] of Object.entries(contractFiles)) {
|
|
106
|
-
const expected = contracts[name];
|
|
107
|
-
if (typeof expected !== "string" || !/^[a-f0-9]{64}$/.test(expected)) {
|
|
108
|
-
drift.push(`${name}:digest-missing`);
|
|
109
|
-
continue;
|
|
110
|
-
}
|
|
111
|
-
if (!await isFile(target)) drift.push(`${name}:unavailable`);
|
|
112
|
-
else if (sha256(await readFile(target)) !== expected) drift.push(`${name}:digest-mismatch`);
|
|
113
|
-
}
|
|
114
|
-
return drift;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export async function runFirstSliceVerification(projectRoot, evidenceDir, sliceContractFile, environment = process.env) {
|
|
47
|
+
export async function runFirstSliceVerification(projectRoot, evidenceDir, sliceContractFile, environment = process.env, options = {}) {
|
|
118
48
|
projectRoot = path.resolve(projectRoot);
|
|
119
49
|
evidenceDir = path.resolve(evidenceDir);
|
|
120
50
|
sliceContractFile = path.resolve(sliceContractFile);
|
|
@@ -123,20 +53,33 @@ export async function runFirstSliceVerification(projectRoot, evidenceDir, sliceC
|
|
|
123
53
|
if (!await isFile(manifestPath)) throw new Error(`missing scaffold manifest: ${manifestPath}`);
|
|
124
54
|
if (!await isFile(sliceContractFile)) throw new Error(`missing Slice Implementation Contract: ${sliceContractFile}`);
|
|
125
55
|
const manifest = JSON.parse(await readFile(manifestPath, "utf8"));
|
|
126
|
-
const
|
|
56
|
+
const contractRoot = path.resolve(options.contractRoot || projectRoot);
|
|
57
|
+
let contract, preflight;
|
|
58
|
+
try {
|
|
59
|
+
const loaded = inspectSliceContract(path.relative(contractRoot, sliceContractFile), {
|
|
60
|
+
root: contractRoot, approval_ref: options.approvalRef, work_unit_id: options.workUnit
|
|
61
|
+
});
|
|
62
|
+
preflight = loaded.report;
|
|
63
|
+
contract = selectSliceWorkUnit(loaded.contract, options.workUnit);
|
|
64
|
+
} catch (error) {
|
|
65
|
+
preflight = { execution_allowed: false, blockers: [error.message] };
|
|
66
|
+
contract = {};
|
|
67
|
+
}
|
|
127
68
|
if (manifest.platform_verification === "candidate") throw new Error("backend-platform: candidate scaffold cannot become first-slice-verified");
|
|
128
|
-
const
|
|
129
|
-
|
|
69
|
+
const contractFailures = [...preflight.blockers];
|
|
70
|
+
if (!preflight.execution_allowed) contractFailures.push("slice-execution-not-authorized");
|
|
71
|
+
if (!(contract.common?.project_roots || []).some(ref => path.resolve(contractRoot, ref) === projectRoot)) contractFailures.push("slice-project-root-mismatch");
|
|
130
72
|
try {
|
|
131
73
|
const identity = contract.architecture_identity ?? contract.resolution?.architecture_identity;
|
|
132
|
-
assertArchitectureAgreement(identity, { manifest });
|
|
74
|
+
if (identity?.source_kind !== "existing-registration") assertArchitectureAgreement(identity, { manifest });
|
|
133
75
|
for (const unit of contract.work_units ?? []) assertArchitectureAgreement(identity, { work_unit: unit.architecture_identity });
|
|
134
76
|
} catch (error) { contractFailures.push(`architecture-identity:${error.message}`); }
|
|
135
77
|
if (![2, 3, 4].includes(manifest.schema_version) || manifest.completion_level !== "empty-scaffold-verified") contractFailures.push("scaffold-not-empty-scaffold-verified");
|
|
136
78
|
if (manifest.schema_version === 3 && manifest.legacy_reconciliation?.status !== "approved") contractFailures.push("legacy-scaffold-reconciliation-required");
|
|
137
|
-
const
|
|
138
|
-
const missingArtifacts =
|
|
139
|
-
|
|
79
|
+
const artifacts = inspectFirstSliceArtifacts(contract, projectRoot);
|
|
80
|
+
const missingArtifacts = artifacts.failures;
|
|
81
|
+
// Generation-time Skill hashes are provenance. Current authority was checked by execution preflight.
|
|
82
|
+
const skillDrift = [];
|
|
140
83
|
if (contractFailures.length || missingArtifacts.length || skillDrift.length) {
|
|
141
84
|
const report = { verification_mode: "first-slice", project_root: projectRoot, slice_contract_ref: sliceContractFile, scaffold_manifest_ref: manifestPath, generated_at: isoNow(), status: "failed", completion_level: manifest.completion_level, contract_failures: contractFailures, missing_artifacts: missingArtifacts, downstream_skill_drift: skillDrift, commands: [] };
|
|
142
85
|
await writeJsonAtomic(reportPath, report);
|
|
@@ -144,9 +87,15 @@ export async function runFirstSliceVerification(projectRoot, evidenceDir, sliceC
|
|
|
144
87
|
}
|
|
145
88
|
|
|
146
89
|
const mavenEvidence = path.join(evidenceDir, "maven");
|
|
147
|
-
const
|
|
148
|
-
const
|
|
149
|
-
const
|
|
90
|
+
const startedAt = Date.now();
|
|
91
|
+
const wrapper = await runScaffoldVerification(projectRoot, mavenEvidence, environment, { firstSlice: true, systemProperties: artifacts.properties });
|
|
92
|
+
const mvc = manifest.architecture_family === "layered-mvc";
|
|
93
|
+
const tests = wrapper.status === "passed" ? verifyFirstSliceTests(contract, projectRoot, startedAt, {
|
|
94
|
+
module: `${manifest.project_name}-${mvc ? "server" : "bootstrap"}`,
|
|
95
|
+
type: `${manifest.base_package}.${mvc ? "architecture.LayeredMvcArchitectureTest" : "ArchitectureRulesTest"}`
|
|
96
|
+
}) : { status: "not-executed", failures: [], evidence: [] };
|
|
97
|
+
const passed = wrapper.status === "passed" && tests.status === "passed";
|
|
98
|
+
const report = { verification_mode: "first-slice", project_root: projectRoot, slice_contract_ref: sliceContractFile, scaffold_manifest_ref: manifestPath, generated_at: isoNow(), status: passed ? "passed" : "failed", test_execution: tests, failure_category: wrapper.failure_category || (tests.status === "failed" ? "first-slice-tests" : null), completion_level: passed ? "first-slice-verified" : manifest.completion_level, contract_failures: [], missing_artifacts: [], downstream_skill_drift: [], commands: wrapper.commands };
|
|
150
99
|
await writeJsonAtomic(reportPath, report);
|
|
151
100
|
if (passed) {
|
|
152
101
|
manifest.completion_level = "first-slice-verified";
|
|
@@ -162,7 +111,7 @@ function parseArgs(argv) {
|
|
|
162
111
|
const result = {};
|
|
163
112
|
for (let index = 0; index < argv.length; index += 2) {
|
|
164
113
|
const flag = argv[index], value = argv[index + 1];
|
|
165
|
-
if (!["--project-root", "--evidence-dir", "--slice-contract-file"].includes(flag) || !value) throw new Error("usage: run_first_slice_verification.mjs --project-root DIR --evidence-dir DIR --slice-contract-file FILE");
|
|
114
|
+
if (!["--project-root", "--evidence-dir", "--slice-contract-file", "--contract-root", "--approval-ref", "--work-unit"].includes(flag) || !value) throw new Error("usage: run_first_slice_verification.mjs --project-root DIR --evidence-dir DIR --slice-contract-file FILE");
|
|
166
115
|
result[flag.slice(2).replaceAll(/-([a-z])/g, (_, letter) => letter.toUpperCase())] = value;
|
|
167
116
|
}
|
|
168
117
|
if (!result.projectRoot || !result.evidenceDir || !result.sliceContractFile) throw new Error("project root, evidence dir, and slice contract file are required");
|
|
@@ -172,7 +121,7 @@ function parseArgs(argv) {
|
|
|
172
121
|
async function main() {
|
|
173
122
|
try {
|
|
174
123
|
const args = parseArgs(process.argv.slice(2));
|
|
175
|
-
const report = await runFirstSliceVerification(args.projectRoot, args.evidenceDir, args.sliceContractFile);
|
|
124
|
+
const report = await runFirstSliceVerification(args.projectRoot, args.evidenceDir, args.sliceContractFile, process.env, args);
|
|
176
125
|
process.stdout.write(`${report.status}: ${path.join(path.resolve(args.evidenceDir), "first-slice-verification.json")}\n`);
|
|
177
126
|
return report.status === "passed" ? 0 : 1;
|
|
178
127
|
} catch (error) {
|
package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs
CHANGED
|
@@ -62,7 +62,7 @@ function validateManifest(manifest) {
|
|
|
62
62
|
if (manifest.schema_version === 4 ? manifest.current_version !== true : manifest.current_version !== manifest.contract_version) throw new Error("脚手架生成元数据清单不是当前合同版本");
|
|
63
63
|
if (JSON.stringify(manifest.verification_commands) !== JSON.stringify(COMMANDS)) throw new Error("脚手架生成元数据清单验证命令不符合固定合同");
|
|
64
64
|
}
|
|
65
|
-
export async function run(projectRoot, evidenceDir, environment = process.env, { timeoutMs = 0, signal, platformOptions = {}, firstSlice = false } = {}) {
|
|
65
|
+
export async function run(projectRoot, evidenceDir, environment = process.env, { timeoutMs = 0, signal, platformOptions = {}, firstSlice = false, systemProperties = {} } = {}) {
|
|
66
66
|
const wrapper = path.join(projectRoot, "mvnw"), manifestPath = path.join(projectRoot, ".yss", "scaffold-generation.json");
|
|
67
67
|
if (!await isFile(wrapper)) throw new Error(`项目根目录缺少 Maven wrapper: ${wrapper}`);
|
|
68
68
|
if (!await isFile(manifestPath)) throw new Error(`项目根目录缺少脚手架生成元数据清单: ${manifestPath}`);
|
|
@@ -114,13 +114,14 @@ export async function run(projectRoot, evidenceDir, environment = process.env, {
|
|
|
114
114
|
const stderrPath = path.join(evidenceDir, `mvnw-${phase}.stderr.log`);
|
|
115
115
|
const startedAt = isoNow();
|
|
116
116
|
const started = process.hrtime.bigint();
|
|
117
|
-
const
|
|
117
|
+
const args = [phase, ...Object.entries(systemProperties).map(([key, value]) => `-D${key}=${value}`)];
|
|
118
|
+
const execution = await runCommand(wrapper, args, { cwd: projectRoot, env: environment, timeoutMs, signal, stdoutFile: stdoutPath, stderrFile: stderrPath, secrets: [environment.MAVEN_REPO_USERNAME, environment.MAVEN_REPO_PASSWORD], progress: true });
|
|
118
119
|
const outcome = { ...execution, exitCode: execution.status };
|
|
119
120
|
const durationMs = Number(process.hrtime.bigint() - started) / 1e6;
|
|
120
121
|
await writeText(stdoutPath, redactSecrets(outcome.stdout, environment));
|
|
121
122
|
await writeText(stderrPath, redactSecrets(outcome.stderr, environment));
|
|
122
123
|
commands.push({
|
|
123
|
-
command: `./mvnw ${
|
|
124
|
+
command: `./mvnw ${args.join(" ")}`,
|
|
124
125
|
phase,
|
|
125
126
|
exit_code: outcome.exitCode,
|
|
126
127
|
failure_category: outcome.termination || classifyFailure(phase, outcome),
|
|
@@ -62,7 +62,7 @@ public interface QualityTemplateGateway {
|
|
|
62
62
|
- `*VO`、`PageResult`、Repository PO。
|
|
63
63
|
- MyBatis、Spring MVC、Jackson、Swagger 类型。
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
展示分页、列表和投影查询由 Application Query Port 定义,Infrastructure 实现。
|
|
66
66
|
|
|
67
67
|
## 5. Domain Error
|
|
68
68
|
|
|
@@ -99,3 +99,10 @@ Web Exception Translator 再将它映射为批准的状态码、错误码和消
|
|
|
99
99
|
- 返回 YSS Skill Execution Result,包含 Domain 文件、行为测试、ArchUnit 结果及实际 `./mvnw ...` 证据。
|
|
100
100
|
- 新聚合、不变量、状态机或跨上下文影响进入 `new_impacts` 并暂停。
|
|
101
101
|
- 检测到旧架构时返回 `unsupported`;如需现代化,退出 scaffold 工作流并单独立项评估。
|
|
102
|
+
|
|
103
|
+
## 聚合创建与重建
|
|
104
|
+
|
|
105
|
+
- 创建入口执行当前创建规则;持久化重建入口恢复已批准状态并校验结构不变量,不重复发送创建事件、生成身份或执行外部调用。两者不得通过公开任意 setter 混为一体。
|
|
106
|
+
- 值对象按值相等;聚合按稳定身份相等。集合对外只读,修改经业务方法;Java 8 使用普通类与防御性复制,不从示例推导 record 等高版本语法。
|
|
107
|
+
- 领域错误携带稳定标识与参数;公开消息和 HTTP 状态由 Web 翻译。测试通过公开行为覆盖非法状态、重复动作和重建后语义一致性。
|
|
108
|
+
- 为业务决策加载一个或一组聚合仍可使用 Domain Gateway;展示列表、分页、排序、统计和投影走 Application Query Port。返回 List 本身不是违规,须按调用目的判定。
|
package/template/.codex/skills/yss-implementation-contract-compiler/references/boundaries.md
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
| Capability | Primary skill | Task modes |
|
|
20
20
|
| --- | --- | --- |
|
|
21
|
+
| `governance.backend-spec-conformance` | `yss-backend-spec-review` | `guidance`, `troubleshooting`, `review-input` |
|
|
21
22
|
| `writing.lifecycle-document` | `i-have-adhd` | `guidance` |
|
|
22
23
|
| `architecture.technical-design` | `yss-technical-design` | `guidance`, `integration`, `review-input` |
|
|
23
24
|
| `architecture.mvc-design` | `yss-technical-design` | `guidance`, `integration`, `review-input` |
|
|
@@ -57,6 +58,7 @@
|
|
|
57
58
|
|
|
58
59
|
| Recipe | Capabilities |
|
|
59
60
|
| --- | --- |
|
|
61
|
+
| `backend.spec-conformance` | `governance.backend-spec-conformance`, `quality.java-code-style` |
|
|
60
62
|
| `backend.ddd-domain-behavior` | `layer.domain`, `layer.application`, `quality.java-code-style` |
|
|
61
63
|
| `backend.ddd-persistence-mybatis` | `layer.persistence`, `framework.mybatis`, `codegen.mapstruct`, `codegen.lombok`, `quality.java-code-style` |
|
|
62
64
|
| `backend.ddd-http-api` | `layer.web-adapter`, `contract.dto-wire`, `contract.request-validation`, `contract.error-mapping`, `codegen.mapstruct`, `codegen.lombok`, `quality.java-code-style` |
|
|
@@ -106,6 +108,9 @@
|
|
|
106
108
|
|
|
107
109
|
| Skill | Dependency | Type | Condition |
|
|
108
110
|
| --- | --- | --- | --- |
|
|
111
|
+
| `yss-backend-spec-review` | `alibaba-java-code-style` | `context-required` | — |
|
|
112
|
+
| `yss-backend-spec-review` | `yss-implementation-contract-compiler` | `coordination-only` | — |
|
|
113
|
+
| `yss-backend-spec-review` | `code-review` | `review-only` | — |
|
|
109
114
|
| `prototype-review` | `i-have-adhd` | `context-conditional` | `lifecycle-document-output` |
|
|
110
115
|
| `yss-implementation-contract-compiler` | `i-have-adhd` | `context-conditional` | `lifecycle-document-output` |
|
|
111
116
|
| `yss-openapi-governance` | `i-have-adhd` | `context-conditional` | `lifecycle-document-output` |
|