scene-capability-engine 3.3.26 → 3.4.6

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.
@@ -3,7 +3,8 @@ const path = require('path');
3
3
  const chalk = require('chalk');
4
4
  const {
5
5
  ensureSpecDomainArtifacts,
6
- validateSpecDomainArtifacts
6
+ validateSpecDomainArtifacts,
7
+ analyzeSpecDomainCoverage
7
8
  } = require('../spec/domain-modeling');
8
9
 
9
10
  function normalizeText(value) {
@@ -109,18 +110,29 @@ async function runSpecDomainValidateCommand(options = {}, dependencies = {}) {
109
110
  await assertSpecExists(projectPath, specId, fileSystem);
110
111
 
111
112
  const validation = await validateSpecDomainArtifacts(projectPath, specId, fileSystem);
113
+ const coverage = await analyzeSpecDomainCoverage(projectPath, specId, fileSystem);
112
114
  const payload = {
113
115
  mode: 'spec-domain-validate',
114
116
  spec_id: specId,
115
117
  passed: validation.passed,
116
118
  ratio: validation.ratio,
117
119
  details: validation.details,
118
- warnings: validation.warnings
120
+ warnings: validation.warnings,
121
+ coverage: {
122
+ passed: coverage.passed,
123
+ coverage_ratio: coverage.coverage_ratio,
124
+ covered_count: coverage.covered_count,
125
+ total_count: coverage.total_count,
126
+ uncovered: coverage.uncovered
127
+ }
119
128
  };
120
129
 
121
130
  if (options.failOnError && !validation.passed) {
122
131
  throw new Error(`spec domain validation failed: ${validation.warnings.join('; ')}`);
123
132
  }
133
+ if (options.failOnGap && !coverage.passed) {
134
+ throw new Error(`spec domain coverage has gaps: ${coverage.uncovered.join(', ')}`);
135
+ }
124
136
 
125
137
  if (options.json) {
126
138
  console.log(JSON.stringify(payload, null, 2));
@@ -138,6 +150,49 @@ async function runSpecDomainValidateCommand(options = {}, dependencies = {}) {
138
150
  return payload;
139
151
  }
140
152
 
153
+ async function runSpecDomainCoverageCommand(options = {}, dependencies = {}) {
154
+ const projectPath = dependencies.projectPath || process.cwd();
155
+ const fileSystem = dependencies.fileSystem || fs;
156
+ const specId = resolveSpecId(options);
157
+ if (!specId) {
158
+ throw new Error('--spec is required');
159
+ }
160
+ await assertSpecExists(projectPath, specId, fileSystem);
161
+
162
+ const coverage = await analyzeSpecDomainCoverage(projectPath, specId, fileSystem);
163
+ const payload = {
164
+ mode: 'spec-domain-coverage',
165
+ spec_id: specId,
166
+ passed: coverage.passed,
167
+ coverage_ratio: coverage.coverage_ratio,
168
+ covered_count: coverage.covered_count,
169
+ total_count: coverage.total_count,
170
+ uncovered: coverage.uncovered,
171
+ items: coverage.items
172
+ };
173
+
174
+ if (options.failOnGap && !coverage.passed) {
175
+ throw new Error(`spec domain coverage has gaps: ${coverage.uncovered.join(', ')}`);
176
+ }
177
+
178
+ if (options.json) {
179
+ console.log(JSON.stringify(payload, null, 2));
180
+ } else if (!options.silent) {
181
+ if (coverage.passed) {
182
+ console.log(chalk.green('✓ Spec domain closed-loop coverage passed'));
183
+ } else {
184
+ console.log(chalk.red('✗ Spec domain closed-loop coverage has gaps'));
185
+ coverage.items
186
+ .filter((item) => !item.covered)
187
+ .forEach((item) => {
188
+ console.log(chalk.gray(` - ${item.id}: ${item.label}`));
189
+ });
190
+ }
191
+ }
192
+
193
+ return payload;
194
+ }
195
+
141
196
  function registerSpecDomainCommand(program) {
142
197
  const specDomain = program
143
198
  .command('spec-domain')
@@ -194,6 +249,7 @@ function registerSpecDomainCommand(program) {
194
249
  .description('Validate problem-domain artifacts for a Spec')
195
250
  .requiredOption('--spec <name>', 'Spec identifier')
196
251
  .option('--fail-on-error', 'Exit non-zero when validation fails')
252
+ .option('--fail-on-gap', 'Exit non-zero when closed-loop coverage is incomplete')
197
253
  .option('--json', 'Output machine-readable JSON')
198
254
  .action(async (options) => {
199
255
  try {
@@ -207,11 +263,31 @@ function registerSpecDomainCommand(program) {
207
263
  process.exit(1);
208
264
  }
209
265
  });
266
+
267
+ specDomain
268
+ .command('coverage')
269
+ .description('Analyze scene-closed-loop research coverage for a Spec')
270
+ .requiredOption('--spec <name>', 'Spec identifier')
271
+ .option('--fail-on-gap', 'Exit non-zero when closed-loop coverage is incomplete')
272
+ .option('--json', 'Output machine-readable JSON')
273
+ .action(async (options) => {
274
+ try {
275
+ await runSpecDomainCoverageCommand(options);
276
+ } catch (error) {
277
+ if (options.json) {
278
+ console.log(JSON.stringify({ success: false, error: error.message }, null, 2));
279
+ } else {
280
+ console.error(chalk.red('❌ spec-domain coverage failed:'), error.message);
281
+ }
282
+ process.exit(1);
283
+ }
284
+ });
210
285
  }
211
286
 
212
287
  module.exports = {
213
288
  runSpecDomainInitCommand,
214
289
  runSpecDomainRefreshCommand,
215
290
  runSpecDomainValidateCommand,
291
+ runSpecDomainCoverageCommand,
216
292
  registerSpecDomainCommand
217
293
  };