@xenonbyte/da-vinci-workflow 0.1.26 → 0.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.
Files changed (45) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +28 -65
  3. package/README.zh-CN.md +28 -65
  4. package/bin/da-vinci-tui.js +8 -0
  5. package/commands/claude/dv/continue.md +5 -0
  6. package/commands/codex/prompts/dv-continue.md +6 -1
  7. package/commands/gemini/dv/continue.toml +5 -0
  8. package/commands/templates/dv-continue.shared.md +33 -0
  9. package/docs/dv-command-reference.md +35 -0
  10. package/docs/execution-chain-migration.md +46 -0
  11. package/docs/execution-chain-plan.md +125 -0
  12. package/docs/prompt-entrypoints.md +8 -0
  13. package/docs/skill-usage.md +217 -0
  14. package/docs/workflow-examples.md +10 -0
  15. package/docs/workflow-overview.md +26 -0
  16. package/docs/zh-CN/dv-command-reference.md +35 -0
  17. package/docs/zh-CN/execution-chain-migration.md +46 -0
  18. package/docs/zh-CN/prompt-entrypoints.md +8 -0
  19. package/docs/zh-CN/skill-usage.md +217 -0
  20. package/docs/zh-CN/workflow-examples.md +10 -0
  21. package/docs/zh-CN/workflow-overview.md +26 -0
  22. package/lib/artifact-parsers.js +120 -0
  23. package/lib/audit.js +61 -0
  24. package/lib/cli.js +351 -13
  25. package/lib/diff-spec.js +242 -0
  26. package/lib/execution-signals.js +136 -0
  27. package/lib/lint-bindings.js +143 -0
  28. package/lib/lint-spec.js +408 -0
  29. package/lib/lint-tasks.js +176 -0
  30. package/lib/planning-parsers.js +567 -0
  31. package/lib/scaffold.js +193 -0
  32. package/lib/scope-check.js +603 -0
  33. package/lib/sidecars.js +369 -0
  34. package/lib/supervisor-review.js +28 -3
  35. package/lib/utils.js +10 -2
  36. package/lib/verify.js +652 -0
  37. package/lib/workflow-contract.js +107 -0
  38. package/lib/workflow-persisted-state.js +297 -0
  39. package/lib/workflow-state.js +785 -0
  40. package/package.json +13 -3
  41. package/references/artifact-templates.md +26 -0
  42. package/references/checkpoints.md +14 -0
  43. package/references/modes.md +10 -0
  44. package/tui/catalog.js +1190 -0
  45. package/tui/index.js +727 -0
package/lib/verify.js ADDED
@@ -0,0 +1,652 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const { STATUS } = require("./workflow-contract");
4
+ const {
5
+ normalizeText,
6
+ unique,
7
+ resolveImplementationLanding,
8
+ resolveChangeDir,
9
+ parseBindingsArtifact,
10
+ parseTasksArtifact,
11
+ parseVerificationArtifact,
12
+ parseRuntimeSpecs,
13
+ readChangeArtifacts,
14
+ readArtifactTexts
15
+ } = require("./planning-parsers");
16
+
17
+ const CODE_FILE_EXTENSIONS = new Set([".js", ".jsx", ".ts", ".tsx", ".html", ".css", ".scss"]);
18
+ const NON_IMPLEMENTATION_DIR_NAMES = new Set([
19
+ ".git",
20
+ ".da-vinci",
21
+ "node_modules",
22
+ "test",
23
+ "tests",
24
+ "__tests__",
25
+ "__mocks__",
26
+ "fixtures",
27
+ "__fixtures__",
28
+ "scripts",
29
+ "coverage",
30
+ "dist",
31
+ "build",
32
+ ".next",
33
+ ".nuxt",
34
+ ".storybook",
35
+ "storybook-static",
36
+ "cypress",
37
+ "playwright-report",
38
+ "e2e"
39
+ ]);
40
+ const NON_IMPLEMENTATION_FILE_PATTERNS = [
41
+ /\.test\.[a-z0-9]+$/i,
42
+ /\.spec\.[a-z0-9]+$/i,
43
+ /\.stories\.[a-z0-9]+$/i,
44
+ /\.story\.[a-z0-9]+$/i
45
+ ];
46
+ const MAX_SCANNED_FILES = 2000;
47
+ const MAX_SCANNED_BYTES_PER_FILE = 512 * 1024;
48
+ const MAX_SCANNED_DIRECTORIES = 10000;
49
+ const MAX_SCAN_DEPTH = 32;
50
+
51
+ function buildEnvelope(name, projectRoot, strict) {
52
+ return {
53
+ status: STATUS.PASS,
54
+ failures: [],
55
+ warnings: [],
56
+ notes: [],
57
+ projectRoot,
58
+ changeId: null,
59
+ strict,
60
+ surface: name
61
+ };
62
+ }
63
+
64
+ function finalize(result, options = {}) {
65
+ result.failures = unique(result.failures);
66
+ result.warnings = unique(result.warnings);
67
+ result.notes = unique(result.notes);
68
+
69
+ if (result.failures.length > 0) {
70
+ result.status = STATUS.BLOCK;
71
+ return result;
72
+ }
73
+ if (result.warnings.length > 0) {
74
+ result.status = result.strict || options.strictWarnings ? STATUS.BLOCK : STATUS.WARN;
75
+ return result;
76
+ }
77
+ result.status = STATUS.PASS;
78
+ return result;
79
+ }
80
+
81
+ function isNonImplementationDirName(name) {
82
+ return NON_IMPLEMENTATION_DIR_NAMES.has(String(name || "").toLowerCase());
83
+ }
84
+
85
+ function isNonImplementationFileName(name) {
86
+ const normalized = String(name || "").trim();
87
+ if (!normalized) {
88
+ return false;
89
+ }
90
+ return NON_IMPLEMENTATION_FILE_PATTERNS.some((pattern) => pattern.test(normalized));
91
+ }
92
+
93
+ function collectCodeFiles(projectRoot) {
94
+ const files = [];
95
+ const scan = {
96
+ truncatedByFileLimit: false,
97
+ truncatedByDirectoryLimit: false,
98
+ depthLimitHits: 0,
99
+ skippedSymlinks: 0,
100
+ readErrors: 0,
101
+ scannedDirectories: 0
102
+ };
103
+ const queue = [{ dir: projectRoot, depth: 0 }];
104
+ const visited = new Set();
105
+ while (queue.length > 0 && files.length < MAX_SCANNED_FILES) {
106
+ const current = queue.pop();
107
+ if (current.depth > MAX_SCAN_DEPTH) {
108
+ scan.depthLimitHits += 1;
109
+ continue;
110
+ }
111
+
112
+ let resolvedCurrent;
113
+ try {
114
+ resolvedCurrent = fs.realpathSync(current.dir);
115
+ } catch (_error) {
116
+ scan.readErrors += 1;
117
+ continue;
118
+ }
119
+ if (visited.has(resolvedCurrent)) {
120
+ continue;
121
+ }
122
+ visited.add(resolvedCurrent);
123
+ scan.scannedDirectories += 1;
124
+ if (scan.scannedDirectories > MAX_SCANNED_DIRECTORIES) {
125
+ scan.truncatedByDirectoryLimit = true;
126
+ break;
127
+ }
128
+
129
+ let entries = [];
130
+ try {
131
+ entries = fs.readdirSync(current.dir, { withFileTypes: true });
132
+ } catch (_error) {
133
+ scan.readErrors += 1;
134
+ continue;
135
+ }
136
+ for (const entry of entries) {
137
+ if (files.length >= MAX_SCANNED_FILES) {
138
+ scan.truncatedByFileLimit = true;
139
+ break;
140
+ }
141
+ if (entry.isDirectory() && isNonImplementationDirName(entry.name)) {
142
+ continue;
143
+ }
144
+ if (entry.isSymbolicLink()) {
145
+ scan.skippedSymlinks += 1;
146
+ continue;
147
+ }
148
+ const absolutePath = path.join(current.dir, entry.name);
149
+ if (entry.isDirectory()) {
150
+ if (current.depth >= MAX_SCAN_DEPTH) {
151
+ scan.depthLimitHits += 1;
152
+ continue;
153
+ }
154
+ queue.push({
155
+ dir: absolutePath,
156
+ depth: current.depth + 1
157
+ });
158
+ continue;
159
+ }
160
+ if (!entry.isFile()) {
161
+ continue;
162
+ }
163
+ if (!CODE_FILE_EXTENSIONS.has(path.extname(entry.name).toLowerCase())) {
164
+ continue;
165
+ }
166
+ if (isNonImplementationFileName(entry.name)) {
167
+ continue;
168
+ }
169
+ files.push(absolutePath);
170
+ }
171
+ }
172
+ if (files.length >= MAX_SCANNED_FILES) {
173
+ scan.truncatedByFileLimit = true;
174
+ }
175
+ return {
176
+ files: files.sort(),
177
+ scan
178
+ };
179
+ }
180
+
181
+ function safeReadFile(filePath) {
182
+ try {
183
+ const stat = fs.statSync(filePath);
184
+ if (stat.size > MAX_SCANNED_BYTES_PER_FILE) {
185
+ return "";
186
+ }
187
+ return fs.readFileSync(filePath, "utf8");
188
+ } catch (_error) {
189
+ return "";
190
+ }
191
+ }
192
+
193
+ function readCodeFileForScan(filePath) {
194
+ try {
195
+ const stat = fs.statSync(filePath);
196
+ if (stat.size > MAX_SCANNED_BYTES_PER_FILE) {
197
+ return {
198
+ text: "",
199
+ bytesRead: 0,
200
+ skippedLarge: true,
201
+ readError: false
202
+ };
203
+ }
204
+ return {
205
+ text: fs.readFileSync(filePath, "utf8"),
206
+ bytesRead: stat.size,
207
+ skippedLarge: false,
208
+ readError: false
209
+ };
210
+ } catch (_error) {
211
+ return {
212
+ text: "",
213
+ bytesRead: 0,
214
+ skippedLarge: false,
215
+ readError: true
216
+ };
217
+ }
218
+ }
219
+
220
+ function allCovered(checks) {
221
+ for (const check of checks) {
222
+ if (!check.covered) {
223
+ return false;
224
+ }
225
+ }
226
+ return true;
227
+ }
228
+
229
+ function createSharedSetup(projectPathInput, options = {}) {
230
+ const projectRoot = path.resolve(projectPathInput || process.cwd());
231
+ const strict = options && options.strict === true;
232
+ const requestedChangeId = options && options.changeId ? String(options.changeId).trim() : "";
233
+ const resolved = resolveChangeDir(projectRoot, requestedChangeId);
234
+
235
+ let artifactPaths = null;
236
+ let artifacts = null;
237
+ if (resolved.changeDir) {
238
+ artifactPaths = readChangeArtifacts(projectRoot, resolved.changeId);
239
+ artifacts = readArtifactTexts(artifactPaths);
240
+ }
241
+
242
+ return {
243
+ projectRoot,
244
+ strict,
245
+ requestedChangeId,
246
+ resolved,
247
+ artifactPaths,
248
+ artifacts
249
+ };
250
+ }
251
+
252
+ function commonSetup(surface, projectPathInput, options) {
253
+ const sharedSetup =
254
+ options && options.sharedSetup
255
+ ? options.sharedSetup
256
+ : createSharedSetup(projectPathInput, options || {});
257
+ const strict =
258
+ (options && options.strict === true) || (sharedSetup && sharedSetup.strict === true);
259
+ const result = buildEnvelope(surface, sharedSetup.projectRoot, strict);
260
+
261
+ result.failures.push(...sharedSetup.resolved.failures);
262
+ result.notes.push(...sharedSetup.resolved.notes);
263
+ if (!sharedSetup.resolved.changeDir) {
264
+ return {
265
+ result: finalize(result),
266
+ resolved: sharedSetup.resolved,
267
+ artifacts: sharedSetup.artifacts,
268
+ artifactPaths: sharedSetup.artifactPaths,
269
+ sharedSetup
270
+ };
271
+ }
272
+ result.changeId = sharedSetup.resolved.changeId;
273
+
274
+ return {
275
+ result,
276
+ resolved: sharedSetup.resolved,
277
+ artifacts: sharedSetup.artifacts,
278
+ artifactPaths: sharedSetup.artifactPaths,
279
+ sharedSetup
280
+ };
281
+ }
282
+
283
+ function verifyBindings(projectPathInput, options = {}) {
284
+ const setup = commonSetup("verify-bindings", projectPathInput, options);
285
+ const { result, artifacts } = setup;
286
+ if (!artifacts) {
287
+ return result;
288
+ }
289
+
290
+ if (!artifacts.bindings) {
291
+ result.failures.push("Missing `pencil-bindings.md` for verify-bindings.");
292
+ return finalize(result);
293
+ }
294
+
295
+ const parsed = parseBindingsArtifact(artifacts.bindings);
296
+ if (parsed.mappings.length === 0) {
297
+ result.failures.push("No implementation mappings found in `pencil-bindings.md`.");
298
+ return finalize(result);
299
+ }
300
+
301
+ for (const mapping of parsed.mappings) {
302
+ const landing = resolveImplementationLanding(result.projectRoot, mapping.implementation);
303
+ if (!landing) {
304
+ result.failures.push(
305
+ `Missing implementation landing for binding "${mapping.implementation}" -> "${mapping.designPage}".`
306
+ );
307
+ }
308
+ }
309
+
310
+ if (parsed.malformed.length > 0) {
311
+ for (const malformed of parsed.malformed) {
312
+ result.warnings.push(`Malformed binding mapping entry: "${malformed}".`);
313
+ }
314
+ }
315
+
316
+ return finalize(result);
317
+ }
318
+
319
+ function verifyImplementation(projectPathInput, options = {}) {
320
+ const setup = commonSetup("verify-implementation", projectPathInput, options);
321
+ const { result, resolved, artifacts } = setup;
322
+ if (!artifacts) {
323
+ return result;
324
+ }
325
+
326
+ const codeScan = collectCodeFiles(result.projectRoot);
327
+ const codeFiles = codeScan.files;
328
+ if (codeFiles.length === 0) {
329
+ result.failures.push("No implementation files were found for verify-implementation.");
330
+ return finalize(result);
331
+ }
332
+
333
+ const specRecords = parseRuntimeSpecs(resolved.changeDir, result.projectRoot);
334
+ const tasksArtifact = parseTasksArtifact(artifacts.tasks || "");
335
+
336
+ if (specRecords.length === 0) {
337
+ result.failures.push("Missing runtime specs for verify-implementation.");
338
+ return finalize(result);
339
+ }
340
+
341
+ const stateChecks = [];
342
+ for (const record of specRecords) {
343
+ const states = record.parsed.sections.states.items || [];
344
+ for (const stateItem of states) {
345
+ const state = normalizeText(String(stateItem || "").split(":")[0]);
346
+ if (!state) {
347
+ continue;
348
+ }
349
+ const stateTokens = state.split(" ").filter((token) => token.length >= 3);
350
+ if (stateTokens.length === 0) {
351
+ continue;
352
+ }
353
+ stateChecks.push({
354
+ recordPath: record.path,
355
+ stateItem,
356
+ tokens: stateTokens,
357
+ covered: false
358
+ });
359
+ }
360
+ }
361
+
362
+ const taskGroupChecks = [];
363
+ for (const group of tasksArtifact.taskGroups) {
364
+ const titleTokens = normalizeText(group.title)
365
+ .split(" ")
366
+ .filter((token) => token.length >= 4);
367
+ if (titleTokens.length === 0) {
368
+ continue;
369
+ }
370
+
371
+ taskGroupChecks.push({
372
+ group,
373
+ tokens: titleTokens,
374
+ covered: false
375
+ });
376
+ }
377
+
378
+ let scannedBytes = 0;
379
+ let skippedLargeFiles = 0;
380
+ let readErrors = 0;
381
+ for (const codeFile of codeFiles) {
382
+ const read = readCodeFileForScan(codeFile);
383
+ scannedBytes += read.bytesRead;
384
+ if (read.skippedLarge) {
385
+ skippedLargeFiles += 1;
386
+ continue;
387
+ }
388
+ if (read.readError) {
389
+ readErrors += 1;
390
+ continue;
391
+ }
392
+
393
+ const lower = String(read.text || "").toLowerCase();
394
+ if (!lower) {
395
+ continue;
396
+ }
397
+
398
+ for (const check of stateChecks) {
399
+ if (check.covered) {
400
+ continue;
401
+ }
402
+ check.covered = check.tokens.some((token) => lower.includes(token));
403
+ }
404
+ for (const check of taskGroupChecks) {
405
+ if (check.covered) {
406
+ continue;
407
+ }
408
+ check.covered = check.tokens.some((token) => lower.includes(token));
409
+ }
410
+
411
+ if (allCovered(stateChecks) && allCovered(taskGroupChecks)) {
412
+ break;
413
+ }
414
+ }
415
+
416
+ for (const check of stateChecks) {
417
+ if (!check.covered) {
418
+ result.warnings.push(
419
+ `State coverage may be missing in implementation: "${check.stateItem}" (${check.recordPath}).`
420
+ );
421
+ }
422
+ }
423
+ for (const check of taskGroupChecks) {
424
+ if (!check.covered) {
425
+ result.warnings.push(
426
+ `Task-group intent may be missing in implementation: "${check.group.id}. ${check.group.title}".`
427
+ );
428
+ }
429
+ }
430
+
431
+ if (codeScan.scan.truncatedByFileLimit) {
432
+ result.warnings.push(
433
+ `verify-implementation hit file scan limit (${MAX_SCANNED_FILES}); deep coverage may be incomplete.`
434
+ );
435
+ }
436
+ if (codeScan.scan.truncatedByDirectoryLimit) {
437
+ result.warnings.push(
438
+ `verify-implementation hit directory scan limit (${MAX_SCANNED_DIRECTORIES}); coverage may be incomplete.`
439
+ );
440
+ }
441
+ if (codeScan.scan.readErrors + readErrors > 0) {
442
+ result.warnings.push(
443
+ `verify-implementation skipped unreadable files/directories (${codeScan.scan.readErrors + readErrors}).`
444
+ );
445
+ }
446
+ if (codeScan.scan.depthLimitHits > 0) {
447
+ result.notes.push(
448
+ `verify-implementation enforced max scan depth (${MAX_SCAN_DEPTH}); skipped deeper paths: ${codeScan.scan.depthLimitHits}.`
449
+ );
450
+ }
451
+ if (codeScan.scan.skippedSymlinks > 0) {
452
+ result.notes.push(
453
+ `verify-implementation skipped symlink entries during scan: ${codeScan.scan.skippedSymlinks}.`
454
+ );
455
+ }
456
+ if (skippedLargeFiles > 0) {
457
+ result.notes.push(
458
+ `verify-implementation skipped files larger than ${MAX_SCANNED_BYTES_PER_FILE} bytes: ${skippedLargeFiles}.`
459
+ );
460
+ }
461
+
462
+ result.summary = {
463
+ codeFiles: codeFiles.length,
464
+ specFiles: specRecords.length,
465
+ taskGroups: tasksArtifact.taskGroups.length,
466
+ scannedBytes
467
+ };
468
+ return finalize(result);
469
+ }
470
+
471
+ function verifyStructure(projectPathInput, options = {}) {
472
+ const setup = commonSetup("verify-structure", projectPathInput, options);
473
+ const { result, artifacts } = setup;
474
+ if (!artifacts) {
475
+ return result;
476
+ }
477
+
478
+ if (!artifacts.bindings) {
479
+ result.failures.push("Missing `pencil-bindings.md` for verify-structure.");
480
+ return finalize(result);
481
+ }
482
+
483
+ const bindings = parseBindingsArtifact(artifacts.bindings);
484
+ const confidence = [];
485
+
486
+ if (bindings.mappings.length === 0) {
487
+ result.failures.push("No binding mappings available for structural comparison.");
488
+ return finalize(result);
489
+ }
490
+
491
+ for (const mapping of bindings.mappings) {
492
+ const landing = resolveImplementationLanding(result.projectRoot, mapping.implementation);
493
+ if (!landing) {
494
+ result.failures.push(`Structural check missing implementation file for "${mapping.implementation}".`);
495
+ continue;
496
+ }
497
+
498
+ const ext = path.extname(landing).toLowerCase();
499
+ const source = safeReadFile(landing);
500
+ const normalizedSource = normalizeText(source);
501
+ const pageTokens = normalizeText(mapping.designPage)
502
+ .split(" ")
503
+ .filter((token) => token.length >= 3);
504
+
505
+ if (ext === ".html" || ext === ".tsx" || ext === ".jsx" || ext === ".js") {
506
+ const hasMarkupIndicators = /<section|<main|<header|<footer|<div/.test(source);
507
+ if (hasMarkupIndicators) {
508
+ confidence.push({ mapping: mapping.implementation, mode: "markup", confidence: "high" });
509
+ } else {
510
+ confidence.push({ mapping: mapping.implementation, mode: "heuristic", confidence: "medium" });
511
+ result.warnings.push(
512
+ `verify-structure used heuristic mode for "${mapping.implementation}" because markup structure was limited.`
513
+ );
514
+ }
515
+ } else {
516
+ confidence.push({ mapping: mapping.implementation, mode: "heuristic", confidence: "low" });
517
+ result.warnings.push(
518
+ `verify-structure used heuristic mode for "${mapping.implementation}" due to unsupported file type ${ext || "(none)"}.`
519
+ );
520
+ }
521
+
522
+ if (pageTokens.length > 0) {
523
+ const covered = pageTokens.some((token) => normalizedSource.includes(token));
524
+ if (!covered) {
525
+ result.warnings.push(
526
+ `Structural drift suspected: design page "${mapping.designPage}" tokens not found in "${mapping.implementation}".`
527
+ );
528
+ }
529
+ }
530
+ }
531
+
532
+ result.structure = {
533
+ confidence
534
+ };
535
+ return finalize(result);
536
+ }
537
+
538
+ function verifyCoverage(projectPathInput, options = {}) {
539
+ const projectRoot = path.resolve(projectPathInput || process.cwd());
540
+ const strict = options.strict === true;
541
+ const changeId = options.changeId ? String(options.changeId).trim() : "";
542
+ const sharedSetup = createSharedSetup(projectRoot, { changeId, strict });
543
+ const sharedOptions = { changeId, strict, sharedSetup };
544
+ const bindingsResult = verifyBindings(projectRoot, sharedOptions);
545
+ const implementationResult = verifyImplementation(projectRoot, sharedOptions);
546
+ const structureResult = verifyStructure(projectRoot, sharedOptions);
547
+
548
+ const result = buildEnvelope("verify-coverage", projectRoot, strict);
549
+ result.changeId = bindingsResult.changeId || implementationResult.changeId || structureResult.changeId || null;
550
+ result.notes.push("verify-coverage aggregates requirement, design, binding, code, and test coverage signals.");
551
+
552
+ if (bindingsResult.status === STATUS.BLOCK) {
553
+ result.failures.push("verify-bindings reported BLOCK.");
554
+ } else if (bindingsResult.status === STATUS.WARN) {
555
+ result.warnings.push("verify-bindings reported WARN.");
556
+ }
557
+ if (implementationResult.status === STATUS.BLOCK) {
558
+ result.failures.push("verify-implementation reported BLOCK.");
559
+ } else if (implementationResult.status === STATUS.WARN) {
560
+ result.warnings.push("verify-implementation reported WARN.");
561
+ }
562
+ if (structureResult.status === STATUS.BLOCK) {
563
+ result.failures.push("verify-structure reported BLOCK.");
564
+ } else if (structureResult.status === STATUS.WARN) {
565
+ result.warnings.push("verify-structure reported WARN.");
566
+ }
567
+
568
+ if (sharedSetup.artifacts && sharedSetup.artifacts.verification) {
569
+ const verificationArtifact = parseVerificationArtifact(sharedSetup.artifacts.verification);
570
+ if (verificationArtifact.requirementCoverage.length === 0) {
571
+ result.warnings.push("`verification.md` is missing requirement-coverage evidence.");
572
+ }
573
+ if (verificationArtifact.designCoverage.length === 0) {
574
+ result.warnings.push("`verification.md` is missing design-coverage evidence.");
575
+ }
576
+ const hasTestEvidence = verificationArtifact.outcome.some((item) => /test|coverage|verify/i.test(item));
577
+ if (!hasTestEvidence) {
578
+ result.warnings.push("`verification.md` outcome lacks explicit test evidence.");
579
+ }
580
+ } else {
581
+ result.warnings.push("Missing `verification.md`; coverage evidence is incomplete.");
582
+ }
583
+
584
+ result.components = {
585
+ bindings: {
586
+ status: bindingsResult.status,
587
+ failures: bindingsResult.failures,
588
+ warnings: bindingsResult.warnings
589
+ },
590
+ implementation: {
591
+ status: implementationResult.status,
592
+ failures: implementationResult.failures,
593
+ warnings: implementationResult.warnings
594
+ },
595
+ structure: {
596
+ status: structureResult.status,
597
+ failures: structureResult.failures,
598
+ warnings: structureResult.warnings,
599
+ confidence: structureResult.structure ? structureResult.structure.confidence : []
600
+ }
601
+ };
602
+
603
+ return finalize(result);
604
+ }
605
+
606
+ function formatVerifyReport(result, title = "Da Vinci verify") {
607
+ const lines = [
608
+ title,
609
+ `Project: ${result.projectRoot}`,
610
+ `Change: ${result.changeId || "(not selected)"}`,
611
+ `Strict mode: ${result.strict ? "yes" : "no"}`,
612
+ `Status: ${result.status}`
613
+ ];
614
+ if (result.summary) {
615
+ for (const [key, value] of Object.entries(result.summary)) {
616
+ lines.push(`${key}: ${value}`);
617
+ }
618
+ }
619
+ if (result.failures.length > 0) {
620
+ lines.push("", "Failures:");
621
+ for (const failure of result.failures) {
622
+ lines.push(`- ${failure}`);
623
+ }
624
+ }
625
+ if (result.warnings.length > 0) {
626
+ lines.push("", "Warnings:");
627
+ for (const warning of result.warnings) {
628
+ lines.push(`- ${warning}`);
629
+ }
630
+ }
631
+ if (result.notes.length > 0) {
632
+ lines.push("", "Notes:");
633
+ for (const note of result.notes) {
634
+ lines.push(`- ${note}`);
635
+ }
636
+ }
637
+ if (result.structure && Array.isArray(result.structure.confidence) && result.structure.confidence.length > 0) {
638
+ lines.push("", "Structure confidence:");
639
+ for (const item of result.structure.confidence) {
640
+ lines.push(`- ${item.mapping}: ${item.mode} (${item.confidence})`);
641
+ }
642
+ }
643
+ return lines.join("\n");
644
+ }
645
+
646
+ module.exports = {
647
+ verifyBindings,
648
+ verifyImplementation,
649
+ verifyStructure,
650
+ verifyCoverage,
651
+ formatVerifyReport
652
+ };