@xenonbyte/da-vinci-workflow 0.1.24 → 0.1.26
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.
- package/CHANGELOG.md +35 -1
- package/README.md +41 -10
- package/README.zh-CN.md +30 -10
- package/SKILL.md +4 -0
- package/commands/claude/dv/design.md +2 -1
- package/commands/codex/prompts/dv-design.md +2 -1
- package/commands/gemini/dv/design.toml +2 -1
- package/docs/constraint-files.md +1 -0
- package/docs/dv-command-reference.md +14 -2
- package/docs/pencil-rendering-workflow.md +9 -7
- package/docs/prompt-presets/README.md +4 -0
- package/docs/visual-assist-presets/README.md +4 -0
- package/docs/workflow-examples.md +13 -11
- package/docs/workflow-overview.md +2 -0
- package/docs/zh-CN/constraint-files.md +1 -0
- package/docs/zh-CN/dv-command-reference.md +14 -2
- package/docs/zh-CN/pencil-rendering-workflow.md +9 -7
- package/docs/zh-CN/prompt-presets/README.md +5 -1
- package/docs/zh-CN/visual-assist-presets/README.md +5 -1
- package/docs/zh-CN/workflow-examples.md +13 -11
- package/docs/zh-CN/workflow-overview.md +2 -0
- package/examples/greenfield-spec-markupflow/README.md +6 -1
- package/lib/async-offload-worker.js +26 -0
- package/lib/async-offload.js +82 -0
- package/lib/audit-parsers.js +223 -51
- package/lib/audit.js +91 -23
- package/lib/cli.js +749 -433
- package/lib/fs-safety.js +1 -4
- package/lib/icon-aliases.js +7 -7
- package/lib/icon-search.js +21 -14
- package/lib/icon-sync.js +220 -41
- package/lib/install.js +128 -60
- package/lib/mcp-runtime-gate.js +4 -7
- package/lib/pen-persistence.js +365 -46
- package/lib/pencil-lock.js +237 -25
- package/lib/pencil-preflight.js +233 -12
- package/lib/pencil-session.js +216 -36
- package/lib/supervisor-review.js +56 -34
- package/lib/utils.js +121 -0
- package/lib/workflow-bootstrap.js +255 -0
- package/package.json +13 -3
- package/references/artifact-templates.md +1 -0
- package/references/checkpoints.md +2 -0
- package/references/design-inputs.md +2 -0
- package/references/pencil-design-to-code.md +2 -0
- package/scripts/fixtures/complex-sample.pen +0 -295
- package/scripts/fixtures/mock-pencil.js +0 -49
- package/scripts/test-audit-context-delta.js +0 -446
- package/scripts/test-audit-design-supervisor.js +0 -537
- package/scripts/test-audit-safety.js +0 -92
- package/scripts/test-icon-aliases.js +0 -96
- package/scripts/test-icon-search.js +0 -77
- package/scripts/test-icon-sync.js +0 -178
- package/scripts/test-mcp-runtime-gate.js +0 -287
- package/scripts/test-mode-consistency.js +0 -339
- package/scripts/test-pen-persistence.js +0 -254
- package/scripts/test-pencil-lock.js +0 -130
- package/scripts/test-pencil-preflight.js +0 -169
- package/scripts/test-pencil-session.js +0 -192
- package/scripts/test-persistence-flows.js +0 -345
- package/scripts/test-supervisor-review-cli.js +0 -619
- package/scripts/test-supervisor-review-integration.js +0 -115
|
@@ -1,537 +0,0 @@
|
|
|
1
|
-
const assert = require("assert/strict");
|
|
2
|
-
const fs = require("fs");
|
|
3
|
-
const os = require("os");
|
|
4
|
-
const path = require("path");
|
|
5
|
-
const { spawnSync } = require("child_process");
|
|
6
|
-
|
|
7
|
-
const { auditProject } = require("../lib/audit");
|
|
8
|
-
|
|
9
|
-
const repo = path.resolve(__dirname, "..");
|
|
10
|
-
const cli = path.join(repo, "bin", "da-vinci.js");
|
|
11
|
-
const fixture = JSON.parse(
|
|
12
|
-
fs.readFileSync(path.join(__dirname, "fixtures", "complex-sample.pen"), "utf8")
|
|
13
|
-
);
|
|
14
|
-
const changeId = "redesign-001";
|
|
15
|
-
|
|
16
|
-
function runTest(name, fn) {
|
|
17
|
-
try {
|
|
18
|
-
fn();
|
|
19
|
-
console.log(`PASS ${name}`);
|
|
20
|
-
} catch (error) {
|
|
21
|
-
console.error(`FAIL ${name}`);
|
|
22
|
-
throw error;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function createHarness() {
|
|
27
|
-
const base = fs.mkdtempSync(path.join(os.tmpdir(), "da-vinci-audit-supervisor-"));
|
|
28
|
-
return {
|
|
29
|
-
base,
|
|
30
|
-
home: path.join(base, "home")
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function runCli(harness, args) {
|
|
35
|
-
const [command, ...rest] = args;
|
|
36
|
-
const result = spawnSync(process.execPath, [cli, command, "--home", harness.home, ...rest], {
|
|
37
|
-
cwd: repo,
|
|
38
|
-
encoding: "utf8",
|
|
39
|
-
maxBuffer: 8 * 1024 * 1024
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
return {
|
|
43
|
-
code: result.status,
|
|
44
|
-
stdout: (result.stdout || "").trim(),
|
|
45
|
-
stderr: (result.stderr || "").trim()
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function expectOk(step, result) {
|
|
50
|
-
assert.equal(result.code, 0, `${step} failed:\n${result.stderr || result.stdout}`);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function writeJson(filePath, payload) {
|
|
54
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
55
|
-
fs.writeFileSync(filePath, JSON.stringify(payload, null, 2));
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function writeText(filePath, text) {
|
|
59
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
60
|
-
fs.writeFileSync(filePath, text);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function setupProject(harness, name, options = {}) {
|
|
64
|
-
const root = path.join(harness.base, name);
|
|
65
|
-
const daVinciDir = path.join(root, ".da-vinci");
|
|
66
|
-
const changeDir = path.join(daVinciDir, "changes", changeId);
|
|
67
|
-
const penPath = path.join(daVinciDir, "designs", `${name}.pen`);
|
|
68
|
-
const nodesFile = path.join(root, `${name}-nodes.json`);
|
|
69
|
-
const variablesFile = path.join(root, `${name}-variables.json`);
|
|
70
|
-
const configureReviewers = options.configureReviewers !== false;
|
|
71
|
-
const requireSupervisorReview = options.requireSupervisorReview === true;
|
|
72
|
-
|
|
73
|
-
const visualAssistLines = [
|
|
74
|
-
"# DA-VINCI",
|
|
75
|
-
"",
|
|
76
|
-
"## Visual Assist",
|
|
77
|
-
"- Preferred adapters:",
|
|
78
|
-
" - frontend-skill"
|
|
79
|
-
];
|
|
80
|
-
|
|
81
|
-
if (configureReviewers) {
|
|
82
|
-
visualAssistLines.push("- Design-supervisor reviewers:");
|
|
83
|
-
visualAssistLines.push(" - frontend-skill");
|
|
84
|
-
visualAssistLines.push("- Design-supervisor review mode:");
|
|
85
|
-
visualAssistLines.push(" - screenshot-and-theme");
|
|
86
|
-
visualAssistLines.push("- Design-supervisor review inputs:");
|
|
87
|
-
visualAssistLines.push(" - screenshots");
|
|
88
|
-
visualAssistLines.push(" - pencil variables");
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
visualAssistLines.push("- Require Supervisor Review:");
|
|
92
|
-
visualAssistLines.push(` - ${requireSupervisorReview ? "true" : "false"}`);
|
|
93
|
-
visualAssistLines.push("");
|
|
94
|
-
|
|
95
|
-
writeText(
|
|
96
|
-
path.join(root, "DA-VINCI.md"),
|
|
97
|
-
visualAssistLines.join("\n")
|
|
98
|
-
);
|
|
99
|
-
writeText(path.join(daVinciDir, "project-inventory.md"), "# Inventory\n");
|
|
100
|
-
writeText(path.join(daVinciDir, "page-map.md"), "# Page Map\n");
|
|
101
|
-
writeText(
|
|
102
|
-
path.join(daVinciDir, "design-registry.md"),
|
|
103
|
-
`# Registry\n- Preferred .pen: .da-vinci/designs/${name}.pen\n`
|
|
104
|
-
);
|
|
105
|
-
writeText(path.join(changeDir, "design-brief.md"), "# Brief\n");
|
|
106
|
-
writeText(path.join(changeDir, "design.md"), "# Design\n");
|
|
107
|
-
writeText(path.join(changeDir, "pencil-bindings.md"), "# Bindings\n");
|
|
108
|
-
|
|
109
|
-
writeJson(nodesFile, { nodes: fixture.children });
|
|
110
|
-
writeJson(variablesFile, { variables: fixture.variables });
|
|
111
|
-
|
|
112
|
-
expectOk(
|
|
113
|
-
`${name} begin`,
|
|
114
|
-
runCli(harness, ["pencil-session", "begin", "--project", root, "--pen", penPath])
|
|
115
|
-
);
|
|
116
|
-
expectOk(
|
|
117
|
-
`${name} persist`,
|
|
118
|
-
runCli(harness, [
|
|
119
|
-
"pencil-session",
|
|
120
|
-
"persist",
|
|
121
|
-
"--project",
|
|
122
|
-
root,
|
|
123
|
-
"--pen",
|
|
124
|
-
penPath,
|
|
125
|
-
"--nodes-file",
|
|
126
|
-
nodesFile,
|
|
127
|
-
"--variables-file",
|
|
128
|
-
variablesFile,
|
|
129
|
-
"--version",
|
|
130
|
-
fixture.version
|
|
131
|
-
])
|
|
132
|
-
);
|
|
133
|
-
expectOk(
|
|
134
|
-
`${name} end`,
|
|
135
|
-
runCli(harness, [
|
|
136
|
-
"pencil-session",
|
|
137
|
-
"end",
|
|
138
|
-
"--project",
|
|
139
|
-
root,
|
|
140
|
-
"--pen",
|
|
141
|
-
penPath,
|
|
142
|
-
"--nodes-file",
|
|
143
|
-
nodesFile,
|
|
144
|
-
"--variables-file",
|
|
145
|
-
variablesFile,
|
|
146
|
-
"--version",
|
|
147
|
-
fixture.version
|
|
148
|
-
])
|
|
149
|
-
);
|
|
150
|
-
|
|
151
|
-
return {
|
|
152
|
-
root,
|
|
153
|
-
changeDir,
|
|
154
|
-
pencilDesignPath: path.join(changeDir, "pencil-design.md")
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
runTest("completion audit warns when supervisor reviewers are configured but not required and no review is recorded", () => {
|
|
159
|
-
const harness = createHarness();
|
|
160
|
-
const project = setupProject(harness, "missing-supervisor-review");
|
|
161
|
-
|
|
162
|
-
writeText(project.pencilDesignPath, "# Pencil Design\n");
|
|
163
|
-
|
|
164
|
-
const result = auditProject(project.root, {
|
|
165
|
-
mode: "completion",
|
|
166
|
-
changeId
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
assert.equal(result.status, "WARN");
|
|
170
|
-
assert.match(
|
|
171
|
-
result.warnings.join("\n"),
|
|
172
|
-
/Design-supervisor reviewers.*does not record a `## Design-Supervisor Review` section/i
|
|
173
|
-
);
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
runTest("completion audit fails when supervisor review is required but no review is recorded", () => {
|
|
177
|
-
const harness = createHarness();
|
|
178
|
-
const project = setupProject(harness, "required-supervisor-review", {
|
|
179
|
-
requireSupervisorReview: true
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
writeText(project.pencilDesignPath, "# Pencil Design\n");
|
|
183
|
-
|
|
184
|
-
const result = auditProject(project.root, {
|
|
185
|
-
mode: "completion",
|
|
186
|
-
changeId
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
assert.equal(result.status, "FAIL");
|
|
190
|
-
assert.match(
|
|
191
|
-
result.failures.join("\n"),
|
|
192
|
-
/Design-supervisor reviewers.*does not record a `## Design-Supervisor Review` section/i
|
|
193
|
-
);
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
runTest("completion audit fails when supervisor review is required but no reviewers are configured", () => {
|
|
197
|
-
const harness = createHarness();
|
|
198
|
-
const project = setupProject(harness, "missing-supervisor-reviewers", {
|
|
199
|
-
configureReviewers: false,
|
|
200
|
-
requireSupervisorReview: true
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
writeText(project.pencilDesignPath, "# Pencil Design\n");
|
|
204
|
-
|
|
205
|
-
const result = auditProject(project.root, {
|
|
206
|
-
mode: "completion",
|
|
207
|
-
changeId
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
assert.equal(result.status, "FAIL");
|
|
211
|
-
assert.match(
|
|
212
|
-
result.failures.join("\n"),
|
|
213
|
-
/Require Supervisor Review: true.*no `Design-supervisor reviewers` are configured/i
|
|
214
|
-
);
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
runTest("completion audit does not treat an empty reviewers field as configured", () => {
|
|
218
|
-
const harness = createHarness();
|
|
219
|
-
const project = setupProject(harness, "empty-supervisor-reviewers", {
|
|
220
|
-
configureReviewers: false,
|
|
221
|
-
requireSupervisorReview: false
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
writeText(
|
|
225
|
-
path.join(project.root, "DA-VINCI.md"),
|
|
226
|
-
[
|
|
227
|
-
"# DA-VINCI",
|
|
228
|
-
"",
|
|
229
|
-
"## Visual Assist",
|
|
230
|
-
"- Preferred adapters:",
|
|
231
|
-
" - frontend-skill",
|
|
232
|
-
"- Design-supervisor reviewers:",
|
|
233
|
-
"- Design-supervisor review mode:",
|
|
234
|
-
" - screenshot-and-theme",
|
|
235
|
-
"- Design-supervisor review inputs:",
|
|
236
|
-
" - screenshots",
|
|
237
|
-
" - pencil variables",
|
|
238
|
-
"- Require Supervisor Review:",
|
|
239
|
-
" - false",
|
|
240
|
-
""
|
|
241
|
-
].join("\n")
|
|
242
|
-
);
|
|
243
|
-
writeText(project.pencilDesignPath, "# Pencil Design\n");
|
|
244
|
-
|
|
245
|
-
const result = auditProject(project.root, {
|
|
246
|
-
mode: "completion",
|
|
247
|
-
changeId
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
assert.equal(result.status, "PASS");
|
|
251
|
-
assert.doesNotMatch(
|
|
252
|
-
result.warnings.join("\n"),
|
|
253
|
-
/Design-supervisor reviewers.*does not record a `## Design-Supervisor Review` section/i
|
|
254
|
-
);
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
runTest("integrity audit warns when supervisor review is required but no review is recorded", () => {
|
|
258
|
-
const harness = createHarness();
|
|
259
|
-
const project = setupProject(harness, "warn-supervisor-review", {
|
|
260
|
-
requireSupervisorReview: true
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
writeText(project.pencilDesignPath, "# Pencil Design\n");
|
|
264
|
-
|
|
265
|
-
const result = auditProject(project.root, {
|
|
266
|
-
mode: "integrity"
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
assert.equal(result.status, "WARN");
|
|
270
|
-
assert.match(
|
|
271
|
-
result.warnings.join("\n"),
|
|
272
|
-
/Design-supervisor reviewers.*does not record a `## Design-Supervisor Review` section/i
|
|
273
|
-
);
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
runTest("completion audit fails when required supervisor review is missing Issue list or Revision outcome", () => {
|
|
277
|
-
const harness = createHarness();
|
|
278
|
-
const project = setupProject(harness, "malformed-supervisor-review", {
|
|
279
|
-
requireSupervisorReview: true
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
writeText(
|
|
283
|
-
project.pencilDesignPath,
|
|
284
|
-
[
|
|
285
|
-
"# Pencil Design",
|
|
286
|
-
"",
|
|
287
|
-
"## Design-Supervisor Review",
|
|
288
|
-
"- Configured reviewers: frontend-skill",
|
|
289
|
-
"- Review mode: screenshot-and-theme",
|
|
290
|
-
"- Review inputs: screenshots, pencil variables",
|
|
291
|
-
"- Status: PASS",
|
|
292
|
-
""
|
|
293
|
-
].join("\n")
|
|
294
|
-
);
|
|
295
|
-
|
|
296
|
-
const result = auditProject(project.root, {
|
|
297
|
-
mode: "completion",
|
|
298
|
-
changeId
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
assert.equal(result.status, "FAIL");
|
|
302
|
-
assert.match(
|
|
303
|
-
result.failures.join("\n"),
|
|
304
|
-
/missing required field\(s\): Issue list and Revision outcome/i
|
|
305
|
-
);
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
runTest("completion audit passes when required supervisor review is recorded as PASS", () => {
|
|
309
|
-
const harness = createHarness();
|
|
310
|
-
const project = setupProject(harness, "pass-supervisor-review", {
|
|
311
|
-
requireSupervisorReview: true
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
writeText(
|
|
315
|
-
project.pencilDesignPath,
|
|
316
|
-
[
|
|
317
|
-
"# Pencil Design",
|
|
318
|
-
"",
|
|
319
|
-
"## Design-Supervisor Review",
|
|
320
|
-
"- Configured reviewers: frontend-skill",
|
|
321
|
-
"- Executed reviewers: frontend-skill",
|
|
322
|
-
"- Review source: skill",
|
|
323
|
-
"- Review mode: screenshot-and-theme",
|
|
324
|
-
"- Review inputs: screenshots, pencil variables, visual thesis, content plan, interaction thesis",
|
|
325
|
-
"- Status: PASS",
|
|
326
|
-
"- Issue list: none",
|
|
327
|
-
"- Revision outcome: approved",
|
|
328
|
-
"- Broad expansion approved: yes",
|
|
329
|
-
"- Implementation-task handoff approved: yes",
|
|
330
|
-
""
|
|
331
|
-
].join("\n")
|
|
332
|
-
);
|
|
333
|
-
|
|
334
|
-
const result = auditProject(project.root, {
|
|
335
|
-
mode: "completion",
|
|
336
|
-
changeId
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
assert.equal(
|
|
340
|
-
result.status,
|
|
341
|
-
"PASS",
|
|
342
|
-
`expected completion audit to pass:\n${JSON.stringify(result, null, 2)}`
|
|
343
|
-
);
|
|
344
|
-
assert.match(
|
|
345
|
-
result.notes.join("\n"),
|
|
346
|
-
/Detected design-supervisor review status PASS/i
|
|
347
|
-
);
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
runTest("completion audit accepts the latest round section when multiple Design-Supervisor Review headings exist", () => {
|
|
351
|
-
const harness = createHarness();
|
|
352
|
-
const project = setupProject(harness, "latest-supervisor-round-wins", {
|
|
353
|
-
requireSupervisorReview: true
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
writeText(
|
|
357
|
-
project.pencilDesignPath,
|
|
358
|
-
[
|
|
359
|
-
"# Pencil Design",
|
|
360
|
-
"",
|
|
361
|
-
"## Design-Supervisor Review",
|
|
362
|
-
"- Configured reviewers: frontend-skill",
|
|
363
|
-
"- Executed reviewers: frontend-skill",
|
|
364
|
-
"- Review source: skill",
|
|
365
|
-
"- Status: BLOCK",
|
|
366
|
-
"- Issue list: command not found",
|
|
367
|
-
"- Revision outcome: blocked",
|
|
368
|
-
"",
|
|
369
|
-
"## Design-Supervisor Review (Round 2 Attempt)",
|
|
370
|
-
"- Configured reviewers: frontend-skill",
|
|
371
|
-
"- Executed reviewers: frontend-skill",
|
|
372
|
-
"- Review source: skill",
|
|
373
|
-
"- Status: PASS",
|
|
374
|
-
"- Issue list: none",
|
|
375
|
-
"- Revision outcome: accepted",
|
|
376
|
-
""
|
|
377
|
-
].join("\n")
|
|
378
|
-
);
|
|
379
|
-
|
|
380
|
-
const result = auditProject(project.root, {
|
|
381
|
-
mode: "completion",
|
|
382
|
-
changeId
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
assert.equal(
|
|
386
|
-
result.status,
|
|
387
|
-
"PASS",
|
|
388
|
-
`expected completion audit to use latest review section:\n${JSON.stringify(result, null, 2)}`
|
|
389
|
-
);
|
|
390
|
-
assert.match(
|
|
391
|
-
result.notes.join("\n"),
|
|
392
|
-
/Detected design-supervisor review status PASS/i
|
|
393
|
-
);
|
|
394
|
-
});
|
|
395
|
-
|
|
396
|
-
runTest("completion audit accepts multiline Issue list and Revision outcome fields", () => {
|
|
397
|
-
const harness = createHarness();
|
|
398
|
-
const project = setupProject(harness, "multiline-supervisor-fields", {
|
|
399
|
-
requireSupervisorReview: true
|
|
400
|
-
});
|
|
401
|
-
|
|
402
|
-
writeText(
|
|
403
|
-
project.pencilDesignPath,
|
|
404
|
-
[
|
|
405
|
-
"# Pencil Design",
|
|
406
|
-
"",
|
|
407
|
-
"## Design-Supervisor Review",
|
|
408
|
-
"- Configured reviewers: frontend-skill",
|
|
409
|
-
"- Executed reviewers: frontend-skill",
|
|
410
|
-
"- Review source: skill",
|
|
411
|
-
"- Status: WARN",
|
|
412
|
-
"- Issue list:",
|
|
413
|
-
" - icon alignment drift on top bar",
|
|
414
|
-
" - hierarchy contrast needs one more pass",
|
|
415
|
-
"- Revision outcome:",
|
|
416
|
-
" - accepted with follow-up in next visual pass",
|
|
417
|
-
""
|
|
418
|
-
].join("\n")
|
|
419
|
-
);
|
|
420
|
-
|
|
421
|
-
const result = auditProject(project.root, {
|
|
422
|
-
mode: "completion",
|
|
423
|
-
changeId
|
|
424
|
-
});
|
|
425
|
-
|
|
426
|
-
assert.equal(
|
|
427
|
-
result.status,
|
|
428
|
-
"PASS",
|
|
429
|
-
`expected accepted WARN with multiline fields to pass:\n${JSON.stringify(result, null, 2)}`
|
|
430
|
-
);
|
|
431
|
-
assert.match(
|
|
432
|
-
result.notes.join("\n"),
|
|
433
|
-
/Detected design-supervisor review status WARN/i
|
|
434
|
-
);
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
runTest("completion audit fails when required supervisor review is not skill-backed", () => {
|
|
438
|
-
const harness = createHarness();
|
|
439
|
-
const project = setupProject(harness, "required-supervisor-skill-source", {
|
|
440
|
-
requireSupervisorReview: true
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
writeText(
|
|
444
|
-
project.pencilDesignPath,
|
|
445
|
-
[
|
|
446
|
-
"# Pencil Design",
|
|
447
|
-
"",
|
|
448
|
-
"## Design-Supervisor Review",
|
|
449
|
-
"- Configured reviewers: frontend-skill",
|
|
450
|
-
"- Review source: inferred",
|
|
451
|
-
"- Status: PASS",
|
|
452
|
-
"- Issue list: none",
|
|
453
|
-
"- Revision outcome: approved",
|
|
454
|
-
""
|
|
455
|
-
].join("\n")
|
|
456
|
-
);
|
|
457
|
-
|
|
458
|
-
const result = auditProject(project.root, {
|
|
459
|
-
mode: "completion",
|
|
460
|
-
changeId
|
|
461
|
-
});
|
|
462
|
-
|
|
463
|
-
assert.equal(result.status, "FAIL");
|
|
464
|
-
assert.match(
|
|
465
|
-
result.failures.join("\n"),
|
|
466
|
-
/must be skill-backed.*Require Supervisor Review: true/i
|
|
467
|
-
);
|
|
468
|
-
});
|
|
469
|
-
|
|
470
|
-
runTest("completion audit fails when required supervisor review omits executed reviewers", () => {
|
|
471
|
-
const harness = createHarness();
|
|
472
|
-
const project = setupProject(harness, "required-supervisor-executed-missing", {
|
|
473
|
-
requireSupervisorReview: true
|
|
474
|
-
});
|
|
475
|
-
|
|
476
|
-
writeText(
|
|
477
|
-
project.pencilDesignPath,
|
|
478
|
-
[
|
|
479
|
-
"# Pencil Design",
|
|
480
|
-
"",
|
|
481
|
-
"## Design-Supervisor Review",
|
|
482
|
-
"- Configured reviewers: frontend-skill",
|
|
483
|
-
"- Review source: skill",
|
|
484
|
-
"- Status: PASS",
|
|
485
|
-
"- Issue list: none",
|
|
486
|
-
"- Revision outcome: approved",
|
|
487
|
-
""
|
|
488
|
-
].join("\n")
|
|
489
|
-
);
|
|
490
|
-
|
|
491
|
-
const result = auditProject(project.root, {
|
|
492
|
-
mode: "completion",
|
|
493
|
-
changeId
|
|
494
|
-
});
|
|
495
|
-
|
|
496
|
-
assert.equal(result.status, "FAIL");
|
|
497
|
-
assert.match(
|
|
498
|
-
result.failures.join("\n"),
|
|
499
|
-
/missing required field\(s\): Executed reviewers/i
|
|
500
|
-
);
|
|
501
|
-
});
|
|
502
|
-
|
|
503
|
-
runTest("completion audit fails when required supervisor review did not execute all configured reviewers", () => {
|
|
504
|
-
const harness = createHarness();
|
|
505
|
-
const project = setupProject(harness, "required-supervisor-executed-incomplete", {
|
|
506
|
-
requireSupervisorReview: true
|
|
507
|
-
});
|
|
508
|
-
|
|
509
|
-
writeText(
|
|
510
|
-
project.pencilDesignPath,
|
|
511
|
-
[
|
|
512
|
-
"# Pencil Design",
|
|
513
|
-
"",
|
|
514
|
-
"## Design-Supervisor Review",
|
|
515
|
-
"- Configured reviewers: frontend-skill",
|
|
516
|
-
"- Executed reviewers: ui-ux-pro-max",
|
|
517
|
-
"- Review source: skill",
|
|
518
|
-
"- Status: PASS",
|
|
519
|
-
"- Issue list: none",
|
|
520
|
-
"- Revision outcome: approved",
|
|
521
|
-
""
|
|
522
|
-
].join("\n")
|
|
523
|
-
);
|
|
524
|
-
|
|
525
|
-
const result = auditProject(project.root, {
|
|
526
|
-
mode: "completion",
|
|
527
|
-
changeId
|
|
528
|
-
});
|
|
529
|
-
|
|
530
|
-
assert.equal(result.status, "FAIL");
|
|
531
|
-
assert.match(
|
|
532
|
-
result.failures.join("\n"),
|
|
533
|
-
/did not execute configured reviewer skill\(s\): frontend-skill/i
|
|
534
|
-
);
|
|
535
|
-
});
|
|
536
|
-
|
|
537
|
-
console.log("All design-supervisor audit tests passed.");
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
const assert = require("assert/strict");
|
|
2
|
-
const fs = require("fs");
|
|
3
|
-
const os = require("os");
|
|
4
|
-
const path = require("path");
|
|
5
|
-
|
|
6
|
-
const { auditProject } = require("../lib/audit");
|
|
7
|
-
|
|
8
|
-
function runTest(name, fn) {
|
|
9
|
-
try {
|
|
10
|
-
fn();
|
|
11
|
-
console.log(`PASS ${name}`);
|
|
12
|
-
} catch (error) {
|
|
13
|
-
console.error(`FAIL ${name}`);
|
|
14
|
-
throw error;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function writeText(filePath, text) {
|
|
19
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
20
|
-
fs.writeFileSync(filePath, text);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function setupProject(name) {
|
|
24
|
-
const root = fs.mkdtempSync(path.join(os.tmpdir(), `da-vinci-audit-safety-${name}-`));
|
|
25
|
-
const daVinciDir = path.join(root, ".da-vinci");
|
|
26
|
-
const designsDir = path.join(daVinciDir, "designs");
|
|
27
|
-
const designRegistryPath = path.join(daVinciDir, "design-registry.md");
|
|
28
|
-
|
|
29
|
-
writeText(path.join(root, "DA-VINCI.md"), "# DA-VINCI\n");
|
|
30
|
-
writeText(path.join(daVinciDir, "project-inventory.md"), "# Inventory\n");
|
|
31
|
-
writeText(path.join(daVinciDir, "page-map.md"), "# Page Map\n");
|
|
32
|
-
fs.mkdirSync(designsDir, { recursive: true });
|
|
33
|
-
|
|
34
|
-
return {
|
|
35
|
-
root,
|
|
36
|
-
daVinciDir,
|
|
37
|
-
designsDir,
|
|
38
|
-
designRegistryPath
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
runTest("integrity audit warns when registry contains out-of-root .pen reference", () => {
|
|
43
|
-
const project = setupProject("escaped-registry");
|
|
44
|
-
writeText(path.join(project.designsDir, "main.pen"), "{}");
|
|
45
|
-
writeText(
|
|
46
|
-
project.designRegistryPath,
|
|
47
|
-
[
|
|
48
|
-
"# Registry",
|
|
49
|
-
"- Preferred .pen: .da-vinci/designs/main.pen",
|
|
50
|
-
"- Legacy .pen: .da-vinci/designs/../../../outside.pen",
|
|
51
|
-
""
|
|
52
|
-
].join("\n")
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
const result = auditProject(project.root, {
|
|
56
|
-
mode: "integrity"
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
assert.equal(result.failures.length, 0);
|
|
60
|
-
assert.match(result.warnings.join("\n"), /escapes project root and will be ignored/i);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
runTest("integrity audit surfaces traversal truncation warnings on deep trees", () => {
|
|
64
|
-
const project = setupProject("truncated-scan");
|
|
65
|
-
writeText(path.join(project.designsDir, "main.pen"), "{}");
|
|
66
|
-
writeText(
|
|
67
|
-
project.designRegistryPath,
|
|
68
|
-
[
|
|
69
|
-
"# Registry",
|
|
70
|
-
"- Preferred .pen: .da-vinci/designs/main.pen",
|
|
71
|
-
""
|
|
72
|
-
].join("\n")
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
let current = project.designsDir;
|
|
76
|
-
for (let depth = 0; depth < 30; depth += 1) {
|
|
77
|
-
current = path.join(current, `deep-${depth}`);
|
|
78
|
-
fs.mkdirSync(current, { recursive: true });
|
|
79
|
-
}
|
|
80
|
-
writeText(path.join(current, "deep-tree.pen"), "{}");
|
|
81
|
-
|
|
82
|
-
const result = auditProject(project.root, {
|
|
83
|
-
mode: "integrity"
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
assert.match(
|
|
87
|
-
result.warnings.join("\n"),
|
|
88
|
-
/File scan truncated under \.da-vinci[\/\\]designs/i
|
|
89
|
-
);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
console.log("All audit safety tests passed.");
|