@xenonbyte/da-vinci-workflow 0.1.25 → 0.2.1

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 (84) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +48 -67
  3. package/README.zh-CN.md +36 -66
  4. package/SKILL.md +3 -0
  5. package/commands/claude/dv/continue.md +5 -0
  6. package/commands/claude/dv/design.md +1 -0
  7. package/commands/codex/prompts/dv-continue.md +6 -1
  8. package/commands/codex/prompts/dv-design.md +1 -0
  9. package/commands/gemini/dv/continue.toml +5 -0
  10. package/commands/gemini/dv/design.toml +1 -0
  11. package/commands/templates/dv-continue.shared.md +33 -0
  12. package/docs/dv-command-reference.md +45 -2
  13. package/docs/execution-chain-migration.md +46 -0
  14. package/docs/execution-chain-plan.md +125 -0
  15. package/docs/pencil-rendering-workflow.md +9 -7
  16. package/docs/prompt-entrypoints.md +6 -0
  17. package/docs/prompt-presets/README.md +4 -0
  18. package/docs/visual-assist-presets/README.md +4 -0
  19. package/docs/workflow-examples.md +23 -11
  20. package/docs/workflow-overview.md +27 -0
  21. package/docs/zh-CN/dv-command-reference.md +45 -2
  22. package/docs/zh-CN/execution-chain-migration.md +46 -0
  23. package/docs/zh-CN/pencil-rendering-workflow.md +9 -7
  24. package/docs/zh-CN/prompt-entrypoints.md +6 -0
  25. package/docs/zh-CN/prompt-presets/README.md +5 -1
  26. package/docs/zh-CN/visual-assist-presets/README.md +5 -1
  27. package/docs/zh-CN/workflow-examples.md +23 -11
  28. package/docs/zh-CN/workflow-overview.md +27 -0
  29. package/examples/greenfield-spec-markupflow/README.md +6 -1
  30. package/lib/artifact-parsers.js +120 -0
  31. package/lib/async-offload-worker.js +26 -0
  32. package/lib/async-offload.js +82 -0
  33. package/lib/audit-parsers.js +152 -32
  34. package/lib/audit.js +145 -23
  35. package/lib/cli.js +1068 -437
  36. package/lib/diff-spec.js +242 -0
  37. package/lib/execution-signals.js +136 -0
  38. package/lib/fs-safety.js +1 -4
  39. package/lib/icon-aliases.js +7 -7
  40. package/lib/icon-search.js +21 -14
  41. package/lib/icon-sync.js +220 -41
  42. package/lib/install.js +128 -60
  43. package/lib/lint-bindings.js +143 -0
  44. package/lib/lint-spec.js +408 -0
  45. package/lib/lint-tasks.js +176 -0
  46. package/lib/mcp-runtime-gate.js +4 -7
  47. package/lib/pen-persistence.js +318 -46
  48. package/lib/pencil-lock.js +237 -25
  49. package/lib/pencil-preflight.js +233 -12
  50. package/lib/pencil-session.js +216 -36
  51. package/lib/planning-parsers.js +567 -0
  52. package/lib/scaffold.js +193 -0
  53. package/lib/scope-check.js +603 -0
  54. package/lib/sidecars.js +369 -0
  55. package/lib/supervisor-review.js +82 -35
  56. package/lib/utils.js +129 -0
  57. package/lib/verify.js +652 -0
  58. package/lib/workflow-bootstrap.js +255 -0
  59. package/lib/workflow-contract.js +107 -0
  60. package/lib/workflow-persisted-state.js +297 -0
  61. package/lib/workflow-state.js +785 -0
  62. package/package.json +21 -3
  63. package/references/artifact-templates.md +26 -0
  64. package/references/checkpoints.md +16 -0
  65. package/references/design-inputs.md +2 -0
  66. package/references/modes.md +10 -0
  67. package/references/pencil-design-to-code.md +2 -0
  68. package/scripts/fixtures/complex-sample.pen +0 -295
  69. package/scripts/fixtures/mock-pencil.js +0 -49
  70. package/scripts/test-audit-context-delta.js +0 -446
  71. package/scripts/test-audit-design-supervisor.js +0 -691
  72. package/scripts/test-audit-safety.js +0 -92
  73. package/scripts/test-icon-aliases.js +0 -96
  74. package/scripts/test-icon-search.js +0 -77
  75. package/scripts/test-icon-sync.js +0 -178
  76. package/scripts/test-mcp-runtime-gate.js +0 -287
  77. package/scripts/test-mode-consistency.js +0 -344
  78. package/scripts/test-pen-persistence.js +0 -403
  79. package/scripts/test-pencil-lock.js +0 -130
  80. package/scripts/test-pencil-preflight.js +0 -169
  81. package/scripts/test-pencil-session.js +0 -192
  82. package/scripts/test-persistence-flows.js +0 -345
  83. package/scripts/test-supervisor-review-cli.js +0 -619
  84. package/scripts/test-supervisor-review-integration.js +0 -115
package/lib/audit.js CHANGED
@@ -1,8 +1,17 @@
1
1
  const fs = require("fs");
2
2
  const path = require("path");
3
- const { getStandardPenStatePath, readPenState, hashPenDocument, readPenDocument } = require("./pen-persistence");
3
+ const {
4
+ getStandardPenStatePath,
5
+ readPenState,
6
+ hashPenDocument,
7
+ readPenDocument,
8
+ assertPenDocumentShape
9
+ } = require("./pen-persistence");
4
10
  const { getSessionStatePath, readSessionState } = require("./pencil-session");
5
11
  const { isPathInside, listFilesRecursiveSafe } = require("./fs-safety");
12
+ const { pathExists, readTextIfExists } = require("./utils");
13
+ const { runModuleExportInWorker } = require("./async-offload");
14
+ const { readExecutionSignals, summarizeSignalsBySurface } = require("./execution-signals");
6
15
  const {
7
16
  normalizeCheckpointLabel,
8
17
  parseCheckpointStatusMap,
@@ -30,17 +39,13 @@ const EXPORT_SCAN_LIMITS = Object.freeze({
30
39
  maxDepth: 4,
31
40
  maxEntries: 1200
32
41
  });
33
-
34
- function pathExists(targetPath) {
35
- return fs.existsSync(targetPath);
36
- }
37
-
38
- function readTextIfExists(targetPath) {
39
- if (!pathExists(targetPath)) {
40
- return "";
41
- }
42
- return fs.readFileSync(targetPath, "utf8");
43
- }
42
+ const PLANNING_SIGNAL_SURFACES = new Set(["lint-spec", "scope-check", "lint-tasks", "lint-bindings"]);
43
+ const VERIFICATION_SIGNAL_SURFACES = new Set([
44
+ "verify-bindings",
45
+ "verify-implementation",
46
+ "verify-structure",
47
+ "verify-coverage"
48
+ ]);
44
49
 
45
50
  function listFilesRecursive(rootDir, limits = {}) {
46
51
  return listFilesRecursiveSafe(rootDir, {
@@ -200,6 +205,16 @@ function normalizeReviewerToken(value) {
200
205
  return String(value || "").trim().toLowerCase();
201
206
  }
202
207
 
208
+ function buildBootstrapCommand(projectRoot, mode, changeId = null) {
209
+ const parts = ["da-vinci", "bootstrap-project", "--project", projectRoot];
210
+ if (changeId) {
211
+ parts.push("--change", changeId);
212
+ } else if (mode === "completion") {
213
+ parts.push("--change", "<change-id>");
214
+ }
215
+ return parts.join(" ");
216
+ }
217
+
203
218
  function auditProject(projectPathInput, options = {}) {
204
219
  const projectRoot = path.resolve(projectPathInput || process.cwd());
205
220
  const mode = options.mode || "integrity";
@@ -246,6 +261,20 @@ function auditProject(projectPathInput, options = {}) {
246
261
 
247
262
  if (!pathExists(daVinciDir)) {
248
263
  failures.push("Missing `.da-vinci/` directory.");
264
+ addMissingArtifacts(projectRoot, [path.join(projectRoot, "DA-VINCI.md"), designsDir], failures);
265
+ const missingWorkflowArtifacts = [
266
+ path.join(daVinciDir, "project-inventory.md"),
267
+ path.join(daVinciDir, "page-map.md"),
268
+ designRegistryPath
269
+ ];
270
+ if (mode === "completion") {
271
+ addMissingArtifacts(projectRoot, missingWorkflowArtifacts, failures);
272
+ } else {
273
+ addMissingArtifacts(projectRoot, missingWorkflowArtifacts, warnings);
274
+ }
275
+ notes.push(
276
+ `Hint: scaffold the required workflow files with \`${buildBootstrapCommand(projectRoot, mode, options.changeId || null)}\`.`
277
+ );
249
278
  return {
250
279
  projectRoot,
251
280
  mode,
@@ -274,6 +303,12 @@ function auditProject(projectPathInput, options = {}) {
274
303
  addMissingArtifacts(projectRoot, completionRequiredArtifacts.slice(1, 4), warnings);
275
304
  }
276
305
 
306
+ if (failures.some((message) => message.startsWith("Missing required artifact: "))) {
307
+ notes.push(
308
+ `Hint: scaffold missing workflow artifacts with \`${buildBootstrapCommand(projectRoot, mode, options.changeId || null)}\`.`
309
+ );
310
+ }
311
+
277
312
  if (designSupervisorRequired && !designSupervisorConfigured) {
278
313
  const message =
279
314
  "DA-VINCI.md sets `Require Supervisor Review: true` but no `Design-supervisor reviewers` are configured.";
@@ -365,7 +400,15 @@ function auditProject(projectPathInput, options = {}) {
365
400
  continue;
366
401
  }
367
402
 
368
- const persistedDocument = readPenDocument(registeredPenPath);
403
+ let persistedDocument;
404
+ try {
405
+ persistedDocument = assertPenDocumentShape(readPenDocument(registeredPenPath), registeredPenPath);
406
+ } catch (error) {
407
+ failures.push(
408
+ `Registered design source is not a valid .pen document: ${relativeTo(projectRoot, registeredPenPath)}`
409
+ );
410
+ continue;
411
+ }
369
412
  const currentHash = hashPenDocument(persistedDocument);
370
413
  if (state.snapshotHash !== currentHash) {
371
414
  failures.push(
@@ -396,16 +439,48 @@ function auditProject(projectPathInput, options = {}) {
396
439
  failures.push("Completion audit requires the Pencil session to be ended and recorded as `closed`.");
397
440
  }
398
441
 
442
+ if (sessionState.forceWithoutSync === true) {
443
+ const message =
444
+ "Pencil session was force-closed without a final live MCP sync verification. " +
445
+ "Run a fresh session persist/end cycle before completion.";
446
+ if (mode === "completion") {
447
+ failures.push(message);
448
+ } else {
449
+ warnings.push(message);
450
+ }
451
+ }
452
+
399
453
  if (sessionState.penPath && pathExists(sessionState.penPath)) {
400
- const penState = readPenState(sessionState.penPath);
401
- const persistedHash = penState && penState.snapshotHash ? String(penState.snapshotHash) : "";
402
-
403
- if (sessionState.lastPersistedHash && persistedHash && sessionState.lastPersistedHash !== persistedHash) {
404
- failures.push("Pencil session state hash does not match the current persisted `.pen` state hash.");
405
- } else if (persistedHash) {
406
- notes.push(
407
- `Detected Pencil session state for ${relativeTo(projectRoot, sessionState.penPath)}.`
454
+ let sessionPenValid = true;
455
+ let currentPenDocument = null;
456
+ try {
457
+ currentPenDocument = assertPenDocumentShape(
458
+ readPenDocument(sessionState.penPath),
459
+ sessionState.penPath
460
+ );
461
+ } catch (error) {
462
+ failures.push(
463
+ `Session-bound .pen is not a valid document: ${relativeTo(projectRoot, sessionState.penPath)}`
408
464
  );
465
+ sessionPenValid = false;
466
+ }
467
+ if (sessionPenValid) {
468
+ const currentHash = hashPenDocument(currentPenDocument);
469
+ const penState = readPenState(sessionState.penPath);
470
+ const persistedHash = penState && penState.snapshotHash ? String(penState.snapshotHash) : "";
471
+
472
+ if (persistedHash && persistedHash !== currentHash) {
473
+ failures.push("Pencil session state metadata hash does not match the current `.pen` document on disk.");
474
+ } else if (
475
+ sessionState.lastPersistedHash &&
476
+ sessionState.lastPersistedHash !== currentHash
477
+ ) {
478
+ failures.push("Pencil session state hash does not match the current persisted `.pen` state hash.");
479
+ } else if (persistedHash) {
480
+ notes.push(
481
+ `Detected Pencil session state for ${relativeTo(projectRoot, sessionState.penPath)}.`
482
+ );
483
+ }
409
484
  }
410
485
  }
411
486
  }
@@ -486,7 +561,7 @@ function auditProject(projectPathInput, options = {}) {
486
561
  if (!review.found) {
487
562
  const message =
488
563
  `DA-VINCI.md configures Design-supervisor reviewers, but ${relativeTo(projectRoot, pencilDesignPath)} ` +
489
- "does not record a `## Design-Supervisor Review` section.";
564
+ "does not record a `Design-Supervisor Review` section.";
490
565
  if (enforceAsFailure) {
491
566
  failures.push(message);
492
567
  } else {
@@ -643,7 +718,7 @@ function auditProject(projectPathInput, options = {}) {
643
718
  if (!hasAnyConcreteContextDelta) {
644
719
  pushUnique(
645
720
  warnings,
646
- `Checkpoint-bearing artifacts in ${changeRel} do not record any concrete \`## Context Delta\` entries.`
721
+ `Checkpoint-bearing artifacts in ${changeRel} do not record any concrete \`Context Delta\` entries.`
647
722
  );
648
723
  } else {
649
724
  notes.push(`Detected context-delta recovery notes in ${changeRel}.`);
@@ -758,6 +833,48 @@ function auditProject(projectPathInput, options = {}) {
758
833
  }
759
834
  }
760
835
 
836
+ const signalSummary =
837
+ options && options.preloadedSignalSummary && typeof options.preloadedSignalSummary === "object"
838
+ ? options.preloadedSignalSummary
839
+ : summarizeSignalsBySurface(
840
+ readExecutionSignals(projectRoot, {
841
+ changeId: options.changeId || ""
842
+ })
843
+ );
844
+ for (const [surface, signal] of Object.entries(signalSummary)) {
845
+ if (surface === "signal-file-parse") {
846
+ const warningMessage =
847
+ Array.isArray(signal.warnings) && signal.warnings[0]
848
+ ? String(signal.warnings[0])
849
+ : "Malformed execution signal files were detected.";
850
+ pushUnique(warnings, warningMessage);
851
+ continue;
852
+ }
853
+
854
+ if (mode === "integrity" && PLANNING_SIGNAL_SURFACES.has(surface)) {
855
+ if (signal.status === "BLOCK") {
856
+ pushUnique(warnings, `Planning signal ${surface} is BLOCK (advisory in integrity mode).`);
857
+ } else if (signal.status === "WARN") {
858
+ pushUnique(notes, `Planning signal ${surface} is WARN.`);
859
+ }
860
+ }
861
+
862
+ if (mode === "completion" && VERIFICATION_SIGNAL_SURFACES.has(surface)) {
863
+ if (signal.status === "BLOCK") {
864
+ failures.push(`Verification signal ${surface} is BLOCK.`);
865
+ } else if (signal.status === "WARN") {
866
+ pushUnique(warnings, `Verification signal ${surface} is WARN.`);
867
+ }
868
+ }
869
+
870
+ if (surface === "diff-spec" && signal.status !== "PASS") {
871
+ pushUnique(warnings, `Planning diff signal ${surface} reports ${signal.status}.`);
872
+ }
873
+ }
874
+ notes.push(
875
+ "Execution-chain signal integration uses advisory planning semantics in integrity mode and blocking verification semantics in completion mode."
876
+ );
877
+
761
878
  const status = failures.length > 0 ? "FAIL" : warnings.length > 0 ? "WARN" : "PASS";
762
879
 
763
880
  return {
@@ -771,6 +888,10 @@ function auditProject(projectPathInput, options = {}) {
771
888
  };
772
889
  }
773
890
 
891
+ function auditProjectAsync(projectPathInput, options = {}) {
892
+ return runModuleExportInWorker(__filename, "auditProject", [projectPathInput, options]);
893
+ }
894
+
774
895
  function formatAuditReport(result) {
775
896
  const lines = [
776
897
  "Da Vinci audit",
@@ -809,5 +930,6 @@ function formatAuditReport(result) {
809
930
 
810
931
  module.exports = {
811
932
  auditProject,
933
+ auditProjectAsync,
812
934
  formatAuditReport
813
935
  };