@xenonbyte/da-vinci-workflow 0.1.23 → 0.1.25

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.
@@ -559,6 +559,8 @@ Use this structure:
559
559
 
560
560
  ## Design-Supervisor Review
561
561
  - Configured reviewers
562
+ - Executed reviewers
563
+ - Review source (`skill` / `manual` / `inferred`)
562
564
  - Review mode
563
565
  - Review inputs
564
566
  - Whether reviewers were distinct from `Preferred adapters`
@@ -567,6 +569,7 @@ Use this structure:
567
569
  - Revision outcome
568
570
  - Whether broad expansion is approved
569
571
  - Whether implementation-task handoff is approved
572
+ - Keep one canonical structured supervisor-review section; avoid ad-hoc headings such as `## Design-Supervisor Review (Round X Attempt)`
570
573
 
571
574
  ## Anchor Surfaces
572
575
  - Which 1-3 anchor screens were designed first
@@ -5,6 +5,7 @@ const path = require("path");
5
5
  const { spawnSync } = require("child_process");
6
6
 
7
7
  const { auditProject } = require("../lib/audit");
8
+ const { inspectDesignSupervisorReview } = require("../lib/audit-parsers");
8
9
 
9
10
  const repo = path.resolve(__dirname, "..");
10
11
  const cli = path.join(repo, "bin", "da-vinci.js");
@@ -318,6 +319,8 @@ runTest("completion audit passes when required supervisor review is recorded as
318
319
  "",
319
320
  "## Design-Supervisor Review",
320
321
  "- Configured reviewers: frontend-skill",
322
+ "- Executed reviewers: frontend-skill",
323
+ "- Review source: skill",
321
324
  "- Review mode: screenshot-and-theme",
322
325
  "- Review inputs: screenshots, pencil variables, visual thesis, content plan, interaction thesis",
323
326
  "- Status: PASS",
@@ -345,4 +348,344 @@ runTest("completion audit passes when required supervisor review is recorded as
345
348
  );
346
349
  });
347
350
 
351
+ runTest("completion audit accepts the latest structured round section when multiple Design-Supervisor Review headings exist", () => {
352
+ const harness = createHarness();
353
+ const project = setupProject(harness, "latest-supervisor-round-wins", {
354
+ requireSupervisorReview: true
355
+ });
356
+
357
+ writeText(
358
+ project.pencilDesignPath,
359
+ [
360
+ "# Pencil Design",
361
+ "",
362
+ "## Design-Supervisor Review",
363
+ "- Configured reviewers: frontend-skill",
364
+ "- Executed reviewers: frontend-skill",
365
+ "- Review source: skill",
366
+ "- Status: BLOCK",
367
+ "- Issue list: command not found",
368
+ "- Revision outcome: blocked",
369
+ "",
370
+ "## Design-Supervisor Review (Round 2 Attempt)",
371
+ "- Configured reviewers: frontend-skill",
372
+ "- Executed reviewers: frontend-skill",
373
+ "- Review source: skill",
374
+ "- Status: PASS",
375
+ "- Issue list: none",
376
+ "- Revision outcome: accepted",
377
+ ""
378
+ ].join("\n")
379
+ );
380
+
381
+ const result = auditProject(project.root, {
382
+ mode: "completion",
383
+ changeId
384
+ });
385
+
386
+ assert.equal(
387
+ result.status,
388
+ "PASS",
389
+ `expected completion audit to use latest review section:\n${JSON.stringify(result, null, 2)}`
390
+ );
391
+ assert.match(
392
+ result.notes.join("\n"),
393
+ /Detected design-supervisor review status PASS/i
394
+ );
395
+ assert.doesNotMatch(
396
+ result.warnings.join("\n"),
397
+ /legacy\/non-structured format/i
398
+ );
399
+ });
400
+
401
+ runTest("completion audit falls back to the latest structured section when newest review entry is legacy-style", () => {
402
+ const harness = createHarness();
403
+ const project = setupProject(harness, "legacy-supervisor-entry-fallback", {
404
+ requireSupervisorReview: true
405
+ });
406
+
407
+ writeText(
408
+ project.pencilDesignPath,
409
+ [
410
+ "# Pencil Design",
411
+ "",
412
+ "## Design-Supervisor Review",
413
+ "- Configured reviewers: frontend-skill",
414
+ "- Executed reviewers: frontend-skill",
415
+ "- Review source: skill",
416
+ "- Status: PASS",
417
+ "- Issue list: none",
418
+ "- Revision outcome: approved",
419
+ "",
420
+ "## Design-Supervisor Review (Round 2 Attempt)",
421
+ "- Result: BLOCK",
422
+ "- Issue list: style quality not approved",
423
+ "- Revision outcome: pending",
424
+ ""
425
+ ].join("\n")
426
+ );
427
+
428
+ const result = auditProject(project.root, {
429
+ mode: "completion",
430
+ changeId
431
+ });
432
+
433
+ assert.equal(
434
+ result.status,
435
+ "WARN",
436
+ `expected completion audit to warn but not fail when latest entry is legacy:\n${JSON.stringify(result, null, 2)}`
437
+ );
438
+ assert.match(
439
+ result.notes.join("\n"),
440
+ /Detected design-supervisor review status PASS/i
441
+ );
442
+ assert.match(
443
+ result.warnings.join("\n"),
444
+ /legacy\/non-structured format; gate evaluation used ## Design-Supervisor Review/i
445
+ );
446
+ });
447
+
448
+ runTest("completion audit fails when only legacy-style Design-Supervisor Review sections exist", () => {
449
+ const harness = createHarness();
450
+ const project = setupProject(harness, "legacy-supervisor-only", {
451
+ requireSupervisorReview: true
452
+ });
453
+
454
+ writeText(
455
+ project.pencilDesignPath,
456
+ [
457
+ "# Pencil Design",
458
+ "",
459
+ "## Design-Supervisor Review (Round 3 Attempt)",
460
+ "- Result: PASS",
461
+ "- Issue list: none",
462
+ "- Revision outcome: accepted",
463
+ ""
464
+ ].join("\n")
465
+ );
466
+
467
+ const result = auditProject(project.root, {
468
+ mode: "completion",
469
+ changeId
470
+ });
471
+
472
+ assert.equal(result.status, "FAIL");
473
+ assert.match(
474
+ result.failures.join("\n"),
475
+ /missing required field\(s\): Configured reviewers/i
476
+ );
477
+ });
478
+
479
+ runTest("completion audit treats Chinese round-attempt heading as legacy and falls back to latest structured section", () => {
480
+ const harness = createHarness();
481
+ const project = setupProject(harness, "legacy-supervisor-chinese-heading", {
482
+ requireSupervisorReview: true
483
+ });
484
+
485
+ writeText(
486
+ project.pencilDesignPath,
487
+ [
488
+ "# Pencil Design",
489
+ "",
490
+ "## Design-Supervisor Review",
491
+ "- Configured reviewers: frontend-skill",
492
+ "- Executed reviewers: frontend-skill",
493
+ "- Review source: skill",
494
+ "- Status: PASS",
495
+ "- Issue list: none",
496
+ "- Revision outcome: approved",
497
+ "",
498
+ "## Design-Supervisor Review(第2轮尝试)",
499
+ "- Result: BLOCK",
500
+ "- Issue list: 视觉层级仍需调整",
501
+ "- Revision outcome: 待修订",
502
+ ""
503
+ ].join("\n")
504
+ );
505
+
506
+ const result = auditProject(project.root, {
507
+ mode: "completion",
508
+ changeId
509
+ });
510
+
511
+ assert.equal(result.status, "WARN");
512
+ assert.match(
513
+ result.warnings.join("\n"),
514
+ /legacy\/non-structured format; gate evaluation used ## Design-Supervisor Review/i
515
+ );
516
+ assert.match(
517
+ result.notes.join("\n"),
518
+ /Detected design-supervisor review status PASS/i
519
+ );
520
+ });
521
+
522
+ runTest("inspectDesignSupervisorReview marks Chinese round-attempt suffix as legacy", () => {
523
+ const review = inspectDesignSupervisorReview(
524
+ [
525
+ "# Pencil Design",
526
+ "",
527
+ "## Design-Supervisor Review",
528
+ "- Configured reviewers: frontend-skill",
529
+ "- Executed reviewers: frontend-skill",
530
+ "- Review source: skill",
531
+ "- Status: PASS",
532
+ "- Issue list: none",
533
+ "- Revision outcome: approved",
534
+ "",
535
+ "## Design-Supervisor Review(第3轮尝试)",
536
+ "- Result: BLOCK",
537
+ "- Issue list: 继续迭代",
538
+ "- Revision outcome: pending",
539
+ ""
540
+ ].join("\n")
541
+ );
542
+
543
+ assert.equal(review.found, true);
544
+ assert.equal(review.latestSectionLooksLegacyAttempt, true);
545
+ assert.equal(review.usedStructuredSectionFallback, true);
546
+ assert.equal(review.selectedFromLatest, false);
547
+ assert.equal(review.status, "PASS");
548
+ });
549
+
550
+ runTest("completion audit accepts multiline Issue list and Revision outcome fields", () => {
551
+ const harness = createHarness();
552
+ const project = setupProject(harness, "multiline-supervisor-fields", {
553
+ requireSupervisorReview: true
554
+ });
555
+
556
+ writeText(
557
+ project.pencilDesignPath,
558
+ [
559
+ "# Pencil Design",
560
+ "",
561
+ "## Design-Supervisor Review",
562
+ "- Configured reviewers: frontend-skill",
563
+ "- Executed reviewers: frontend-skill",
564
+ "- Review source: skill",
565
+ "- Status: WARN",
566
+ "- Issue list:",
567
+ " - icon alignment drift on top bar",
568
+ " - hierarchy contrast needs one more pass",
569
+ "- Revision outcome:",
570
+ " - accepted with follow-up in next visual pass",
571
+ ""
572
+ ].join("\n")
573
+ );
574
+
575
+ const result = auditProject(project.root, {
576
+ mode: "completion",
577
+ changeId
578
+ });
579
+
580
+ assert.equal(
581
+ result.status,
582
+ "PASS",
583
+ `expected accepted WARN with multiline fields to pass:\n${JSON.stringify(result, null, 2)}`
584
+ );
585
+ assert.match(
586
+ result.notes.join("\n"),
587
+ /Detected design-supervisor review status WARN/i
588
+ );
589
+ });
590
+
591
+ runTest("completion audit fails when required supervisor review is not skill-backed", () => {
592
+ const harness = createHarness();
593
+ const project = setupProject(harness, "required-supervisor-skill-source", {
594
+ requireSupervisorReview: true
595
+ });
596
+
597
+ writeText(
598
+ project.pencilDesignPath,
599
+ [
600
+ "# Pencil Design",
601
+ "",
602
+ "## Design-Supervisor Review",
603
+ "- Configured reviewers: frontend-skill",
604
+ "- Review source: inferred",
605
+ "- Status: PASS",
606
+ "- Issue list: none",
607
+ "- Revision outcome: approved",
608
+ ""
609
+ ].join("\n")
610
+ );
611
+
612
+ const result = auditProject(project.root, {
613
+ mode: "completion",
614
+ changeId
615
+ });
616
+
617
+ assert.equal(result.status, "FAIL");
618
+ assert.match(
619
+ result.failures.join("\n"),
620
+ /must be skill-backed.*Require Supervisor Review: true/i
621
+ );
622
+ });
623
+
624
+ runTest("completion audit fails when required supervisor review omits executed reviewers", () => {
625
+ const harness = createHarness();
626
+ const project = setupProject(harness, "required-supervisor-executed-missing", {
627
+ requireSupervisorReview: true
628
+ });
629
+
630
+ writeText(
631
+ project.pencilDesignPath,
632
+ [
633
+ "# Pencil Design",
634
+ "",
635
+ "## Design-Supervisor Review",
636
+ "- Configured reviewers: frontend-skill",
637
+ "- Review source: skill",
638
+ "- Status: PASS",
639
+ "- Issue list: none",
640
+ "- Revision outcome: approved",
641
+ ""
642
+ ].join("\n")
643
+ );
644
+
645
+ const result = auditProject(project.root, {
646
+ mode: "completion",
647
+ changeId
648
+ });
649
+
650
+ assert.equal(result.status, "FAIL");
651
+ assert.match(
652
+ result.failures.join("\n"),
653
+ /missing required field\(s\): Executed reviewers/i
654
+ );
655
+ });
656
+
657
+ runTest("completion audit fails when required supervisor review did not execute all configured reviewers", () => {
658
+ const harness = createHarness();
659
+ const project = setupProject(harness, "required-supervisor-executed-incomplete", {
660
+ requireSupervisorReview: true
661
+ });
662
+
663
+ writeText(
664
+ project.pencilDesignPath,
665
+ [
666
+ "# Pencil Design",
667
+ "",
668
+ "## Design-Supervisor Review",
669
+ "- Configured reviewers: frontend-skill",
670
+ "- Executed reviewers: ui-ux-pro-max",
671
+ "- Review source: skill",
672
+ "- Status: PASS",
673
+ "- Issue list: none",
674
+ "- Revision outcome: approved",
675
+ ""
676
+ ].join("\n")
677
+ );
678
+
679
+ const result = auditProject(project.root, {
680
+ mode: "completion",
681
+ changeId
682
+ });
683
+
684
+ assert.equal(result.status, "FAIL");
685
+ assert.match(
686
+ result.failures.join("\n"),
687
+ /did not execute configured reviewer skill\(s\): frontend-skill/i
688
+ );
689
+ });
690
+
348
691
  console.log("All design-supervisor audit tests passed.");
@@ -283,6 +283,11 @@ runTest("design-supervisor review stays distinct from preferred adapters and is
283
283
  const content = read(file);
284
284
  assert.match(content, /design-supervisor review/, `${file} should instruct design flows to run design-supervisor review when configured`);
285
285
  assert.match(content, /Require Supervisor Review: true/, `${file} should explain the hard-gate branch for supervisor review`);
286
+ assert.doesNotMatch(
287
+ content,
288
+ /^\s*##\s*Design-Supervisor Review\s*\(Round [^)]+Attempt\)\s*$/im,
289
+ `${file} should not provide ad-hoc supervisor-review heading templates`
290
+ );
286
291
  }
287
292
  });
288
293
 
@@ -33,6 +33,10 @@ function createTempDir() {
33
33
  return fs.mkdtempSync(path.join(os.tmpdir(), "da-vinci-pen-persistence-"));
34
34
  }
35
35
 
36
+ function escapeRegExp(value) {
37
+ return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
38
+ }
39
+
36
40
  function writePayloadFiles(tempDir, fixture) {
37
41
  const nodesFile = path.join(tempDir, "nodes.json");
38
42
  const variablesFile = path.join(tempDir, "variables.json");
@@ -183,6 +187,151 @@ runTest("comparePenSync fails when live payload is newer than disk", () => {
183
187
  assert.notEqual(result.liveHash, result.persistedHash);
184
188
  });
185
189
 
190
+ runTest("writePenFromPayloadFiles rejects pen-state metadata passed as nodes payload", () => {
191
+ const tempDir = createTempDir();
192
+ const outputPath = path.join(tempDir, "written.pen");
193
+ const nodesFile = path.join(tempDir, "state-like.json");
194
+
195
+ fs.writeFileSync(
196
+ nodesFile,
197
+ JSON.stringify(
198
+ {
199
+ schema: 1,
200
+ penPath: "/tmp/demo.pen",
201
+ snapshotHash: "abc123",
202
+ topLevelIds: [],
203
+ topLevelCount: 0
204
+ },
205
+ null,
206
+ 2
207
+ )
208
+ );
209
+
210
+ assert.throws(
211
+ () =>
212
+ writePenFromPayloadFiles({
213
+ outputPath,
214
+ nodesFile
215
+ }),
216
+ /appears to be pen state metadata/i
217
+ );
218
+ });
219
+
220
+ runTest("comparePenSync rejects pen-state metadata passed as nodes payload", () => {
221
+ const fixture = JSON.parse(fs.readFileSync(fixturePath, "utf8"));
222
+ const tempDir = createTempDir();
223
+ const { nodesFile, variablesFile } = writePayloadFiles(tempDir, fixture);
224
+ const outputPath = path.join(tempDir, "written.pen");
225
+ const invalidNodesFile = path.join(tempDir, "state-like.json");
226
+
227
+ writePenFromPayloadFiles({
228
+ outputPath,
229
+ nodesFile,
230
+ variablesFile,
231
+ version: fixture.version
232
+ });
233
+
234
+ fs.writeFileSync(
235
+ invalidNodesFile,
236
+ JSON.stringify(
237
+ {
238
+ schema: 1,
239
+ penPath: outputPath,
240
+ snapshotHash: "abc123",
241
+ topLevelIds: fixture.children.map((node) => node.id),
242
+ topLevelCount: fixture.children.length
243
+ },
244
+ null,
245
+ 2
246
+ )
247
+ );
248
+
249
+ assert.throws(
250
+ () =>
251
+ comparePenSync({
252
+ penPath: outputPath,
253
+ nodesFile: invalidNodesFile,
254
+ version: fixture.version
255
+ }),
256
+ /appears to be pen state metadata/i
257
+ );
258
+ });
259
+
260
+ runTest("writePenFromPayloadFiles accepts nodes payload even when metadata keys are present", () => {
261
+ const fixture = JSON.parse(fs.readFileSync(fixturePath, "utf8"));
262
+ const tempDir = createTempDir();
263
+ const nodesFile = path.join(tempDir, "nodes-with-metadata.json");
264
+ const variablesFile = path.join(tempDir, "variables.json");
265
+ const outputPath = path.join(tempDir, "written.pen");
266
+
267
+ fs.writeFileSync(
268
+ nodesFile,
269
+ JSON.stringify(
270
+ {
271
+ nodes: fixture.children,
272
+ penPath: "/tmp/demo.pen",
273
+ snapshotHash: "abc123",
274
+ topLevelIds: fixture.children.map((node) => node.id),
275
+ topLevelCount: fixture.children.length
276
+ },
277
+ null,
278
+ 2
279
+ )
280
+ );
281
+ fs.writeFileSync(variablesFile, JSON.stringify({ variables: fixture.variables }, null, 2));
282
+
283
+ writePenFromPayloadFiles({
284
+ outputPath,
285
+ nodesFile,
286
+ variablesFile,
287
+ version: fixture.version
288
+ });
289
+
290
+ assert.deepEqual(JSON.parse(fs.readFileSync(outputPath, "utf8")), fixture);
291
+ });
292
+
293
+ runTest("writePenFromPayloadFiles shows relative nodes path in metadata-payload error when file is under cwd", () => {
294
+ const originalCwd = process.cwd();
295
+ const localRoot = fs.mkdtempSync(path.join(originalCwd, ".tmp-da-vinci-pen-path-"));
296
+
297
+ try {
298
+ process.chdir(localRoot);
299
+ const outputPath = path.join(localRoot, "written.pen");
300
+ const nodesFile = path.join(localRoot, "state-like.json");
301
+
302
+ fs.writeFileSync(
303
+ nodesFile,
304
+ JSON.stringify(
305
+ {
306
+ schema: 1,
307
+ penPath: "/tmp/demo.pen",
308
+ snapshotHash: "abc123",
309
+ topLevelIds: [],
310
+ topLevelCount: 0
311
+ },
312
+ null,
313
+ 2
314
+ )
315
+ );
316
+
317
+ assert.throws(
318
+ () =>
319
+ writePenFromPayloadFiles({
320
+ outputPath,
321
+ nodesFile
322
+ }),
323
+ (error) => {
324
+ assert.match(error.message, /\(state-like\.json\)/i);
325
+ assert.doesNotMatch(error.message, new RegExp(escapeRegExp(localRoot)));
326
+ return true;
327
+ }
328
+ );
329
+ } finally {
330
+ process.chdir(originalCwd);
331
+ fs.rmSync(localRoot, { recursive: true, force: true });
332
+ }
333
+ });
334
+
186
335
  runTest("global Pencil lock serializes projects", () => {
187
336
  const tempDir = createTempDir();
188
337
  const homeDir = path.join(tempDir, "home");