@namewta/speculo 0.8.11 → 0.8.13
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 -1
- package/package.json +1 -1
- package/template/canonical/canonical-specdev-goal-plan.md +13 -7
- package/template/canonical/canonical-specdev-grill-with-docs.md +1 -1
- package/template/canonical/canonical-specdev-orchestrate-implementation.md +29 -11
- package/template/canonical/canonical-specdev-spec.md +1 -1
- package/template/canonical/canonical-specdev-tickets.md +38 -4
- package/template/skills/engineering-standards-builder/README.md +6 -28
- package/template/skills/engineering-standards-builder/SKILL.md +88 -163
- package/template/skills/engineering-standards-builder/examples/README.md +2 -0
- package/template/skills/engineering-standards-builder/manifest.txt +4 -0
- package/template/skills/engineering-standards-builder/references/rules/00-governance-and-precedence.md +22 -29
- package/template/skills/engineering-standards-builder/references/rules/01-project-discovery.md +26 -59
- package/template/skills/engineering-standards-builder/references/rules/02-evidence-topology-and-scope.md +28 -55
- package/template/skills/engineering-standards-builder/references/rules/03-interview-and-decisions.md +4 -3
- package/template/skills/engineering-standards-builder/references/rules/14-generation-contract.md +64 -80
- package/template/skills/engineering-standards-builder/references/rules/15-validation-contract.md +24 -47
- package/template/skills/engineering-standards-builder/references/rules/16-language-adapter-contract.md +11 -48
- package/template/skills/engineering-standards-builder/references/rules/README.md +3 -3
- package/template/skills/engineering-standards-builder/scripts/self-test.mjs +51 -5
- package/template/skills/engineering-standards-builder/scripts/validate-builder.mjs +16 -4
- package/template/skills/engineering-standards-builder/scripts/validate-generated-skill.mjs +166 -64
- package/template/skills/engineering-standards-builder/templates/README.md +12 -3
- package/template/skills/engineering-standards-builder/templates/domain-skill/SKILL.md.template +32 -0
- package/template/skills/engineering-standards-builder/templates/project-skill/SKILL.md.template +16 -11
- package/template/skills/engineering-standards-builder/templates/project-skill/generated-skill-set.json.template +7 -0
- package/template/skills/engineering-standards-builder/templates/project-skill/references/project/00-project-profile.md.template +3 -1
- package/template/skills/engineering-standards-builder/templates/project-skill/references/project/01-module-map.md.template +4 -0
- package/template/skills/engineering-standards-builder/templates/project-skill/references/project/02-decisions-and-exceptions.md.template +1 -1
- package/template/skills/engineering-standards-builder/templates/project-skill/references/project/03-skill-map.md.template +19 -0
- package/template/skills/engineering-standards-builder/templates/project-skill/references/project/04-source-and-template-map.md.template +22 -0
- package/template/skills/engineering-standards-builder/templates/project-skill/references/project/review-checklist.md.template +2 -0
- package/template/skills/git-history-squash/SKILL.md +100 -0
- package/template/skills/git-history-squash/assets/request-template.json +18 -0
- package/template/skills/git-history-squash/references/recovery-contract.md +50 -0
- package/template/skills/git-history-squash/references/rewrite-contract.md +123 -0
- package/template/skills/git-history-squash/references/submodule-contract.md +54 -0
- package/template/skills/git-history-squash/scripts/git-history-squash.mjs +1171 -0
- package/template/workflows/specdev/I-implement/I-implement.md +12 -4
- package/template/workflows/specdev/I-implement/execution-preflight.md +4 -0
- package/template/workflows/specdev/README.md +1 -1
- package/template/workflows/specdev/T-tickets/T-tickets.md +16 -3
- package/template/workflows/specdev/T-tickets/ticket-readiness.md +3 -0
- package/template/workflows/specdev/T-tickets/ticket-template.md +3 -0
- package/template/workflows/specdev/T-tickets/tickets-map-template.md +15 -0
- package/template/workflows/specdev/common/rules/artifact-contract.md +1 -1
- package/template/workflows/specdev/common/skills/subagent-delivery/SKILL.md +4 -2
- package/template/workflows/specdev/common/skills/subagent-delivery/references/external-web-subagent.md +2 -1
- package/template/workflows/specdev/common/skills/subagent-delivery/references/native-subagent.md +2 -1
- package/template/workflows/specdev/common/skills/subagent-delivery/references/source-package.md +4 -2
- package/template/workflows/specdev/common/tools/validate-specdev.mjs +143 -3
package/template/skills/engineering-standards-builder/references/rules/03-interview-and-decisions.md
CHANGED
|
@@ -66,10 +66,11 @@ Verification:
|
|
|
66
66
|
|
|
67
67
|
用户已经明确授权实施时:
|
|
68
68
|
|
|
69
|
-
-
|
|
70
|
-
-
|
|
69
|
+
- 使用治理合同中优先级最高且已复核的项目证据;
|
|
70
|
+
- 用户指定模板、当前门禁与成熟受测实现发生冲突时,记录 current/target/migration,不按数量选择;
|
|
71
71
|
- 对存量项目采用 Ratchet;
|
|
72
|
-
-
|
|
72
|
+
- 缺少项目证据的细节省略或标记为 `pending-decision`,只有安全与正确性底线可采用 Builder baseline;
|
|
73
|
+
- 不伪造用户选择或把通用默认写成项目既有规范;
|
|
73
74
|
- 继续生成可用规范,不用形式化问答阻塞。
|
|
74
75
|
|
|
75
76
|
## 完成条件
|
package/template/skills/engineering-standards-builder/references/rules/14-generation-contract.md
CHANGED
|
@@ -1,105 +1,89 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 项目 Skill Set 生成合同
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 最小结构
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
必须生成 `.agents/skills/engineering-standards/`,它是项目工程规范的权威入口和路由器。只有存在独立触发价值时才生成同级领域 Skill。
|
|
6
6
|
|
|
7
7
|
```text
|
|
8
|
-
.agents/skills/
|
|
8
|
+
.agents/skills/
|
|
9
|
+
engineering-standards/
|
|
10
|
+
SKILL.md
|
|
11
|
+
generated-skill-set.json
|
|
12
|
+
references/project/
|
|
13
|
+
00-project-profile.md
|
|
14
|
+
01-module-map.md
|
|
15
|
+
02-decisions-and-exceptions.md
|
|
16
|
+
03-skill-map.md
|
|
17
|
+
04-source-and-template-map.md
|
|
18
|
+
review-checklist.md
|
|
19
|
+
<optional-domain-skill>/
|
|
20
|
+
SKILL.md
|
|
21
|
+
references/...
|
|
9
22
|
```
|
|
10
23
|
|
|
11
|
-
|
|
24
|
+
根入口使用 `templates/project-skill/SKILL.md.template`;领域入口使用 `templates/domain-skill/SKILL.md.template`。详细内容只创建有证据且被入口路由的文件。
|
|
12
25
|
|
|
13
|
-
|
|
14
|
-
SKILL.md
|
|
15
|
-
references/
|
|
16
|
-
project/
|
|
17
|
-
00-project-profile.md
|
|
18
|
-
01-module-map.md
|
|
19
|
-
02-decisions-and-exceptions.md
|
|
20
|
-
rules/
|
|
21
|
-
architecture-and-boundaries.md
|
|
22
|
-
files-and-naming.md
|
|
23
|
-
api-errors-resources.md
|
|
24
|
-
testing.md
|
|
25
|
-
security-and-data.md
|
|
26
|
-
quality-gates.md
|
|
27
|
-
review-and-delivery.md
|
|
28
|
-
typescript/...
|
|
29
|
-
java/...
|
|
30
|
-
go/...
|
|
31
|
-
rust/...
|
|
32
|
-
```
|
|
26
|
+
## Skill 拆分门
|
|
33
27
|
|
|
34
|
-
|
|
28
|
+
领域 Skill 必须同时具备:独立触发场景、重复使用价值、项目证据、清晰责任边界和上下文缩减收益。语言、目录、Agent 数量或“看起来完整”都不是拆分理由。
|
|
35
29
|
|
|
36
|
-
|
|
30
|
+
根 Skill 维护跨域规范、项目画像、模块地图、决策/例外、Skill Map 和来源地图。领域 Skill 维护特定开发工作流、关键源码/模板导航、集成点和验证方式;不得重新定义根 Skill 的冲突规则。
|
|
37
31
|
|
|
38
|
-
|
|
32
|
+
## 项目源码与模板引用
|
|
39
33
|
|
|
40
|
-
|
|
41
|
-
- 描述适用项目与触发分支;
|
|
42
|
-
- 先读取 project profile 和 module map;
|
|
43
|
-
- 根据当前变更 scope 路由到最小 references;
|
|
44
|
-
- 要求实现前检查边界、实现后运行真实门禁;
|
|
45
|
-
- 不复制详细语言规范。
|
|
34
|
+
每个重要来源使用项目根相对路径,并说明:
|
|
46
35
|
|
|
47
|
-
|
|
36
|
+
- 何时读取;
|
|
37
|
+
- 它负责的能力或生成范围;
|
|
38
|
+
- 输出目标;
|
|
39
|
+
- 生成后需要的注册、import、配置或补充实现;
|
|
40
|
+
- 对应消费者、测试或 validator;
|
|
41
|
+
- 发现 legacy 或不适用场景时如何处理。
|
|
48
42
|
|
|
49
|
-
|
|
43
|
+
项目 FM、scaffold 和源码保持唯一事实源。Skill 不复制 `.ftl`、源码文件或大段代码;只做精准导航和行为约束。
|
|
50
44
|
|
|
51
|
-
|
|
52
|
-
- `01-module-map.md.template`
|
|
53
|
-
- `02-decisions-and-exceptions.md.template`
|
|
54
|
-
- `review-checklist.md.template`
|
|
45
|
+
## 所有权清单
|
|
55
46
|
|
|
56
|
-
|
|
47
|
+
根目录中的 `generated-skill-set.json` 使用 `templates/project-skill/generated-skill-set.json.template`,最小 schema:
|
|
57
48
|
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"schema_version": 1,
|
|
52
|
+
"generator": "engineering-standards-builder",
|
|
53
|
+
"skills": [
|
|
54
|
+
{
|
|
55
|
+
"name": "engineering-standards",
|
|
56
|
+
"path": ".agents/skills/engineering-standards",
|
|
57
|
+
"role": "router"
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
}
|
|
68
61
|
```
|
|
69
62
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
## 选择性生成
|
|
73
|
-
|
|
74
|
-
- 通用规则按项目风险裁剪,但安全、错误、资源、测试和门禁不能被无理由删除;
|
|
75
|
-
- TypeScript 项目才生成 `typescript/`;
|
|
76
|
-
- React/Vue 分别按模块生成,混合仓库分别限定 scope;
|
|
77
|
-
- Java/Spring Boot、Go、Rust 同理;
|
|
78
|
-
- 未支持语言生成通用规则与明确 fallback,不伪造专属语法规范。
|
|
63
|
+
允许的 role 只有 `router` 和 `domain`。必须恰有一个 router;path 必须等于 `.agents/skills/<name>`。不要加入时间戳、hash、模型、扫描计数或可从文件树重新推导的字段。
|
|
79
64
|
|
|
80
|
-
##
|
|
65
|
+
## 安全刷新
|
|
81
66
|
|
|
82
|
-
|
|
67
|
+
- create:目标名称与已有未登记 Skill 冲突时不得覆盖;
|
|
68
|
+
- refresh:只管理旧清单列出的 Skill,保留其他 `.agents/skills/*`;
|
|
69
|
+
- legacy 根 Skill 无清单时,只可在计划中声明接管 `engineering-standards` 本身,不能顺带接管同级 Skills;
|
|
70
|
+
- 删除和重命名必须在计划中列出;
|
|
71
|
+
- 先形成完整候选并验证,再替换已登记内容;失败时保持或恢复旧 Skill Set;
|
|
72
|
+
- 保留仍有效的用户决策、例外、公共 API 和项目特有知识;
|
|
73
|
+
- 相同输入重复刷新不得产生无意义 diff。
|
|
83
74
|
|
|
84
|
-
|
|
85
|
-
- 用户明确确认要新增的命令;
|
|
86
|
-
- 对尚未实现的目标命令标记 `planned`,不能当作当前可执行门禁。
|
|
75
|
+
兼容入口只在用户要求或旧路径已存在时使用 `templates/compatibility/`,并保持一句单向路由;它们不进入项目规范正文。
|
|
87
76
|
|
|
88
|
-
##
|
|
77
|
+
## 规则格式
|
|
89
78
|
|
|
90
|
-
|
|
79
|
+
所有 MUST/SHOULD 至少包含:
|
|
91
80
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
81
|
+
```text
|
|
82
|
+
Scope:
|
|
83
|
+
Level: MUST | SHOULD
|
|
84
|
+
Source:
|
|
85
|
+
Rule:
|
|
86
|
+
Verification:
|
|
87
|
+
```
|
|
99
88
|
|
|
100
|
-
|
|
101
|
-
- merge/refresh:先读取、备份或通过版本控制保护,再原子替换;
|
|
102
|
-
- dry-run:只输出计划和内容摘要,不写文件;
|
|
103
|
-
- 不覆盖目标根外路径;
|
|
104
|
-
- 不修改生成代码或 Vendor;
|
|
105
|
-
- 不删除未在计划中声明的用户文件。
|
|
89
|
+
项目命令只能来自现有配置/CI 或用户明确决定。尚未实现的命令标记 `planned`,不能报告为当前门禁。
|
package/template/skills/engineering-standards-builder/references/rules/15-validation-contract.md
CHANGED
|
@@ -1,65 +1,42 @@
|
|
|
1
|
-
# 验证合同
|
|
1
|
+
# Skill Set 验证合同
|
|
2
2
|
|
|
3
3
|
## 静态验证
|
|
4
4
|
|
|
5
5
|
运行:
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
node <skill-root>/scripts/validate-generated-skill.mjs
|
|
9
|
-
--root <project-root> \
|
|
10
|
-
--strict
|
|
8
|
+
node <skill-root>/scripts/validate-generated-skill.mjs --root <project-root> --strict
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
验证器必须确认:
|
|
14
12
|
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
13
|
+
- `engineering-standards` 根路由和六个项目 references 存在;
|
|
14
|
+
- `generated-skill-set.json` schema、唯一 router、name/path/role 与实际目录一致;
|
|
15
|
+
- 清单中每个 Skill 的 frontmatter 合法,根入口能路由到所有领域 Skill;
|
|
16
|
+
- Markdown 链接只能落在项目根内,目标存在且不经 symlink 越界;
|
|
17
|
+
- 来源地图至少引用一个 Skill 外的真实项目文件;
|
|
18
18
|
- 没有未替换模板变量;
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
- 没有 wrapper 循环;
|
|
24
|
-
- canonical 内容只有一份。
|
|
19
|
+
- MUST/SHOULD 规则包含 scope、source、rule 和 verification;
|
|
20
|
+
- 生成的语言/框架规则有 Project Inventory 信号;
|
|
21
|
+
- compatibility wrapper 只有一句且不形成循环;
|
|
22
|
+
- 同名 canonical 正文没有重复副本。
|
|
25
23
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
人工检查:
|
|
29
|
-
|
|
30
|
-
- 模块地图与仓库事实一致;
|
|
31
|
-
- 每个命令来自 manifest/CI/用户决策;
|
|
32
|
-
- React/Vue、Java/Spring、Go、Rust 规则只应用于正确 scope;
|
|
33
|
-
- current/target/migration 没有混写;
|
|
34
|
-
- 用户决定和仍有效例外被保留;
|
|
35
|
-
- 没有将 Builder 默认伪装为仓库事实;
|
|
36
|
-
- 不存在通用规则中的语言专属目录硬编码。
|
|
37
|
-
|
|
38
|
-
## 项目门禁
|
|
24
|
+
验证器只检查清单声明的 Builder 产物,不把清单外 Skill 当作待删除对象。
|
|
39
25
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
```text
|
|
43
|
-
command
|
|
44
|
-
working directory
|
|
45
|
-
exit code
|
|
46
|
-
result
|
|
47
|
-
unverified impact
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
失败时保留原始失败,不通过改配置、删测试或扩大排除获取通过。
|
|
26
|
+
## 语义验证
|
|
51
27
|
|
|
52
|
-
|
|
28
|
+
人工复核模块地图、Skill 拆分、命令来源、模板责任范围、消费者/测试、current/target/migration、用户决策和例外。尤其确认:
|
|
53
29
|
|
|
54
|
-
|
|
30
|
+
- 没有把 Builder 默认写成项目事实;
|
|
31
|
+
- 没有用 legacy 多数模式覆盖用户指定模板或成熟实现;
|
|
32
|
+
- 没有复制项目模板/源码造成第二事实源;
|
|
33
|
+
- 每个领域 Skill 都能独立触发并减少上下文;
|
|
34
|
+
- 未覆盖范围被明确省略或标为待确认,而非伪造规则。
|
|
55
35
|
|
|
56
|
-
##
|
|
36
|
+
## 项目门禁与幂等性
|
|
57
37
|
|
|
58
|
-
|
|
38
|
+
只运行项目真实存在且已授权的命令,记录 working directory、退出码、结果和未验证影响。失败时保留原始失败,不改配置或删测试掩盖问题。
|
|
59
39
|
|
|
60
|
-
|
|
61
|
-
- failed;
|
|
62
|
-
- not-run;
|
|
63
|
-
- not-applicable。
|
|
40
|
+
使用相同项目事实与决策再次生成,应无无意义 diff。时间戳和扫描计数不得进入生成内容。
|
|
64
41
|
|
|
65
|
-
|
|
42
|
+
结果使用 `passed`、`failed`、`not-run`、`not-applicable`;未运行不能报告为通过。
|
|
@@ -1,55 +1,18 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 语言适配器与 Fallback
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Builder 内置语言/框架 references 是发现和审计检查清单。它们帮助识别公共 API、类型/错误/资源、并发、测试、工具链和版本风险,但不能覆盖项目代码、模板、配置与用户决定。
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
```text
|
|
8
|
-
references/<language>/00-detection-and-scope.md
|
|
9
|
-
references/<language>/... language rules
|
|
10
|
-
examples/<language>/<fixture>/
|
|
11
|
-
discover-project.mjs detection signals
|
|
12
|
-
self-test.mjs assertions
|
|
13
|
-
SKILL.md routing pointer
|
|
14
|
-
manifest entry
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
框架、运行时和应用类型放在语言目录下的 `frameworks/`、`runtimes/`、`app-types/`,不要污染通用规则。
|
|
18
|
-
|
|
19
|
-
## Detection
|
|
20
|
-
|
|
21
|
-
检测至少交叉使用两类信号:
|
|
22
|
-
|
|
23
|
-
- manifest/build/toolchain;
|
|
24
|
-
- 源文件与入口;
|
|
25
|
-
- CI/build 命令;
|
|
26
|
-
- 框架依赖或注解;
|
|
27
|
-
- 官方目录/模块配置。
|
|
28
|
-
|
|
29
|
-
仅文件扩展名通常为 medium confidence;manifest + 源码入口可为 high confidence。
|
|
30
|
-
|
|
31
|
-
## 语言规则最小覆盖
|
|
32
|
-
|
|
33
|
-
- 模块/package/crate 与 public API;
|
|
34
|
-
- 命名和目录;
|
|
35
|
-
- 类型/错误/资源/并发;
|
|
36
|
-
- 测试布局和工具;
|
|
37
|
-
- formatter、lint/static analysis、build;
|
|
38
|
-
- 版本与兼容策略;
|
|
39
|
-
- 官方依据;
|
|
40
|
-
- 语言专属访谈触发条件。
|
|
5
|
+
只读取 Project Inventory 和源码证据命中的适配器。一个依赖、一种扩展名或根目录 manifest 不能让规则扩散到所有模块。
|
|
41
6
|
|
|
42
7
|
## 未内置语言
|
|
43
8
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
1. 仍生成通用规则;
|
|
47
|
-
2. 从仓库事实提取真实 formatter/linter/test/build 命令;
|
|
48
|
-
3. 保留现有目录和语言惯例;
|
|
49
|
-
4. 将语言专属规则标记为 `pending-adapter`;
|
|
50
|
-
5. 不把 TypeScript、Java、Go 或 Rust 模式套用;
|
|
51
|
-
6. 必要时按本合同构建新适配器。
|
|
9
|
+
遇到 Python、Kotlin、C#、C/C++ 或其他未内置语言时:
|
|
52
10
|
|
|
53
|
-
|
|
11
|
+
1. 从项目代码、配置、测试和 CI 提取真实规范;
|
|
12
|
+
2. 保留现有目录、公开边界和成熟惯例;
|
|
13
|
+
3. 引用真实 formatter、lint、test 和 build 命令;
|
|
14
|
+
4. 对缺乏证据的语言细节省略或标记 `pending-decision`;
|
|
15
|
+
5. 不套用 TypeScript、Java、Go 或 Rust 模式;
|
|
16
|
+
6. 只有需要让 Builder 本身长期识别该生态时,才扩展语言适配器与 fixture。
|
|
54
17
|
|
|
55
|
-
|
|
18
|
+
无内置适配器不阻止生成项目专属 Skill。项目证据优先于 Builder 覆盖范围。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 通用规则索引
|
|
2
2
|
|
|
3
|
-
`references/rules/`
|
|
3
|
+
`references/rules/` 描述跨语言的审计维度、证据和治理合同。它们是 Builder 的工作方法,不是可直接复制到项目的规范正文;语言、框架、运行时的具体结论必须由项目证据支持。
|
|
4
4
|
|
|
5
5
|
按运行阶段读取:
|
|
6
6
|
|
|
@@ -18,6 +18,6 @@
|
|
|
18
18
|
- [工具链、质量门禁与 CI](11-tooling-quality-gates-and-ci.md)
|
|
19
19
|
- [Git、评审与交付](12-git-review-and-delivery.md)
|
|
20
20
|
- [采用、例外与 Ratchet](13-adoption-exceptions-and-ratchets.md)
|
|
21
|
-
- [
|
|
22
|
-
- [验证合同](15-validation-contract.md)
|
|
21
|
+
- [项目 Skill Set 生成合同](14-generation-contract.md)
|
|
22
|
+
- [Skill Set 验证合同](15-validation-contract.md)
|
|
23
23
|
- [语言适配器与 Fallback](16-language-adapter-contract.md)
|
|
@@ -83,27 +83,43 @@ function assertFixture(inventory, expected, fixtureName) {
|
|
|
83
83
|
|
|
84
84
|
async function writeGeneratedFixture(projectRoot) {
|
|
85
85
|
await mkdir(path.join(projectRoot, 'src'), { recursive: true });
|
|
86
|
+
await mkdir(path.join(projectRoot, 'docs', 'fm'), { recursive: true });
|
|
86
87
|
await writeFile(path.join(projectRoot, 'package.json'), `${JSON.stringify({
|
|
87
88
|
name: 'generated-validation-fixture', private: true,
|
|
88
89
|
dependencies: { vue: '^3.5.0' }, devDependencies: { typescript: '^5.9.0', vite: '^7.0.0' },
|
|
89
90
|
}, null, 2)}\n`);
|
|
90
91
|
await writeFile(path.join(projectRoot, 'tsconfig.json'), '{"compilerOptions":{"strict":true},"include":["src"]}\n');
|
|
91
92
|
await writeFile(path.join(projectRoot, 'src', 'App.vue'), '<script setup lang="ts">const value = 1</script>\n<template><main>{{ value }}</main></template>\n');
|
|
93
|
+
await writeFile(path.join(projectRoot, 'docs', 'fm', 'component.vue.ftl'), '<template><main>${title}</main></template>\n');
|
|
92
94
|
|
|
93
95
|
const canonical = path.join(projectRoot, '.agents', 'skills', 'engineering-standards');
|
|
96
|
+
const domain = path.join(projectRoot, '.agents', 'skills', 'generated-vue-development');
|
|
94
97
|
await mkdir(path.join(canonical, 'references', 'project'), { recursive: true });
|
|
95
98
|
await mkdir(path.join(canonical, 'references', 'rules'), { recursive: true });
|
|
96
99
|
await mkdir(path.join(canonical, 'references', 'typescript', 'frameworks'), { recursive: true });
|
|
97
|
-
await
|
|
100
|
+
await mkdir(path.join(domain, 'references'), { recursive: true });
|
|
101
|
+
await writeFile(path.join(canonical, 'generated-skill-set.json'), `${JSON.stringify({
|
|
102
|
+
schema_version: 1,
|
|
103
|
+
generator: 'engineering-standards-builder',
|
|
104
|
+
skills: [
|
|
105
|
+
{ name: 'engineering-standards', path: '.agents/skills/engineering-standards', role: 'router' },
|
|
106
|
+
{ name: 'generated-vue-development', path: '.agents/skills/generated-vue-development', role: 'domain' },
|
|
107
|
+
],
|
|
108
|
+
}, null, 2)}\n`);
|
|
109
|
+
await writeFile(path.join(canonical, 'SKILL.md'), `---\nname: engineering-standards\ndescription: Generated fixture standards.\n---\n\n# Engineering Standards\n\nRead [profile](references/project/00-project-profile.md), [modules](references/project/01-module-map.md), [decisions](references/project/02-decisions-and-exceptions.md), [skills](references/project/03-skill-map.md), [sources](references/project/04-source-and-template-map.md), [review](references/project/review-checklist.md), [rules](references/rules/core.md), [TypeScript](references/typescript/language.md), and [Vue](references/typescript/frameworks/vue.md). Route Vue work to [generated-vue-development](../generated-vue-development/SKILL.md).\n`);
|
|
98
110
|
await writeFile(path.join(canonical, 'references', 'project', '00-project-profile.md'), '# Project Profile\n\nVue fixture.\n');
|
|
99
111
|
await writeFile(path.join(canonical, 'references', 'project', '01-module-map.md'), '# Module Map\n\n- module:. uses TypeScript and Vue.\n');
|
|
100
112
|
await writeFile(path.join(canonical, 'references', 'project', '02-decisions-and-exceptions.md'), '# Decisions and Exceptions\n\nNo exceptions.\n');
|
|
113
|
+
await writeFile(path.join(canonical, 'references', 'project', '03-skill-map.md'), '# Skill Map\n\n- generated-vue-development: Vue implementation.\n');
|
|
114
|
+
await writeFile(path.join(canonical, 'references', 'project', '04-source-and-template-map.md'), '# Source and Template Map\n\n- [Vue implementation](../../../../../src/App.vue)\n- [Component template](../../../../../docs/fm/component.vue.ftl)\n');
|
|
101
115
|
await writeFile(path.join(canonical, 'references', 'project', 'review-checklist.md'), '# Review Checklist\n\n- Verify scope.\n');
|
|
102
116
|
const rule = `# Core Rules\n\n### CORE-001 Validated boundary\nScope: repository\nLevel: MUST\nSource: repository-fact\nApplies when: external input enters the application.\nRule: Validate external input before domain use.\nRationale: Preserve trusted internal models.\nVerification: Review boundary tests.\nException: Recorded project exception only.\n`;
|
|
103
117
|
await writeFile(path.join(canonical, 'references', 'rules', 'core.md'), rule);
|
|
104
118
|
await writeFile(path.join(canonical, 'references', 'typescript', 'language.md'), rule.replace('CORE-001', 'TS-001'));
|
|
105
119
|
await writeFile(path.join(canonical, 'references', 'typescript', 'frameworks', 'vue.md'), rule.replace('CORE-001', 'VUE-001'));
|
|
106
|
-
|
|
120
|
+
await writeFile(path.join(domain, 'SKILL.md'), '---\nname: generated-vue-development\ndescription: Implement Vue features in the generated fixture.\n---\n\n# Vue Development\n\nRead `../engineering-standards/SKILL.md`, then inspect [the mature component](../../../src/App.vue) and [the project template](../../../docs/fm/component.vue.ftl).\n');
|
|
121
|
+
await writeFile(path.join(domain, 'references', 'workflow.md'), '# Workflow\n\nUse the project template and verify the component.\n');
|
|
122
|
+
return { canonical, domain };
|
|
107
123
|
}
|
|
108
124
|
|
|
109
125
|
async function main() {
|
|
@@ -174,9 +190,39 @@ async function main() {
|
|
|
174
190
|
|
|
175
191
|
const generatedTemp = await mkdtemp(path.join(os.tmpdir(), 'standards-builder-generated-'));
|
|
176
192
|
tempRoots.push(generatedTemp);
|
|
177
|
-
const canonical = await writeGeneratedFixture(generatedTemp);
|
|
193
|
+
const { canonical } = await writeGeneratedFixture(generatedTemp);
|
|
178
194
|
const valid = runNode(generatedValidator, ['--root', generatedTemp, '--strict']);
|
|
179
|
-
assert(valid.status === 0, `valid generated Skill was rejected: ${(valid.stderr || valid.stdout).trim()}`);
|
|
195
|
+
assert(valid.status === 0, `valid generated Skill Set was rejected: ${(valid.stderr || valid.stdout).trim()}`);
|
|
196
|
+
|
|
197
|
+
const unowned = path.join(generatedTemp, '.agents', 'skills', 'user-maintained-skill');
|
|
198
|
+
await mkdir(unowned, { recursive: true });
|
|
199
|
+
await writeFile(path.join(unowned, 'SKILL.md'), '---\nname: user-maintained-skill\ndescription: User-owned fixture skill.\n---\n\n# User Skill\n');
|
|
200
|
+
const withUnowned = runNode(generatedValidator, ['--root', generatedTemp, '--strict']);
|
|
201
|
+
assert(withUnowned.status === 0, 'validator must ignore a Skill outside the Builder ownership manifest');
|
|
202
|
+
|
|
203
|
+
const sourceMap = path.join(canonical, 'references', 'project', '04-source-and-template-map.md');
|
|
204
|
+
const sourceMapText = await readFile(sourceMap, 'utf8');
|
|
205
|
+
await writeFile(sourceMap, `${sourceMapText}\n- [Missing project source](../../../../../src/missing.ts)\n`);
|
|
206
|
+
const brokenSource = runNode(generatedValidator, ['--root', generatedTemp, '--strict']);
|
|
207
|
+
assert(brokenSource.status !== 0, 'validator must reject a missing project source link');
|
|
208
|
+
await writeFile(sourceMap, sourceMapText);
|
|
209
|
+
|
|
210
|
+
const ownershipPath = path.join(canonical, 'generated-skill-set.json');
|
|
211
|
+
const ownershipText = await readFile(ownershipPath, 'utf8');
|
|
212
|
+
const badOwnership = JSON.parse(ownershipText);
|
|
213
|
+
badOwnership.skills[1].path = '.agents/skills/../outside';
|
|
214
|
+
await writeFile(ownershipPath, `${JSON.stringify(badOwnership, null, 2)}\n`);
|
|
215
|
+
const escapingOwnership = runNode(generatedValidator, ['--root', generatedTemp, '--strict']);
|
|
216
|
+
assert(escapingOwnership.status !== 0, 'validator must reject an invalid owned Skill path');
|
|
217
|
+
await writeFile(ownershipPath, ownershipText);
|
|
218
|
+
|
|
219
|
+
const routerPath = path.join(canonical, 'SKILL.md');
|
|
220
|
+
const routerText = await readFile(routerPath, 'utf8');
|
|
221
|
+
await writeFile(routerPath, routerText.replace('[generated-vue-development](../generated-vue-development/SKILL.md)', '`generated-vue-development`'));
|
|
222
|
+
const missingRoute = runNode(generatedValidator, ['--root', generatedTemp, '--strict']);
|
|
223
|
+
assert(missingRoute.status !== 0, 'validator must reject a domain Skill that is not linked by the router');
|
|
224
|
+
await writeFile(routerPath, routerText);
|
|
225
|
+
|
|
180
226
|
await writeFile(path.join(canonical, 'references', 'typescript', 'frameworks', 'react.md'), '# React\n');
|
|
181
227
|
const wrongFramework = runNode(generatedValidator, ['--root', generatedTemp, '--strict']);
|
|
182
228
|
assert(wrongFramework.status !== 0, 'strict validator must reject an unselected React rule in a Vue-only project');
|
|
@@ -186,7 +232,7 @@ async function main() {
|
|
|
186
232
|
await writeFile(path.join(wrapper, 'SKILL.md'), '---\nname: typescript-standards\ndescription: alias\n---\n\nRoute to engineering-standards.\nSecond body line.\n');
|
|
187
233
|
const badWrapper = runNode(generatedValidator, ['--root', generatedTemp, '--strict']);
|
|
188
234
|
assert(badWrapper.status !== 0, 'validator must reject a multi-line compatibility wrapper');
|
|
189
|
-
process.stdout.write('self-test: generated Skill validator OK\n');
|
|
235
|
+
process.stdout.write('self-test: generated Skill Set validator OK\n');
|
|
190
236
|
|
|
191
237
|
const manifest = runNode(manifestScript, ['--root', args.root, '--check']);
|
|
192
238
|
assert(manifest.status === 0, `manifest check failed: ${(manifest.stderr || manifest.stdout).trim()}`);
|
|
@@ -35,7 +35,8 @@ async function collect(rootReal) {
|
|
|
35
35
|
const directories = [];
|
|
36
36
|
async function visit(directory) {
|
|
37
37
|
const relDir = toPosix(path.relative(rootReal, directory)) || '.';
|
|
38
|
-
const entries = await readdir(directory, { withFileTypes: true })
|
|
38
|
+
const entries = (await readdir(directory, { withFileTypes: true }))
|
|
39
|
+
.filter((entry) => entry.name !== '.DS_Store');
|
|
39
40
|
entries.sort((a, b) => a.name.localeCompare(b.name, 'en'));
|
|
40
41
|
directories.push({ abs: directory, rel: relDir, entries });
|
|
41
42
|
for (const entry of entries) {
|
|
@@ -89,7 +90,7 @@ function normalizeLinkTarget(sourceAbs, link) {
|
|
|
89
90
|
}
|
|
90
91
|
|
|
91
92
|
async function computeManifest(rootReal, files) {
|
|
92
|
-
return `${files.map((file) => file.rel).filter((rel) => rel !== 'manifest.txt' &&
|
|
93
|
+
return `${files.map((file) => file.rel).filter((rel) => rel !== 'manifest.txt' && path.posix.basename(rel) !== '.DS_Store').sort((a, b) => a.localeCompare(b, 'en')).join('\n')}\n`;
|
|
93
94
|
}
|
|
94
95
|
|
|
95
96
|
function failLines(errors, warnings) {
|
|
@@ -138,9 +139,11 @@ async function main() {
|
|
|
138
139
|
if (frontmatter.values.name !== 'engineering-standards-builder') errors.push('stable Skill ID must remain engineering-standards-builder for drop-in replacement');
|
|
139
140
|
if (!frontmatter.values.description) errors.push('frontmatter description is missing');
|
|
140
141
|
if (/[<>]/.test(frontmatter.values.description ?? '')) errors.push('frontmatter description must not contain angle brackets');
|
|
142
|
+
if (frontmatter.values['disable-model-invocation'] !== 'true') errors.push('Builder must remain explicitly user-invoked with disable-model-invocation: true');
|
|
143
|
+
}
|
|
144
|
+
for (const requiredConcept of ['Agent Team', 'generated-skill-set.json', '最小原则', 'Skill 边界']) {
|
|
145
|
+
if (!skillText.includes(requiredConcept)) errors.push(`SKILL.md is missing core contract: ${requiredConcept}`);
|
|
141
146
|
}
|
|
142
|
-
const completionCount = (skillText.match(/\*\*完成标准\*\*/g) ?? []).length;
|
|
143
|
-
if (completionCount < 7) errors.push(`SKILL.md must define completion criteria for all seven stages; found ${completionCount}`);
|
|
144
147
|
}
|
|
145
148
|
|
|
146
149
|
const localMarkdownFiles = files.filter((file) => /\.md(?:\.template)?$/.test(file.name) || file.name === 'SKILL.md.template');
|
|
@@ -218,6 +221,15 @@ async function main() {
|
|
|
218
221
|
if (!indexText.includes(file.name)) errors.push(`template is not indexed in templates/README.md: ${file.rel}`);
|
|
219
222
|
}
|
|
220
223
|
}
|
|
224
|
+
for (const requiredTemplate of [
|
|
225
|
+
'templates/project-skill/SKILL.md.template',
|
|
226
|
+
'templates/project-skill/generated-skill-set.json.template',
|
|
227
|
+
'templates/project-skill/references/project/03-skill-map.md.template',
|
|
228
|
+
'templates/project-skill/references/project/04-source-and-template-map.md.template',
|
|
229
|
+
'templates/domain-skill/SKILL.md.template',
|
|
230
|
+
]) {
|
|
231
|
+
if (!fileByRel.has(requiredTemplate)) errors.push(`required Skill Set template missing: ${requiredTemplate}`);
|
|
232
|
+
}
|
|
221
233
|
|
|
222
234
|
const fixtureExpectations = files.filter((file) => file.name === 'expected.json' && file.rel.startsWith('examples/'));
|
|
223
235
|
if (fixtureExpectations.length < 6) errors.push(`at least six fixture contracts are required; found ${fixtureExpectations.length}`);
|