artifact-chain-assistant 0.8.2 → 0.8.4

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.
Files changed (81) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +23 -0
  4. package/INSTALL.md +10 -10
  5. package/README.md +4 -4
  6. package/README.zh-CN.md +4 -4
  7. package/adapters/claude/.claude-plugin/plugin.json +1 -1
  8. package/adapters/claude/INSTALL.md +10 -10
  9. package/adapters/claude/agent-methods/catalog.yaml +1 -1
  10. package/adapters/claude/compatibility.json +4 -4
  11. package/adapters/claude/families-src/prd-feature/implementation.yaml +39 -0
  12. package/adapters/claude/families-src/scenario-script/implementation.yaml +39 -0
  13. package/adapters/claude/family-apis/catalog.json +16 -0
  14. package/adapters/claude/family-apis/e2e-test/api.json +205 -0
  15. package/adapters/claude/family-apis/schema/family-api.schema.json +235 -0
  16. package/adapters/claude/schemas/project-facts.schema.json +80 -0
  17. package/adapters/claude/scripts/check-family-api.mjs +84 -0
  18. package/adapters/claude/scripts/export-family-api.mjs +184 -0
  19. package/adapters/claude/scripts/family-compile.mjs +309 -0
  20. package/adapters/claude/scripts/family-help-render.mjs +214 -0
  21. package/adapters/claude/scripts/lib/family-api-validator.mjs +413 -0
  22. package/adapters/claude/scripts/lib/generated/canonical-json-v1-manifest.json +7 -0
  23. package/adapters/claude/scripts/lib/generated/canonical-json-v1-vectors.generated.json +50 -0
  24. package/adapters/claude/scripts/lib/generated/canonical-json-v1.generated.mjs +130 -0
  25. package/adapters/claude/scripts/lib/generated/family-api-validator.generated.mjs +1756 -0
  26. package/adapters/claude/scripts/lib/generated/project-facts-validator.generated.mjs +638 -0
  27. package/adapters/claude/scripts/lib/generated/schema-validator-manifest.json +20 -0
  28. package/adapters/claude/scripts/lib/method-query.mjs +177 -0
  29. package/adapters/claude/scripts/method-query.mjs +67 -0
  30. package/adapters/claude/skills/setup/SKILL.md +3 -2
  31. package/adapters/codex/.codex-plugin/plugin.json +1 -1
  32. package/adapters/codex/INSTALL.md +10 -10
  33. package/adapters/codex/agent-methods/catalog.yaml +1 -1
  34. package/adapters/codex/compatibility.json +4 -4
  35. package/adapters/codex/families-src/prd-feature/implementation.yaml +39 -0
  36. package/adapters/codex/families-src/scenario-script/implementation.yaml +39 -0
  37. package/adapters/codex/family-apis/catalog.json +16 -0
  38. package/adapters/codex/family-apis/e2e-test/api.json +205 -0
  39. package/adapters/codex/family-apis/schema/family-api.schema.json +235 -0
  40. package/adapters/codex/schemas/project-facts.schema.json +80 -0
  41. package/adapters/codex/scripts/check-family-api.mjs +84 -0
  42. package/adapters/codex/scripts/export-family-api.mjs +184 -0
  43. package/adapters/codex/scripts/family-compile.mjs +309 -0
  44. package/adapters/codex/scripts/family-help-render.mjs +214 -0
  45. package/adapters/codex/scripts/lib/family-api-validator.mjs +413 -0
  46. package/adapters/codex/scripts/lib/generated/canonical-json-v1-manifest.json +7 -0
  47. package/adapters/codex/scripts/lib/generated/canonical-json-v1-vectors.generated.json +50 -0
  48. package/adapters/codex/scripts/lib/generated/canonical-json-v1.generated.mjs +130 -0
  49. package/adapters/codex/scripts/lib/generated/family-api-validator.generated.mjs +1756 -0
  50. package/adapters/codex/scripts/lib/generated/project-facts-validator.generated.mjs +638 -0
  51. package/adapters/codex/scripts/lib/generated/schema-validator-manifest.json +20 -0
  52. package/adapters/codex/scripts/lib/method-query.mjs +177 -0
  53. package/adapters/codex/scripts/method-query.mjs +67 -0
  54. package/adapters/codex/skills/setup/SKILL.md +3 -2
  55. package/agent-methods/catalog.yaml +1 -1
  56. package/compatibility.json +4 -4
  57. package/families-src/prd-feature/implementation.yaml +39 -0
  58. package/families-src/scenario-script/implementation.yaml +39 -0
  59. package/family-apis/catalog.json +16 -0
  60. package/family-apis/e2e-test/api.json +205 -0
  61. package/family-apis/schema/family-api.schema.json +235 -0
  62. package/package.json +2 -2
  63. package/schemas/project-facts.schema.json +80 -0
  64. package/scripts/check-family-api.mjs +84 -0
  65. package/scripts/export-family-api.mjs +184 -0
  66. package/scripts/family-compile.mjs +309 -0
  67. package/scripts/family-help-render.mjs +214 -0
  68. package/scripts/generate-canonical-json-protocol.mjs +75 -0
  69. package/scripts/generate-schema-validators.mjs +141 -0
  70. package/scripts/lib/family-api-validator.mjs +413 -0
  71. package/scripts/lib/generated/canonical-json-v1-manifest.json +7 -0
  72. package/scripts/lib/generated/canonical-json-v1-vectors.generated.json +50 -0
  73. package/scripts/lib/generated/canonical-json-v1.generated.mjs +130 -0
  74. package/scripts/lib/generated/family-api-validator.generated.mjs +1756 -0
  75. package/scripts/lib/generated/project-facts-validator.generated.mjs +638 -0
  76. package/scripts/lib/generated/schema-validator-manifest.json +20 -0
  77. package/scripts/lib/method-query.mjs +177 -0
  78. package/scripts/method-query.mjs +67 -0
  79. package/skills/setup/SKILL.md +3 -2
  80. package/skills-src/setup/SKILL.md +3 -2
  81. package/templates/git-hooks/pre-commit.sh +5 -2
@@ -0,0 +1,205 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "api": {
4
+ "id": "artifact.e2e-test-family",
5
+ "major": 1,
6
+ "revisionDigest": "sha256:66cdd1098a93eb8d128c57010a9405dd1de55bee1e702ced44a93c202cc4b463",
7
+ "displayName": "E2E Test Skill Family",
8
+ "summary": "Standard skill family for authoring, reviewing, and repairing E2E test specification artifacts",
9
+ "artifactTypes": [
10
+ "e2e-test"
11
+ ]
12
+ },
13
+ "services": [
14
+ {
15
+ "id": "artifact.e2e-test.default",
16
+ "required": true,
17
+ "kind": "workflow",
18
+ "intents": [
19
+ "route"
20
+ ],
21
+ "accepts": [
22
+ "User request or intent to work with E2E test specifications"
23
+ ],
24
+ "produces": [
25
+ "Routed to appropriate E2E test service"
26
+ ],
27
+ "sideEffectCeiling": "read-only",
28
+ "mixSafe": false,
29
+ "discoverable": {
30
+ "default": true
31
+ }
32
+ },
33
+ {
34
+ "id": "artifact.e2e-test.help",
35
+ "required": true,
36
+ "kind": "operation",
37
+ "intents": [
38
+ "help"
39
+ ],
40
+ "accepts": [
41
+ "Help request for E2E test capabilities"
42
+ ],
43
+ "produces": [
44
+ "Capability overview and adoption guidance"
45
+ ],
46
+ "sideEffectCeiling": "read-only",
47
+ "mixSafe": false,
48
+ "discoverable": {
49
+ "help": true
50
+ }
51
+ },
52
+ {
53
+ "id": "artifact.e2e-test.author",
54
+ "required": true,
55
+ "kind": "workflow",
56
+ "intents": [
57
+ "author"
58
+ ],
59
+ "accepts": [
60
+ {
61
+ "protocol": "prd-feature-input",
62
+ "version": "1.0",
63
+ "input": "PRD feature artifact or user intent for E2E test authoring"
64
+ },
65
+ {
66
+ "protocol": "scenario-script-input",
67
+ "version": "1.0",
68
+ "input": "Scenario script artifact providing E2E test context"
69
+ },
70
+ {
71
+ "protocol": "e2e-raw-input-envelope",
72
+ "version": "1.0",
73
+ "input": "schemas/author-raw-input.json"
74
+ },
75
+ {
76
+ "protocol": "e2e-stage-resume",
77
+ "version": "1.0",
78
+ "input": "inputs.{inspection,assessment,matrix} resume with source and digest revalidation"
79
+ }
80
+ ],
81
+ "produces": [
82
+ "artifact.e2e-test@1",
83
+ {
84
+ "protocol": "preview-manifest-v1.0",
85
+ "version": "1.0",
86
+ "input": "schemas/preview-manifest.json"
87
+ }
88
+ ],
89
+ "sideEffectCeiling": "write-authorized-artifacts",
90
+ "mixSafe": false,
91
+ "modes": {
92
+ "supported": ["preview", "commit"],
93
+ "default": "preview"
94
+ }
95
+ },
96
+ {
97
+ "id": "artifact.e2e-test.review",
98
+ "required": true,
99
+ "kind": "operation",
100
+ "intents": [
101
+ "review"
102
+ ],
103
+ "accepts": [
104
+ "artifact.e2e-test@1"
105
+ ],
106
+ "produces": [
107
+ {
108
+ "protocol": "review-result-v1.0",
109
+ "version": "1.0",
110
+ "input": "Review Result Protocol v1.0 JSON object"
111
+ }
112
+ ],
113
+ "sideEffectCeiling": "write-review-result",
114
+ "mixSafe": false
115
+ },
116
+ {
117
+ "id": "artifact.e2e-test.repair",
118
+ "required": true,
119
+ "kind": "workflow",
120
+ "intents": [
121
+ "repair"
122
+ ],
123
+ "accepts": [
124
+ {
125
+ "protocol": "review-result-v1.0",
126
+ "version": "1.0",
127
+ "input": "Review Result Protocol v1.0 JSON with open findings"
128
+ },
129
+ "artifact.e2e-test@1"
130
+ ],
131
+ "produces": [
132
+ "artifact.e2e-test@1",
133
+ {
134
+ "protocol": "preview-manifest-v1.0",
135
+ "version": "1.0",
136
+ "input": "schemas/preview-manifest.json"
137
+ }
138
+ ],
139
+ "sideEffectCeiling": "write-authorized-artifacts",
140
+ "mixSafe": false,
141
+ "modes": {
142
+ "supported": ["preview", "commit"],
143
+ "default": "preview"
144
+ }
145
+ }
146
+ ],
147
+ "capabilities": [
148
+ {
149
+ "id": "e2e-candidate-assessment",
150
+ "summary": "Risk-driven E2E candidate path identification from PRD and scenario artifacts"
151
+ },
152
+ {
153
+ "id": "e2e-matrix-design",
154
+ "summary": "Eight-dimensional E2E test case design model"
155
+ },
156
+ {
157
+ "id": "e2e-proof-reconciliation",
158
+ "summary": "Traceability from test specification through implementation to execution evidence"
159
+ },
160
+ {
161
+ "id": "e2e-finding-capability-manifest",
162
+ "summary": "Machine-readable E2E-F-001..012 capability states with stable blockers"
163
+ }
164
+ ],
165
+ "dataTransferObjects": [
166
+ {
167
+ "id": "e2e-test-matrix",
168
+ "schemaId": "e2e-test/matrix/v1",
169
+ "schema": "schemas/matrix.json",
170
+ "version": "1.0",
171
+ "digestRule": "stableDigest(canonicalize key-sorted JSON) -> sha256:<64hex>",
172
+ "services": [
173
+ { "service": "artifact.e2e-test.author", "position": "intermediate-output (run root)" },
174
+ { "service": "artifact.e2e-test.review", "position": "intermediate-input (cross-validation)" },
175
+ { "service": "artifact.e2e-test.repair", "position": "intermediate-input" }
176
+ ],
177
+ "artifactBinding": "E2E artifact package manifest binds the minimal artifact and authoritative matrix by relative references and content digests"
178
+ },
179
+ {
180
+ "id": "e2e-finding-capability-manifest",
181
+ "schemaId": "e2e-test/finding-capability-manifest/v1",
182
+ "schema": "schemas/finding-capability-manifest.json",
183
+ "version": "1.0",
184
+ "digestRule": "stableDigest(rules) -> sha256:<64hex>",
185
+ "services": []
186
+ },
187
+ {
188
+ "id": "e2e-artifact-package-manifest",
189
+ "schemaId": "e2e-test/artifact-package-manifest/v1",
190
+ "schema": "schemas/artifact-package-manifest.json",
191
+ "version": "1.0",
192
+ "digestRule": "stableDigest(manifest excluding packageDigest) -> sha256:<64hex>",
193
+ "services": [
194
+ { "service": "artifact.e2e-test.author", "position": "formal-output" },
195
+ { "service": "artifact.e2e-test.review", "position": "required-input" },
196
+ { "service": "artifact.e2e-test.repair", "position": "required-input-and-output" }
197
+ ]
198
+ }
199
+ ],
200
+ "qualificationKinds": [
201
+ "operationalQualification",
202
+ "methodForwardQualification",
203
+ "releaseArtifactCertification"
204
+ ]
205
+ }
@@ -0,0 +1,235 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://artifact-chain.dev/schemas/family-api.schema.json",
4
+ "title": "Skill Family API",
5
+ "description": "Schema for standard Skill Family API definitions owned by artifact-chain-assistant",
6
+ "type": "object",
7
+ "required": ["schemaVersion", "api", "services"],
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "schemaVersion": {
11
+ "type": "integer",
12
+ "const": 1
13
+ },
14
+ "api": {
15
+ "type": "object",
16
+ "required": ["id", "major", "revisionDigest", "displayName", "summary"],
17
+ "additionalProperties": false,
18
+ "properties": {
19
+ "id": {
20
+ "type": "string",
21
+ "pattern": "^artifact\\.[a-z][a-z0-9-]*-family$",
22
+ "description": "Canonical family API identity in the artifact.* namespace"
23
+ },
24
+ "major": {
25
+ "type": "integer",
26
+ "minimum": 1,
27
+ "description": "Major version indicating compatibility family"
28
+ },
29
+ "revisionDigest": {
30
+ "type": "string",
31
+ "pattern": "^sha256:[a-f0-9]{64}$",
32
+ "description": "Immutable SHA-256 digest of canonical API content excluding this field"
33
+ },
34
+ "displayName": {
35
+ "type": "string",
36
+ "minLength": 1
37
+ },
38
+ "summary": {
39
+ "type": "string",
40
+ "minLength": 1
41
+ },
42
+ "artifactTypes": {
43
+ "type": "array",
44
+ "items": { "type": "string", "minLength": 1 },
45
+ "uniqueItems": true,
46
+ "description": "Artifact types this family operates on"
47
+ }
48
+ }
49
+ },
50
+ "services": {
51
+ "type": "array",
52
+ "minItems": 1,
53
+ "items": {
54
+ "$ref": "#/definitions/service"
55
+ }
56
+ },
57
+ "capabilities": {
58
+ "type": "array",
59
+ "items": {
60
+ "$ref": "#/definitions/capability"
61
+ },
62
+ "description": "Optional public capabilities (no implementation state)"
63
+ },
64
+ "dataTransferObjects": {
65
+ "type": "array",
66
+ "items": { "$ref": "#/definitions/dataTransferObject" },
67
+ "description": "Family API 拥有的结构化中间或正式 DTO 声明;schema 仍由 family 提供,接口编译器只校验声明形状和服务引用。"
68
+ },
69
+ "qualificationKinds": {
70
+ "type": "array",
71
+ "items": { "type": "string", "pattern": "^[a-z][A-Za-z0-9]+$" },
72
+ "uniqueItems": true,
73
+ "description": "该 Family API 区分的资格种类名称;只声明种类,不把资格证据或成熟度写入接口。"
74
+ }
75
+ },
76
+ "definitions": {
77
+ "service": {
78
+ "type": "object",
79
+ "required": ["id", "required", "kind", "intents"],
80
+ "additionalProperties": false,
81
+ "properties": {
82
+ "id": {
83
+ "type": "string",
84
+ "pattern": "^artifact\\.[a-z][a-z0-9-]+\\.[a-z][a-z0-9-]+$",
85
+ "description": "Canonical service identity: artifact.<family>.<intent>"
86
+ },
87
+ "required": {
88
+ "type": "boolean",
89
+ "description": "Whether this service is required for API conformance"
90
+ },
91
+ "kind": {
92
+ "type": "string",
93
+ "enum": ["workflow", "operation"],
94
+ "description": "workflow = stateful multi-step; operation = stateless single-step"
95
+ },
96
+ "intents": {
97
+ "type": "array",
98
+ "minItems": 1,
99
+ "items": {
100
+ "type": "string",
101
+ "enum": ["default", "help", "author", "review", "repair", "route", "audit", "generate", "batch"]
102
+ },
103
+ "uniqueItems": true
104
+ },
105
+ "accepts": {
106
+ "type": "array",
107
+ "items": {
108
+ "anyOf": [
109
+ { "type": "string", "minLength": 1 },
110
+ { "$ref": "#/definitions/protocolRef" }
111
+ ]
112
+ },
113
+ "description": "Input contract references or natural language descriptions"
114
+ },
115
+ "produces": {
116
+ "type": "array",
117
+ "items": {
118
+ "anyOf": [
119
+ { "type": "string", "minLength": 1 },
120
+ { "$ref": "#/definitions/protocolRef" }
121
+ ]
122
+ },
123
+ "description": "Output contract references or natural language descriptions"
124
+ },
125
+ "sideEffectCeiling": {
126
+ "type": "string",
127
+ "enum": ["none", "read-only", "write-authorized-artifacts", "write-review-result", "write-project-artifacts"],
128
+ "description": "Maximum side effect level this service may produce"
129
+ },
130
+ "mixSafe": {
131
+ "type": "boolean",
132
+ "default": false,
133
+ "description": "Whether this service can be bound to a different family implementation than other services in the same family"
134
+ },
135
+ "discoverable": {
136
+ "type": "object",
137
+ "additionalProperties": false,
138
+ "properties": {
139
+ "default": {
140
+ "type": "boolean",
141
+ "description": "Whether this is the default entry point"
142
+ },
143
+ "help": {
144
+ "type": "boolean",
145
+ "description": "Whether this is the help entry point"
146
+ }
147
+ }
148
+ },
149
+ "modes": {
150
+ "$ref": "#/definitions/serviceModes"
151
+ }
152
+ }
153
+ },
154
+ "capability": {
155
+ "type": "object",
156
+ "required": ["id", "summary"],
157
+ "additionalProperties": false,
158
+ "properties": {
159
+ "id": {
160
+ "type": "string",
161
+ "minLength": 1
162
+ },
163
+ "summary": {
164
+ "type": "string",
165
+ "minLength": 1
166
+ }
167
+ }
168
+ },
169
+ "serviceModes": {
170
+ "type": "object",
171
+ "required": ["supported", "default"],
172
+ "additionalProperties": false,
173
+ "properties": {
174
+ "supported": {
175
+ "type": "array",
176
+ "minItems": 1,
177
+ "items": { "type": "string", "pattern": "^[a-z][a-z0-9-]*$" },
178
+ "uniqueItems": true
179
+ },
180
+ "default": { "type": "string", "pattern": "^[a-z][a-z0-9-]*$" }
181
+ }
182
+ },
183
+ "dataTransferObject": {
184
+ "type": "object",
185
+ "required": ["id", "schemaId", "schema", "version", "digestRule", "services"],
186
+ "additionalProperties": false,
187
+ "properties": {
188
+ "id": { "type": "string", "pattern": "^[a-z][a-z0-9-]*$" },
189
+ "schemaId": { "type": "string", "minLength": 1 },
190
+ "schema": { "type": "string", "pattern": "^[^/\\\\~].*\\.json$" },
191
+ "version": { "type": "string", "minLength": 1 },
192
+ "digestRule": { "type": "string", "minLength": 1 },
193
+ "artifactBinding": { "type": "string", "minLength": 1 },
194
+ "services": {
195
+ "type": "array",
196
+ "items": {
197
+ "type": "object",
198
+ "required": ["service", "position"],
199
+ "additionalProperties": false,
200
+ "properties": {
201
+ "service": { "type": "string", "pattern": "^artifact\\.[a-z][a-z0-9-]+\\.[a-z][a-z0-9-]+$" },
202
+ "position": { "type": "string", "minLength": 1 }
203
+ }
204
+ }
205
+ }
206
+ }
207
+ },
208
+ "contractRef": {
209
+ "type": "string",
210
+ "pattern": "^[a-z][a-z0-9-]*\\.[a-z][a-z0-9-]*@[0-9]+$",
211
+ "description": "Artifact Contract major identity reference (e.g., artifact.e2e-test@1)"
212
+ },
213
+ "protocolRef": {
214
+ "type": "object",
215
+ "required": ["protocol", "version"],
216
+ "additionalProperties": false,
217
+ "properties": {
218
+ "protocol": {
219
+ "type": "string",
220
+ "minLength": 1,
221
+ "description": "Protocol name when no formal Artifact Contract exists"
222
+ },
223
+ "version": {
224
+ "type": "string",
225
+ "minLength": 1,
226
+ "description": "Protocol version"
227
+ },
228
+ "input": {
229
+ "type": "string",
230
+ "description": "Describes what the protocol accepts or produces"
231
+ }
232
+ }
233
+ }
234
+ }
235
+ }
@@ -0,0 +1,80 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://artifact-chain.dev/schemas/project-facts.schema.json",
4
+ "title": "Project Facts Evidence Envelope",
5
+ "description": "Structured envelope capturing project identity, configuration, graph state, and evidence for registry/planner consumption",
6
+ "type": "object",
7
+ "required": ["schemaVersion", "projectRoot", "configDigest", "artifactGraphSummary", "targetArtifact", "contractRevisionDigest", "proofStatus", "versionLockStatus", "sourcesFreshness", "bindingFreshness", "evidenceDigest"],
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "schemaVersion": {
11
+ "type": "integer",
12
+ "const": 1
13
+ },
14
+ "projectRoot": {
15
+ "type": "string",
16
+ "minLength": 1,
17
+ "description": "Project root identity (path or canonical identifier)"
18
+ },
19
+ "configDigest": {
20
+ "type": "string",
21
+ "pattern": "^sha256:[a-f0-9]{64}$",
22
+ "description": "SHA-256 digest of artifact-graph.config.yaml content"
23
+ },
24
+ "policyDigest": {
25
+ "type": ["string", "null"],
26
+ "pattern": "^sha256:[a-f0-9]{64}$",
27
+ "description": "SHA-256 digest of project policy overlay, or null if none"
28
+ },
29
+ "artifactGraphSummary": {
30
+ "type": "object",
31
+ "required": ["artifactCount", "edgeCount", "contextTargets"],
32
+ "additionalProperties": false,
33
+ "properties": {
34
+ "artifactCount": { "type": "integer", "minimum": 0 },
35
+ "edgeCount": { "type": "integer", "minimum": 0 },
36
+ "contextTargets": {
37
+ "type": "array",
38
+ "items": { "type": "string" }
39
+ }
40
+ }
41
+ },
42
+ "targetArtifact": {
43
+ "type": "object",
44
+ "required": ["type", "id"],
45
+ "additionalProperties": false,
46
+ "properties": {
47
+ "type": { "type": "string", "minLength": 1 },
48
+ "id": { "type": "string", "minLength": 1 }
49
+ }
50
+ },
51
+ "contractRevisionDigest": {
52
+ "type": "string",
53
+ "pattern": "^sha256:[a-f0-9]{64}$",
54
+ "description": "SHA-256 digest of the contract revision content"
55
+ },
56
+ "proofStatus": {
57
+ "type": "string",
58
+ "enum": ["present", "missing", "stale"]
59
+ },
60
+ "versionLockStatus": {
61
+ "type": "string",
62
+ "enum": ["fresh", "stale", "missing"]
63
+ },
64
+ "sourcesFreshness": {
65
+ "type": "string",
66
+ "enum": ["fresh", "stale", "missing"],
67
+ "description": "Freshness of the adopted catalog source set"
68
+ },
69
+ "bindingFreshness": {
70
+ "type": "string",
71
+ "enum": ["fresh", "stale", "missing"],
72
+ "description": "Freshness of the project provider binding"
73
+ },
74
+ "evidenceDigest": {
75
+ "type": "string",
76
+ "pattern": "^sha256:[a-f0-9]{64}$",
77
+ "description": "SHA-256 digest of the envelope content excluding this field, using stable key serialization"
78
+ }
79
+ }
80
+ }
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env node
2
+ // @feature ACA18 @scenario S-65 @decision D-ACA-18
3
+ // Deterministic Family API catalog validator.
4
+ // Validates all API files in family-apis/ and checks catalog consistency.
5
+ // JSON envelope output; non-zero exit on failure.
6
+ import { readdir, readFile } from 'node:fs/promises';
7
+ import { dirname, join } from 'node:path';
8
+ import { fileURLToPath } from 'node:url';
9
+ import {
10
+ validateFamilyApi,
11
+ validateCatalogConsistency,
12
+ loadJson,
13
+ } from './lib/family-api-validator.mjs';
14
+
15
+ const root = dirname(dirname(fileURLToPath(import.meta.url)));
16
+ const familyApisDir = join(root, 'family-apis');
17
+
18
+ const errors = [];
19
+ const warnings = [];
20
+ const checked = [];
21
+
22
+ try {
23
+ // 1. Load catalog
24
+ const catalogPath = join(familyApisDir, 'catalog.json');
25
+ let catalog;
26
+ try {
27
+ catalog = await loadJson(catalogPath);
28
+ } catch (err) {
29
+ errors.push(`Failed to load catalog.json: ${err.message}`);
30
+ emitResult();
31
+ process.exit(1);
32
+ }
33
+
34
+ // 2. Discover API files
35
+ const apiFiles = [];
36
+ const familiesDir = join(familyApisDir);
37
+ for (const entry of await readdir(familiesDir, { withFileTypes: true })) {
38
+ if (!entry.isDirectory() || entry.name === 'schema') continue;
39
+ const apiPath = join(familiesDir, entry.name, 'api.json');
40
+ try {
41
+ const content = await loadJson(apiPath);
42
+ apiFiles.push({
43
+ path: `${entry.name}/api.json`,
44
+ id: content.api?.id,
45
+ major: content.api?.major,
46
+ content,
47
+ });
48
+ } catch (err) {
49
+ // Non-API directories are fine
50
+ }
51
+ }
52
+
53
+ // 3. Validate each API file (async now)
54
+ for (const apiFile of apiFiles) {
55
+ const result = await validateFamilyApi(apiFile.content, { sourcePath: apiFile.path });
56
+ errors.push(...result.errors.map(e => typeof e === 'string' ? e : e.message || JSON.stringify(e)));
57
+ warnings.push(...result.warnings);
58
+ checked.push(apiFile.path);
59
+ }
60
+
61
+ // 4. Validate catalog consistency
62
+ const consistency = validateCatalogConsistency(
63
+ catalog,
64
+ apiFiles.map(f => ({ id: f.id, major: f.major, content: f.content })),
65
+ );
66
+ errors.push(...consistency.errors);
67
+
68
+ emitResult();
69
+ if (errors.length > 0) process.exit(1);
70
+ } catch (err) {
71
+ errors.push(`Unexpected error: ${err.message}`);
72
+ emitResult();
73
+ process.exit(1);
74
+ }
75
+
76
+ function emitResult() {
77
+ const envelope = {
78
+ ok: errors.length === 0,
79
+ checked,
80
+ errors,
81
+ warnings,
82
+ };
83
+ process.stdout.write(JSON.stringify(envelope, null, 2) + '\n');
84
+ }