create-yss-spec 2.2.1 → 2.2.2
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/README.md +2 -2
- package/package.json +1 -1
- package/src/cli.js +56 -8
- package/template/AGENTS.md +1 -13
- package/template/docs/adr/README.md +7 -0
- package/template/docs/agents/skills-maintenance.md +1 -1
- package/template/docs/process/implementation-repo-integration.md +1 -1
- package/template/scripts/verify-governance-release +7 -5
- package/template.manifest.json +30 -7
- package/template.snapshot.json +5 -4
- package/template/docs/adr/0001-product-design-prototype-entrypoint.md +0 -54
- package/template/docs/adr/0002-yss-project-repository-mode.md +0 -3
- package/template/docs/adr/0003-machine-readable-lifecycle-registry.md +0 -5
- package/template/docs/adr/0006-stable-lifecycle-ids-and-generated-structure.md +0 -5
- package/template/docs/adr/0007-lifecycle-native-work-units.md +0 -5
package/README.md
CHANGED
|
@@ -47,7 +47,7 @@ npx create-yss-spec@latest --version
|
|
|
47
47
|
- init / sync 不把模板源治理笔记、wiki、审查证据、源仓 CI / Cloud 环境和公开发布清单写入项目实例;attach 会带上 `yss-public-skills.json` 供 `verify-template` 使用
|
|
48
48
|
- 空 gitlink / detached HEAD / git-submodule 挂载点 fail closed,`--force` 也不能覆盖
|
|
49
49
|
|
|
50
|
-
本次模板适配按 `2.2.
|
|
50
|
+
本次模板适配按 `2.2.2` 发布,绑定 `yss-spec-project-template@51189ca`。`templateCommit` 会写入实例 metadata;“最新模板”指用户执行的 `npx create-yss-spec@latest` 所携带的最新已发布快照,CLI 运行时不会拉取模板仓库。实例会带上 YSS 前端技能叠加层(如 `ytable-usage`、`formily-foundation`、`yss-page-module-development`)、`.cursorrules` 与 `.agents/rules/yss-ai-skills.md`;审查临时目录 `docs/.scratch/` 与已退役的 `high-fidelity-html-prototype` 等独立入口不进入快照。
|
|
51
51
|
|
|
52
52
|
## 接管已有项目
|
|
53
53
|
|
|
@@ -100,7 +100,7 @@ npx create-yss-spec@latest sync --dry-run
|
|
|
100
100
|
- 对已知的 Spec / Ticket 旧路径执行一次性迁移
|
|
101
101
|
- 迁移目标已存在且内容不一致时停止,不静默覆盖
|
|
102
102
|
- `.gitmodules`、gitlink(mode `160000`)和 `apps/` 下已挂载实现仓是用户资产,不创建、不覆盖、不删除
|
|
103
|
-
- init / sync
|
|
103
|
+
- init / sync 保持实例边界:不复制 `wiki/`、`.github/`、`.template-source/`、源仓库 ADR、Cursor Cloud 环境配置、`docs/reviews/` 或根 `package.json`;共享 `scripts/`、`scripts/vendor/`、`.nvmrc` 和 `.gitignore` 属于实例门禁所需资产。attach 结束后会执行 `scripts/sync-skills --check`、`scripts/update-skill-lock --check` 和 `scripts/verify-template`,门禁失败会回滚文件和 metadata
|
|
104
104
|
|
|
105
105
|
```bash
|
|
106
106
|
npx create-yss-spec@latest sync --target-dir . --dry-run
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -18,6 +18,7 @@ const TEMPLATE_MANIFEST = JSON.parse(TEMPLATE_MANIFEST_TEXT);
|
|
|
18
18
|
const ROOT_EXCLUDED_ENTRIES = new Set(TEMPLATE_MANIFEST.excludeRootEntries);
|
|
19
19
|
const ROOT_EXCLUDED_FILES = new Set(TEMPLATE_MANIFEST.excludeRootFiles);
|
|
20
20
|
const EXCLUDED_RELATIVE_PATHS = new Set(TEMPLATE_MANIFEST.excludePaths);
|
|
21
|
+
const ALLOWED_RELATIVE_PATHS = new Set(TEMPLATE_MANIFEST.allowFiles || []);
|
|
21
22
|
const INIT_EXCLUDED_ROOT_ENTRIES = new Set(
|
|
22
23
|
TEMPLATE_MANIFEST.initExcludeRootEntries || [],
|
|
23
24
|
);
|
|
@@ -47,6 +48,7 @@ const AGENT_SKILL_ROOTS = [
|
|
|
47
48
|
];
|
|
48
49
|
const GITLINK_MODE = "160000";
|
|
49
50
|
const UNMANAGED_USER_PATHS = new Set([".gitmodules"]);
|
|
51
|
+
const GITLINK_PATH_CACHE = new Map();
|
|
50
52
|
const INSTANCE_FORBIDDEN_PATHS = [
|
|
51
53
|
".template-source",
|
|
52
54
|
".github",
|
|
@@ -96,6 +98,9 @@ function readTemplateSnapshot() {
|
|
|
96
98
|
if (!/^[0-9a-f]{64}$/.test(snapshot.snapshotHash || "")) {
|
|
97
99
|
throw new Error("模板快照必须包含 64 位 snapshotHash");
|
|
98
100
|
}
|
|
101
|
+
if (snapshot.manifestHash !== TEMPLATE_MANIFEST_VERSION) {
|
|
102
|
+
throw new Error("模板快照与当前 template.manifest.json 不一致,请重新构建 CLI 包");
|
|
103
|
+
}
|
|
99
104
|
|
|
100
105
|
if (
|
|
101
106
|
snapshot.encodedPaths !== undefined &&
|
|
@@ -322,8 +327,20 @@ function collectGitRoots(start) {
|
|
|
322
327
|
}
|
|
323
328
|
let current = path.resolve(start);
|
|
324
329
|
while (true) {
|
|
330
|
+
const gitPath = path.join(current, ".git");
|
|
331
|
+
let validGitEntry = false;
|
|
332
|
+
try {
|
|
333
|
+
const gitStat = fs.lstatSync(gitPath);
|
|
334
|
+
validGitEntry = gitStat.isDirectory()
|
|
335
|
+
? fs.readdirSync(gitPath).length > 0
|
|
336
|
+
: gitStat.isFile();
|
|
337
|
+
} catch (error) {
|
|
338
|
+
if (error.code !== "ENOENT") {
|
|
339
|
+
throw error;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
325
342
|
if (
|
|
326
|
-
|
|
343
|
+
validGitEntry ||
|
|
327
344
|
fs.existsSync(path.join(current, ".gitmodules"))
|
|
328
345
|
) {
|
|
329
346
|
roots.push(current);
|
|
@@ -378,7 +395,7 @@ function gitLsFilesStage(repoRoot, relativePath) {
|
|
|
378
395
|
const result = spawnSync(
|
|
379
396
|
"git",
|
|
380
397
|
["-C", repoRoot, "ls-files", "--stage", "--", relativePath],
|
|
381
|
-
{ encoding: "utf8" },
|
|
398
|
+
{ encoding: "utf8", timeout: 5000 },
|
|
382
399
|
);
|
|
383
400
|
if (result.status !== 0) {
|
|
384
401
|
return null;
|
|
@@ -394,11 +411,34 @@ function gitLsFilesStage(repoRoot, relativePath) {
|
|
|
394
411
|
return { mode: match[1], sha: match[2], stage: match[3], path: match[4] };
|
|
395
412
|
}
|
|
396
413
|
|
|
414
|
+
function gitlinkPaths(repoRoot) {
|
|
415
|
+
const resolvedRoot = path.resolve(repoRoot);
|
|
416
|
+
if (GITLINK_PATH_CACHE.has(resolvedRoot)) {
|
|
417
|
+
return GITLINK_PATH_CACHE.get(resolvedRoot);
|
|
418
|
+
}
|
|
419
|
+
const result = spawnSync(
|
|
420
|
+
"git",
|
|
421
|
+
["-C", resolvedRoot, "ls-files", "--stage", "-z"],
|
|
422
|
+
{ encoding: "utf8", timeout: 5000 },
|
|
423
|
+
);
|
|
424
|
+
const paths = new Set();
|
|
425
|
+
if (result.status === 0) {
|
|
426
|
+
for (const record of (result.stdout || "").split("\0")) {
|
|
427
|
+
const match = record.match(/^(160000)\s+[0-9a-f]+\s+\d+\s+(.+)$/i);
|
|
428
|
+
if (match) {
|
|
429
|
+
paths.add(match[2].replaceAll("\\", "/"));
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
GITLINK_PATH_CACHE.set(resolvedRoot, paths);
|
|
434
|
+
return paths;
|
|
435
|
+
}
|
|
436
|
+
|
|
397
437
|
function gitShowSuperproject(cwd) {
|
|
398
438
|
const result = spawnSync(
|
|
399
439
|
"git",
|
|
400
440
|
["-C", cwd, "rev-parse", "--show-superproject-working-tree"],
|
|
401
|
-
{ encoding: "utf8" },
|
|
441
|
+
{ encoding: "utf8", timeout: 5000 },
|
|
402
442
|
);
|
|
403
443
|
if (result.status !== 0) {
|
|
404
444
|
return "";
|
|
@@ -410,7 +450,7 @@ function gitAbbrevRef(cwd) {
|
|
|
410
450
|
const result = spawnSync(
|
|
411
451
|
"git",
|
|
412
452
|
["-C", cwd, "rev-parse", "--abbrev-ref", "HEAD"],
|
|
413
|
-
{ encoding: "utf8" },
|
|
453
|
+
{ encoding: "utf8", timeout: 5000 },
|
|
414
454
|
);
|
|
415
455
|
if (result.status !== 0) {
|
|
416
456
|
return null;
|
|
@@ -432,21 +472,22 @@ function isEmptyDir(dir) {
|
|
|
432
472
|
|
|
433
473
|
function isGitSubmoduleMount(repoRoot, targetPathValue) {
|
|
434
474
|
const relative = posixRelative(repoRoot, path.resolve(targetPathValue));
|
|
435
|
-
if (!relative || relative.startsWith("..") || path.isAbsolute(relative)) {
|
|
475
|
+
if (!relative || relative === "." || relative.startsWith("..") || path.isAbsolute(relative)) {
|
|
436
476
|
return false;
|
|
437
477
|
}
|
|
438
478
|
if (readGitmodules(repoRoot).some((item) => item.path === relative)) {
|
|
439
479
|
return true;
|
|
440
480
|
}
|
|
441
|
-
|
|
442
|
-
return staged?.mode === GITLINK_MODE;
|
|
481
|
+
return gitlinkPaths(repoRoot).has(relative);
|
|
443
482
|
}
|
|
444
483
|
|
|
445
484
|
function inspectCheckoutState(repoRoot, targetPathValue) {
|
|
446
485
|
const resolved = path.resolve(targetPathValue);
|
|
447
486
|
const relative = posixRelative(path.resolve(repoRoot), resolved);
|
|
448
487
|
const inRepo = Boolean(relative) && !relative.startsWith("..");
|
|
449
|
-
const staged = inRepo
|
|
488
|
+
const staged = inRepo && gitlinkPaths(repoRoot).has(relative)
|
|
489
|
+
? { mode: GITLINK_MODE }
|
|
490
|
+
: null;
|
|
450
491
|
const listed = inRepo
|
|
451
492
|
? readGitmodules(repoRoot).some((item) => item.path === relative)
|
|
452
493
|
: false;
|
|
@@ -577,6 +618,11 @@ function targetPath(targetDir, relativePath) {
|
|
|
577
618
|
|
|
578
619
|
function shouldExcludeRelativePath(relativePath, mode = "managed") {
|
|
579
620
|
const normalized = normalizeRelativePath(relativePath);
|
|
621
|
+
if ([...ALLOWED_RELATIVE_PATHS].some(
|
|
622
|
+
(allowedPath) => allowedPath === normalized || allowedPath.startsWith(`${normalized}/`),
|
|
623
|
+
)) {
|
|
624
|
+
return false;
|
|
625
|
+
}
|
|
580
626
|
return EXCLUDED_RELATIVE_PATHS.has(normalized) ||
|
|
581
627
|
(mode === "init" &&
|
|
582
628
|
(INIT_EXCLUDED_RELATIVE_PATHS.has(normalized) ||
|
|
@@ -1505,6 +1551,7 @@ function gitDirtyWarning(targetDir) {
|
|
|
1505
1551
|
const probe = spawnSync("git", ["rev-parse", "--is-inside-work-tree"], {
|
|
1506
1552
|
cwd: targetDir,
|
|
1507
1553
|
encoding: "utf8",
|
|
1554
|
+
timeout: 5000,
|
|
1508
1555
|
});
|
|
1509
1556
|
if (probe.status !== 0 || probe.stdout.trim() !== "true") {
|
|
1510
1557
|
return null;
|
|
@@ -1512,6 +1559,7 @@ function gitDirtyWarning(targetDir) {
|
|
|
1512
1559
|
const status = spawnSync("git", ["status", "--porcelain"], {
|
|
1513
1560
|
cwd: targetDir,
|
|
1514
1561
|
encoding: "utf8",
|
|
1562
|
+
timeout: 5000,
|
|
1515
1563
|
});
|
|
1516
1564
|
if (status.status === 0 && status.stdout.trim()) {
|
|
1517
1565
|
return "警告:目标 Git worktree 存在未提交改动;CLI 不会自动 stash 或提交,请在结果后检查 git diff / git status";
|
package/template/AGENTS.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
- `repository_mode: project-instance`:按产品研发生命周期分诊任务。
|
|
11
11
|
- 文件缺失、schema 版本不支持或模式值非法时,停止路由并执行迁移检查;不根据目录、Git 远程或占位符猜测身份。
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
仓库身份契约由根目录 `yss-project.yaml` 和本文件共同声明。
|
|
14
14
|
|
|
15
15
|
## 2. 单一事实来源
|
|
16
16
|
|
|
@@ -108,15 +108,3 @@ README、用户指南、根目录 `CLAUDE.md` 和其他说明文档只引用或
|
|
|
108
108
|
## 12. 测试质量基线
|
|
109
109
|
|
|
110
110
|
模板推荐值为 Domain / Application `>= 90%`、API `>= 80%`、前端组件 `>= 75%`、已明确的关键流程 `100% E2E`。只有项目实例在测试策略中明确采纳或覆盖后才构成 CI 门禁;未定义关键流程清单时,不声称其 E2E 覆盖率达到 100%。
|
|
111
|
-
|
|
112
|
-
## Cursor Cloud specific instructions
|
|
113
|
-
|
|
114
|
-
本仓库是 `template-source` Harness / 研发管理仓库,没有前端 / 后端运行时应用;可运行、可测试的表面只有 Node 工具链与治理校验脚本。
|
|
115
|
-
|
|
116
|
-
- 依赖与工具链:Node `>=22 <27`(`.nvmrc` 固定 22);`pnpm` 通过 `packageManager` 字段由 corepack 自动切换到 `10.15.0`,无需手工切换。Node 依赖只装在 `.template-source/tooling/node`,仓库根没有 `package.json`。本仓 wiki-root 为 `.template-source/wiki`。
|
|
117
|
-
- 测试:`pnpm --dir .template-source/tooling/node test`(`node --test`,共 16 个用例)。
|
|
118
|
-
- 构建 / lint:`pnpm --dir .template-source/tooling/node build:vendor` 与 `check:vendor` 维护 `scripts/vendor/*.mjs`;顶层 lint 是 `verify-template` 内对所有脚本执行的 `node --check`。
|
|
119
|
-
- 完整发布门禁(相当于"运行应用"):`scripts/verify-template`,成功输出 `模板发布校验通过`。它串联证据索引、`pnpm test`、`check:vendor` 和全部 `scripts/verify-*` 场景校验。
|
|
120
|
-
- 非显然的坑:`scripts/verify-lifecycle-registry`(及其对应测试 `Node lifecycle registry verifier ...`)会 shell out 到 `python3` 并 `import jsonschema` 做 JSON Schema 校验。缺少该 Python 模块时测试会以 `ModuleNotFoundError: No module named 'jsonschema'` 失败,而不是代码问题;update script 已负责 `pip3 install jsonschema`。
|
|
121
|
-
- `pnpm install` 输出的 `Ignored build scripts: esbuild` 警告可忽略:esbuild 0.28.2 在本平台自带二进制,`build:vendor` / `check:vendor` 无需 `pnpm approve-builds`。
|
|
122
|
-
- 常用只读入口:`scripts/repository-mode`(返回仓库身份)、`scripts/generate-lifecycle-artifacts`(从 `docs/process/lifecycle-registry.yaml` 派生产物,输出应无 diff)、`scripts/export-yss-skills --output <dir>`(导出公开技能)。
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# 产品架构决策
|
|
2
|
+
|
|
3
|
+
本目录只记录当前项目自身的架构决策,不承载 `yss-spec-project-template` 模板源仓库的治理决策。
|
|
4
|
+
|
|
5
|
+
创建 ADR 时使用 [`docs/templates/adr-template.md`](../templates/adr-template.md),并为每条决策补充状态、背景、取舍、后果和相关资产。
|
|
6
|
+
|
|
7
|
+
模板源仓库的 ADR 保存在 `.template-source/adr/`,不会随 `create-yss-spec` 进入 `project-instance`。
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
scripts/verify-template
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
模板源维护引入或更新分发到实例的 Node 工具时,维护侧依赖、构建和 vendor 校验只在模板源治理区及 CI 中执行;实例门禁不得安装依赖或重建 vendor。实例只消费已提交的 `scripts/lib/*.mjs` 与 `scripts/vendor/*.mjs`,具体维护侧命令和治理决策不属于项目实例文档。
|
|
61
61
|
|
|
62
62
|
6. 需要重新加载技能的客户端在变更落地后重启或刷新项目。
|
|
63
63
|
|
|
@@ -30,7 +30,7 @@ apps/
|
|
|
30
30
|
- **frontend**:依赖安装、测试、type-check 与构建优先使用 `pnpm`(例如 `pnpm test`、`pnpm type-check`、`pnpm build`)。不要默认 `npm` 或 `yarn`。
|
|
31
31
|
- **backend**:校验、测试与编译优先使用项目根 `./mvnw`(例如 `./mvnw validate`、`./mvnw test`、`./mvnw package`)。不要默认裸 `mvn`。
|
|
32
32
|
- Ticket、Slice Implementation Contract、CI 和 Review 证据必须写下实际执行的上述命令。既有仓库缺少 `pnpm` 或 Maven Wrapper 时,先记录受控例外、替代命令和责任人,再执行。
|
|
33
|
-
- 本模板源仓库没有产品 frontend / backend
|
|
33
|
+
- 本模板源仓库没有产品 frontend / backend 运行时;模板源维护侧的 Node 校验命令和环境约束只记录在 `.template-source/` 治理区及 CI 配置中,不属于项目实例实现命令。
|
|
34
34
|
|
|
35
35
|
## 1.3 `repository_scope: git-submodule`
|
|
36
36
|
|
|
@@ -25,11 +25,13 @@ if (isTemplateSource(root)) {
|
|
|
25
25
|
if (status === "proposed" && !/尚未|未实现|不构成|不能把/.test(body)) failures.push(`${relativePath} 的 proposed ADR 缺少未实施/非当前门禁说明`);
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
if (isTemplateSource(root)) {
|
|
29
|
+
for (const [relativePath, marker] of Object.entries({
|
|
30
|
+
".template-source/adr/0003-machine-readable-lifecycle-registry.md": "状态:部分实施(Phase 1)",
|
|
31
|
+
".template-source/adr/0006-stable-lifecycle-ids-and-generated-structure.md": "状态:部分实施(Phase 1)"
|
|
32
|
+
})) {
|
|
33
|
+
if (!read(relativePath).includes(marker)) failures.push(`${relativePath} 未声明当前实现状态`);
|
|
34
|
+
}
|
|
33
35
|
}
|
|
34
36
|
if (!read("scripts/verify-lifecycle-scenarios").includes("ROUTE_REGISTRY_IDS")) failures.push("生命周期场景未校验稳定 ID 映射");
|
|
35
37
|
if (failures.length > 0) {
|
package/template.manifest.json
CHANGED
|
@@ -1,4 +1,31 @@
|
|
|
1
1
|
{
|
|
2
|
+
"allowRootEntries": [
|
|
3
|
+
".agents",
|
|
4
|
+
".claude",
|
|
5
|
+
".codex",
|
|
6
|
+
".cursor",
|
|
7
|
+
".hermes",
|
|
8
|
+
".pi",
|
|
9
|
+
".qoder",
|
|
10
|
+
".trae",
|
|
11
|
+
"docs",
|
|
12
|
+
"scripts"
|
|
13
|
+
],
|
|
14
|
+
"allowRootFiles": [
|
|
15
|
+
".cursorrules",
|
|
16
|
+
".gitignore",
|
|
17
|
+
".nvmrc",
|
|
18
|
+
"AGENTS.md",
|
|
19
|
+
"CLAUDE.md",
|
|
20
|
+
"CONTEXT.md",
|
|
21
|
+
"README.md",
|
|
22
|
+
"skills-lock.json",
|
|
23
|
+
"yss-project.yaml",
|
|
24
|
+
"yss-public-skills.json"
|
|
25
|
+
],
|
|
26
|
+
"allowFiles": [
|
|
27
|
+
"docs/adr/README.md"
|
|
28
|
+
],
|
|
2
29
|
"excludeRootEntries": [
|
|
3
30
|
".git",
|
|
4
31
|
".codegraph",
|
|
@@ -27,7 +54,8 @@
|
|
|
27
54
|
".pi/settings.json",
|
|
28
55
|
".cursor/environment.json",
|
|
29
56
|
"docs/.scratch",
|
|
30
|
-
"docs/reviews"
|
|
57
|
+
"docs/reviews",
|
|
58
|
+
"docs/adr"
|
|
31
59
|
],
|
|
32
60
|
"renderPaths": [
|
|
33
61
|
"AGENTS.md",
|
|
@@ -37,13 +65,8 @@
|
|
|
37
65
|
"exampleDocPaths": [
|
|
38
66
|
"docs/discovery/IDEATION.md"
|
|
39
67
|
],
|
|
40
|
-
"initExcludeRootEntries": [
|
|
41
|
-
"scripts",
|
|
42
|
-
"wiki"
|
|
43
|
-
],
|
|
68
|
+
"initExcludeRootEntries": [],
|
|
44
69
|
"initExcludeRootFiles": [
|
|
45
|
-
".gitignore",
|
|
46
|
-
".nvmrc",
|
|
47
70
|
"yss-public-skills.json"
|
|
48
71
|
],
|
|
49
72
|
"initExcludePaths": []
|
package/template.snapshot.json
CHANGED
|
@@ -3,14 +3,15 @@
|
|
|
3
3
|
"templateName": "yss-spec-project-template",
|
|
4
4
|
"templateSource": "github:iloveZzz/yss-spec-project-template",
|
|
5
5
|
"templateRepository": "https://github.com/iloveZzz/yss-spec-project-template.git",
|
|
6
|
-
"requestedRef": "
|
|
7
|
-
"templateCommit": "
|
|
6
|
+
"requestedRef": "51189cae987209ff9076a3336269318f47615d5a",
|
|
7
|
+
"templateCommit": "51189cae987209ff9076a3336269318f47615d5a",
|
|
8
|
+
"manifestHash": "10d161eb3f0e426aeefad5d0b9e6c5522d8a4dbacb9edf6ed64285dcb40b1a65",
|
|
8
9
|
"encodedPaths": {
|
|
9
10
|
".codex/skills/data-analytics/.gitignore": ".codex/skills/data-analytics/__yss_dotfile__.gitignore",
|
|
10
11
|
".codex/skills/product-design/.npmignore": ".codex/skills/product-design/__yss_dotfile__.npmignore",
|
|
11
12
|
".codex/skills/product-design/templates/prototype/.npmrc": ".codex/skills/product-design/templates/prototype/__yss_dotfile__.npmrc",
|
|
12
13
|
".gitignore": "__yss_dotfile__.gitignore"
|
|
13
14
|
},
|
|
14
|
-
"snapshotHash": "
|
|
15
|
-
"generatedAt": "2026-08-
|
|
15
|
+
"snapshotHash": "f3ae9ea041af1b2c07c60e3d9794f22dc20f7c7cd655e95fba442344f124a8b2",
|
|
16
|
+
"generatedAt": "2026-08-25T05:55:41.500Z"
|
|
16
17
|
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
# ADR-0001: 使用 product-design:index 作为产品原型主入口
|
|
2
|
-
|
|
3
|
-
## 状态
|
|
4
|
-
|
|
5
|
-
已接受
|
|
6
|
-
|
|
7
|
-
## 日期
|
|
8
|
-
|
|
9
|
-
2026-07-06
|
|
10
|
-
|
|
11
|
-
## 背景
|
|
12
|
-
|
|
13
|
-
YSS Harness 需要把有 UI 功能的产品原型产出收敛到一个稳定入口,同时保留需求质询、设计系统、Ant Design v6 事实校验、原型评审和用户确认门禁。原有 `product-design-prototype`、`wireframe-prototype`、`high-fidelity-html-prototype` 容易被理解为并列主入口,导致 Agent 在不同阶段选择不同原型工具,难以保证高保真 HTML 原型、AntD v6 校验证据和用户确认记录一致落地。
|
|
14
|
-
|
|
15
|
-
## 决策
|
|
16
|
-
|
|
17
|
-
产品原型产出统一以 `product-design:index` 为主入口。该 skill 只负责路由,实际按 Product Design plugin 规则进入 `$get-context`、`$ideate`、`$prototype`、`$image-to-code`、`$url-to-code`、`$design-qa` 或 `$share` 等 focused skill。
|
|
18
|
-
|
|
19
|
-
`grill-with-docs` 保留为前置需求质询和领域边界澄清工具,不承担 HTML 原型产出。`antd` skill / `antd` CLI 作为 Ant Design v6 组件、demo、token、semantic 和 design.md 的事实来源。YSS 生命周期继续负责 `prototype-review`、用户确认、Spec 回填和 OpenAPI Draft 门禁。
|
|
20
|
-
|
|
21
|
-
## 考虑的替代方案
|
|
22
|
-
|
|
23
|
-
| 方案 | 优点 | 缺点 | 结论 |
|
|
24
|
-
|------|------|------|------|
|
|
25
|
-
| 继续使用多个原型技能并列入口 | 对旧流程改动少 | Agent 路由不稳定,难以统一 AntD v6 高保真 HTML 原型和确认记录 | 放弃 |
|
|
26
|
-
| 只保留 `high-fidelity-html-prototype` | 产物目标直接 | 缺少 Product Design plugin 的上下文、视觉探索、URL / 图片 / 代码来源路由能力 | 放弃 |
|
|
27
|
-
| 以 `product-design:index` 为主入口,只保留仍有迁移价值的兼容入口 | 路由统一,可复用 focused skills,并允许逐步退休冗余入口 | 需要在文档中明确 legacy 边界 | 采纳 |
|
|
28
|
-
|
|
29
|
-
## 后果
|
|
30
|
-
|
|
31
|
-
### 正面影响
|
|
32
|
-
|
|
33
|
-
- 有 UI 的产品原型产出入口统一。
|
|
34
|
-
- 高保真 HTML 原型、AntD CLI 校验证据和用户确认记录成为固定门禁。
|
|
35
|
-
- Figma / Axure / Excalidraw / 截图 / URL 可作为输入来源,而不是主交付格式。
|
|
36
|
-
|
|
37
|
-
### 负面影响
|
|
38
|
-
|
|
39
|
-
- 需要维护完整 `.codex/skills/product-design/` 插件目录,不能只复制 `index/SKILL.md`。
|
|
40
|
-
- 部分旧原型技能在过渡期仍会存在,需要通过流程文档限制使用语境;无继续维护价值的入口直接退休,不保留空壳目录。
|
|
41
|
-
|
|
42
|
-
### 风险
|
|
43
|
-
|
|
44
|
-
- 如果 Agent 未读取 Product Design focused skill,可能误把 `product-design:index` 当作直接产出技能。
|
|
45
|
-
- 如果跳过 `antd` CLI 查询,原型可能偏离 Ant Design v6 组件 API 或 token。
|
|
46
|
-
- 如果缺少用户确认记录,下游 Spec 校准和 OpenAPI Draft 会失去体验冻结依据。
|
|
47
|
-
|
|
48
|
-
## 相关
|
|
49
|
-
|
|
50
|
-
- `AGENTS.md`
|
|
51
|
-
- `docs/design/README.md`
|
|
52
|
-
- `docs/process/lifecycle-artifact-map.md`
|
|
53
|
-
- `docs/design/README.md`
|
|
54
|
-
- `docs/design/templates/prototype-confirmation-template.md`
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
# ADR-0002: 使用 `yss-project.yaml` 声明仓库身份
|
|
2
|
-
|
|
3
|
-
模板源仓库与模板实例仓库共享大部分目录和流程资产,仅靠目录、Git 远程或占位符推断身份会导致 Agent 选错流程。因此在根目录使用 `yss-project.yaml` 作为 CLI 与 Agent 共享的稳定契约,只声明 `schema_version` 和 `repository_mode`:模板源仓库使用 `template-source`,`create-yss-spec` 生成的仓库使用 `project-instance`。项目名称、团队规模和 Tracker 等易变信息不放入该清单,避免形成新的重复配置源。
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
# ADR-0003: 使用机器可读生命周期注册表作为结构化事实源
|
|
2
|
-
|
|
3
|
-
> 状态:部分实施(Phase 1)。本次发布只接受注册表、稳定 ID、结构表和工作单元表的 shadow 校验;数量、索引、依赖图和测试 fixture 的完整生成属于后续提案,未实现前不得作为模板发布能力宣称。
|
|
4
|
-
|
|
5
|
-
YSS 研发操作系统当前在 Markdown 生命周期地图、主控 skill、机器契约、用户指南和场景测试中重复表达阶段、门禁、产物与工作单元,已经出现门禁数量和工作单元数量漂移。Phase 1 引入生命周期注册表,集中保存这些结构事实及稳定标识,并由它派生当前 Markdown 结构表;Markdown 继续承担人工解释和审查,ADR 继续记录取舍,业务语义不被压缩成难读的配置。数量、索引、依赖图和测试 fixture 的自动生成必须在独立 Phase 中与生成器和校验器一并落地,不能只通过 ADR 宣告完成。
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
# ADR-0006: 使用稳定命名空间 ID 并派生生命周期结构资产
|
|
2
|
-
|
|
3
|
-
> 状态:部分实施(Phase 1)。稳定 ID、发布基线和两份结构视图已受管;索引、依赖图和测试 fixture 的完整生成属于后续提案,未实现前不得把人工维护的场景断言称为派生产物。
|
|
4
|
-
|
|
5
|
-
生命周期对象的中文名称、展示顺序和数量会随流程演进而变化,不能继续用“第几个门禁”或表格行数作为兼容身份。Phase 1 为主阶段、门禁、产物、工作单元和证据分别使用 `stage.*`、`gate.*`、`artifact.*`、`work-unit.*`、`evidence.*` 稳定 ID,并由 `docs/process/lifecycle-registry.yaml` 生成当前结构表;用户指南、ADR 和实践说明继续人工维护并引用稳定 ID。数量、索引、依赖图和测试 fixture 必须在后续 Phase 通过同一生成器和 `--check` 一并交付。已发布 ID 不复用,语义变化通过弃用记录和 schema 迁移表达,以牺牲直接编辑派生表格的便利换取跨版本引用稳定性。
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
# ADR-0007: 生命周期使用原生工作单元并保留 Matt 兼容入口
|
|
2
|
-
|
|
3
|
-
`yss-product-lifecycle` 作为 YSS 研发生命周期的唯一默认入口,持有阶段、产物状态、门禁和正式资产所有权。Spec 综合、Ticket 正式化和切片实现使用生命周期注册表中的原生工作单元;`to-spec`、`to-tickets`、`implement` 保留为用户显式调用的兼容输入,结果必须回交生命周期验收,生命周期不得自动调用这些 user-invoked skills。
|
|
4
|
-
|
|
5
|
-
该选择避免用户手动记忆阶段切换,也避免 Matt 默认的 `ready-for-agent`、commit 等行为越过 YSS 门禁和授权。代价是生命周期需要维护原生工作单元路由和兼容适配,但两者在 `work_unit_routes` 中共享一个稳定工作单元身份、使用互斥的 `native` / `compatibility` 路由,不再形成两套执行定义。
|