@tekyzinc/gsd-t 2.71.14 → 2.71.16
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 +15 -0
- package/bin/design-orchestrator.js +95 -4
- package/bin/orchestrator.js +109 -1
- package/commands/gsd-t-design-build.md +30 -373
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to GSD-T are documented here. Updated with each release.
|
|
4
4
|
|
|
5
|
+
## [2.71.16] - 2026-04-08
|
|
6
|
+
|
|
7
|
+
### Added (orchestrator — automated AI review loop)
|
|
8
|
+
- **Automated review before human review** — orchestrator now spawns an independent reviewer Claude (no builder context) that compares built components against design contracts. If issues found, spawns a fixer Claude, re-measures, and re-reviews (max 2 cycles). Only after automated review passes do items reach human review. This is the Term 2 equivalent, running deterministically in JavaScript.
|
|
9
|
+
- **Review report persistence** — each auto-review cycle writes results to `.gsd-t/design-review/auto-review/`. Unresolved issues are written to `{phase}-unresolved.json` for human visibility.
|
|
10
|
+
- **Structured review output** — reviewer uses `[REVIEW_ISSUES]` markers for reliable parsing. Fallback parser catches DEVIATION/FAIL/CRITICAL keywords.
|
|
11
|
+
|
|
12
|
+
### Pipeline (updated)
|
|
13
|
+
Build → Measure → **Automated AI Review** (reviewer → fixer → re-review loop) → Human Review → Next Tier
|
|
14
|
+
|
|
15
|
+
## [2.71.15] - 2026-04-08
|
|
16
|
+
|
|
17
|
+
### Changed (design-build command → orchestrator delegate)
|
|
18
|
+
- **`gsd-t-design-build.md` now delegates to the JS orchestrator** — the 388-line prompt-based command is replaced with a thin wrapper that runs `gsd-t design-build`. Both `/user:gsd-t-design-build` and `gsd-t design-build` now end up in the same deterministic pipeline. No more prompt-based gates that get skipped.
|
|
19
|
+
|
|
5
20
|
## [2.71.14] - 2026-04-08
|
|
6
21
|
|
|
7
22
|
### Added (design-build orchestrator)
|
|
@@ -300,6 +300,91 @@ function buildFixPrompt(phase, needsWork) {
|
|
|
300
300
|
return `Apply these specific fixes to ${phase} components:\n\n${fixes}\n\nApply the changes and EXIT. Do not rebuild anything else.`;
|
|
301
301
|
}
|
|
302
302
|
|
|
303
|
+
// ─── Automated AI Review (Term 2 equivalent) ───────────────────────────────
|
|
304
|
+
|
|
305
|
+
function buildReviewPrompt(phase, items, measurements, projectDir, ports) {
|
|
306
|
+
const singular = PHASE_SINGULAR[phase];
|
|
307
|
+
const contractsDir = path.join(projectDir, CONTRACTS_DIR);
|
|
308
|
+
|
|
309
|
+
const componentList = items.map(c => {
|
|
310
|
+
const sourcePath = c.sourcePath || guessPaths(phase, c);
|
|
311
|
+
return `- **${c.componentName}** — contract: ${c.fullContractPath}, source: ${sourcePath}, selector: \`${c.selector || "." + c.id}\``;
|
|
312
|
+
}).join("\n");
|
|
313
|
+
|
|
314
|
+
// Include any measurement failures for context
|
|
315
|
+
const failedMeasurements = [];
|
|
316
|
+
for (const item of items) {
|
|
317
|
+
const m = measurements[item.id] || [];
|
|
318
|
+
const failures = m.filter(x => !x.pass);
|
|
319
|
+
if (failures.length > 0) {
|
|
320
|
+
failedMeasurements.push(`- ${item.componentName}: ${failures.map(f => `${f.property}: expected ${f.expected}, got ${f.actual}`).join("; ")}`);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const measurementContext = failedMeasurements.length > 0
|
|
324
|
+
? `\n## Known Measurement Failures\nPlaywright already detected these — verify they are real issues:\n${failedMeasurements.join("\n")}\n`
|
|
325
|
+
: "";
|
|
326
|
+
|
|
327
|
+
return `You are an INDEPENDENT design reviewer. You have NO knowledge of how these components were built. Your job is to compare the built ${phase} against their design contracts and find deviations.
|
|
328
|
+
|
|
329
|
+
## Components to Review
|
|
330
|
+
${componentList}
|
|
331
|
+
|
|
332
|
+
${measurementContext}
|
|
333
|
+
## Review Process
|
|
334
|
+
|
|
335
|
+
For EACH component:
|
|
336
|
+
1. Read the design contract file (path given above) — note every specified property value
|
|
337
|
+
2. Read the source file — check that specified values are implemented correctly
|
|
338
|
+
3. Use Playwright to render the component at http://localhost:${ports.reviewPort}/ and measure:
|
|
339
|
+
- Does the component render and have correct dimensions?
|
|
340
|
+
- Do colors, fonts, spacing, border-radius match the contract?
|
|
341
|
+
- For charts: correct chart type, orientation, axis labels, legend position, data format?
|
|
342
|
+
- For layouts: correct grid columns, gap, padding, child count and arrangement?
|
|
343
|
+
- For interactive elements: correct states, hover effects, click behavior?
|
|
344
|
+
4. Compare contract values against actual rendered values — be SPECIFIC (exact px, hex, counts)
|
|
345
|
+
|
|
346
|
+
## Output Format
|
|
347
|
+
|
|
348
|
+
Output your findings between these markers. Each issue must have component, severity (critical/high/medium/low), and description with SPECIFIC contract vs. actual values:
|
|
349
|
+
|
|
350
|
+
[REVIEW_ISSUES]
|
|
351
|
+
[
|
|
352
|
+
{"component": "ComponentName", "severity": "critical", "description": "Contract specifies donut chart but rendered as pie chart (no inner radius)"},
|
|
353
|
+
{"component": "ComponentName", "severity": "high", "description": "Grid gap: contract 16px, actual 24px"}
|
|
354
|
+
]
|
|
355
|
+
[/REVIEW_ISSUES]
|
|
356
|
+
|
|
357
|
+
If ALL components match their contracts, output:
|
|
358
|
+
[REVIEW_ISSUES]
|
|
359
|
+
[]
|
|
360
|
+
[/REVIEW_ISSUES]
|
|
361
|
+
|
|
362
|
+
## Rules
|
|
363
|
+
- You write ZERO code. You ONLY review.
|
|
364
|
+
- Be HARSH. Your value is in catching what the builder missed.
|
|
365
|
+
- NEVER say "looks close" or "appears to match" — give SPECIFIC values.
|
|
366
|
+
- Every contract property must be verified. Missing verification = missed issue.
|
|
367
|
+
- Severity guide: critical = wrong component type, missing element, broken render. high = wrong dimensions, colors, layout. medium = spacing/padding off. low = minor visual difference.`;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function buildAutoFixPrompt(phase, issues, items, projectDir) {
|
|
371
|
+
const issueList = issues.map((issue, i) => {
|
|
372
|
+
const item = items.find(c => c.componentName === issue.component);
|
|
373
|
+
const contractPath = item ? item.fullContractPath : "check .gsd-t/contracts/design/";
|
|
374
|
+
return `${i + 1}. [${issue.severity}] **${issue.component}** — ${issue.description}\n Contract: ${contractPath}`;
|
|
375
|
+
}).join("\n");
|
|
376
|
+
|
|
377
|
+
return `The automated design reviewer found these issues. Fix each one by reading the design contract and correcting the implementation.
|
|
378
|
+
|
|
379
|
+
## Issues to Fix
|
|
380
|
+
${issueList}
|
|
381
|
+
|
|
382
|
+
## Rules
|
|
383
|
+
- Read each component's design contract for the correct values — do NOT guess
|
|
384
|
+
- Fix ONLY the listed issues — do not modify other components or add features
|
|
385
|
+
- After fixing all issues, EXIT. Do not start servers or ask for review.`;
|
|
386
|
+
}
|
|
387
|
+
|
|
303
388
|
// ─── Summary ────────────────────────────────────────────────────────────────
|
|
304
389
|
|
|
305
390
|
function formatSummary(phase, result) {
|
|
@@ -329,11 +414,13 @@ ${BOLD}Pipeline:${RESET}
|
|
|
329
414
|
1. Read contracts from .gsd-t/contracts/design/
|
|
330
415
|
2. Start dev server + review server
|
|
331
416
|
3. For each tier (elements → widgets → pages):
|
|
332
|
-
a. Spawn Claude to build components
|
|
417
|
+
a. Spawn Claude (builder) to build components from contracts
|
|
333
418
|
b. Measure with Playwright
|
|
334
|
-
c.
|
|
335
|
-
d.
|
|
336
|
-
e.
|
|
419
|
+
c. Spawn Claude (reviewer) to compare against contracts — independent, no builder context
|
|
420
|
+
d. If reviewer finds issues → spawn Claude (fixer) → re-measure → re-review (max 2 cycles)
|
|
421
|
+
e. Queue for human review (only after automated review passes)
|
|
422
|
+
f. Wait for human review submission (blocks until human approves)
|
|
423
|
+
g. Process feedback, proceed to next tier
|
|
337
424
|
`);
|
|
338
425
|
}
|
|
339
426
|
|
|
@@ -351,11 +438,15 @@ const designBuildWorkflow = {
|
|
|
351
438
|
timeout: 600_000,
|
|
352
439
|
devServerTimeout: 30_000,
|
|
353
440
|
maxReviewCycles: 3,
|
|
441
|
+
maxAutoReviewCycles: 2,
|
|
442
|
+
reviewTimeout: 300_000,
|
|
354
443
|
},
|
|
355
444
|
completionMessage: "All done. Run your app to verify: npm run dev",
|
|
356
445
|
|
|
357
446
|
discoverWork,
|
|
358
447
|
buildPrompt,
|
|
448
|
+
buildReviewPrompt,
|
|
449
|
+
buildAutoFixPrompt,
|
|
359
450
|
measure,
|
|
360
451
|
buildQueueItem,
|
|
361
452
|
buildFixPrompt,
|
package/bin/orchestrator.js
CHANGED
|
@@ -576,7 +576,78 @@ ${BOLD}Phases:${RESET} ${this.wf.phases.join(" → ")}
|
|
|
576
576
|
measurements = this.wf.measure(projectDir, phase, items, { devPort, reviewPort }) || {};
|
|
577
577
|
}
|
|
578
578
|
|
|
579
|
-
//
|
|
579
|
+
// 6d.5. Automated AI review loop (Term 2 equivalent)
|
|
580
|
+
// Spawns an independent reviewer Claude that compares built output against contracts.
|
|
581
|
+
// If issues found → spawn fixer Claude → re-measure → re-review until clean.
|
|
582
|
+
const maxAutoReviewCycles = this.wf.defaults?.maxAutoReviewCycles || 2;
|
|
583
|
+
if (this.wf.buildReviewPrompt) {
|
|
584
|
+
let autoReviewCycle = 0;
|
|
585
|
+
let autoReviewClean = false;
|
|
586
|
+
|
|
587
|
+
while (autoReviewCycle < maxAutoReviewCycles && !autoReviewClean) {
|
|
588
|
+
autoReviewCycle++;
|
|
589
|
+
heading(`Automated Review — ${phase} (cycle ${autoReviewCycle}/${maxAutoReviewCycles})`);
|
|
590
|
+
|
|
591
|
+
// Spawn reviewer Claude — independent, no builder context
|
|
592
|
+
const reviewPrompt = this.wf.buildReviewPrompt(phase, items, measurements, projectDir, { devPort, reviewPort });
|
|
593
|
+
log(`\n${CYAN} ⚙${RESET} Spawning reviewer Claude for ${phase}...`);
|
|
594
|
+
const reviewTimeout = this.wf.defaults?.reviewTimeout || 300_000;
|
|
595
|
+
const reviewResult = this.spawnClaude(projectDir, reviewPrompt, reviewTimeout);
|
|
596
|
+
|
|
597
|
+
// Parse reviewer output for issues
|
|
598
|
+
const issues = this.wf.parseReviewResult
|
|
599
|
+
? this.wf.parseReviewResult(reviewResult.output, phase)
|
|
600
|
+
: this._parseDefaultReviewResult(reviewResult.output);
|
|
601
|
+
|
|
602
|
+
if (reviewResult.exitCode === 0) {
|
|
603
|
+
success(`Reviewer finished in ${reviewResult.duration}s`);
|
|
604
|
+
} else {
|
|
605
|
+
warn(`Reviewer exited with code ${reviewResult.exitCode} after ${reviewResult.duration}s`);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// Write review report
|
|
609
|
+
const reportDir = path.join(this.getReviewDir(projectDir), "auto-review");
|
|
610
|
+
ensureDir(reportDir);
|
|
611
|
+
fs.writeFileSync(
|
|
612
|
+
path.join(reportDir, `${phase}-cycle-${autoReviewCycle}.json`),
|
|
613
|
+
JSON.stringify({ cycle: autoReviewCycle, issues, output: reviewResult.output.slice(0, 5000) }, null, 2)
|
|
614
|
+
);
|
|
615
|
+
|
|
616
|
+
if (issues.length === 0) {
|
|
617
|
+
autoReviewClean = true;
|
|
618
|
+
success(`Automated review passed — no issues found in ${phase}`);
|
|
619
|
+
} else {
|
|
620
|
+
warn(`Automated review found ${issues.length} issue(s) in ${phase}`);
|
|
621
|
+
for (const issue of issues) {
|
|
622
|
+
dim(`${issue.component || "?"}: ${issue.description || issue.reason || "issue"} [${issue.severity || "medium"}]`);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
if (autoReviewCycle < maxAutoReviewCycles) {
|
|
626
|
+
// Spawn fixer Claude with the issues
|
|
627
|
+
const fixPrompt = this.wf.buildAutoFixPrompt
|
|
628
|
+
? this.wf.buildAutoFixPrompt(phase, issues, items, projectDir)
|
|
629
|
+
: this._defaultAutoFixPrompt(phase, issues);
|
|
630
|
+
|
|
631
|
+
log(`\n${CYAN} ⚙${RESET} Spawning fixer Claude for ${issues.length} issue(s)...`);
|
|
632
|
+
const fixResult = this.spawnClaude(projectDir, fixPrompt, 120_000);
|
|
633
|
+
if (fixResult.exitCode === 0) success(`Fixer finished in ${fixResult.duration}s`);
|
|
634
|
+
else warn(`Fixer exited with code ${fixResult.exitCode}`);
|
|
635
|
+
|
|
636
|
+
// Re-measure after fixes
|
|
637
|
+
if (!skipMeasure && this.wf.measure) {
|
|
638
|
+
measurements = this.wf.measure(projectDir, phase, items, { devPort, reviewPort }) || {};
|
|
639
|
+
}
|
|
640
|
+
} else {
|
|
641
|
+
warn(`Max auto-review cycles reached — ${issues.length} issue(s) will go to human review`);
|
|
642
|
+
// Attach unresolved issues to measurements for human visibility
|
|
643
|
+
const issueFile = path.join(this.getReviewDir(projectDir), "auto-review", `${phase}-unresolved.json`);
|
|
644
|
+
fs.writeFileSync(issueFile, JSON.stringify(issues, null, 2));
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// 6e. Human review cycle
|
|
580
651
|
let reviewCycle = 0;
|
|
581
652
|
let allApproved = false;
|
|
582
653
|
|
|
@@ -655,6 +726,43 @@ ${BOLD}Phases:${RESET} ${this.wf.phases.join(" → ")}
|
|
|
655
726
|
};
|
|
656
727
|
}
|
|
657
728
|
|
|
729
|
+
_parseDefaultReviewResult(output) {
|
|
730
|
+
// Try to parse JSON issues array from reviewer output
|
|
731
|
+
// Reviewer is instructed to output JSON between markers
|
|
732
|
+
const jsonMatch = output.match(/\[REVIEW_ISSUES\]([\s\S]*?)\[\/REVIEW_ISSUES\]/);
|
|
733
|
+
if (jsonMatch) {
|
|
734
|
+
try { return JSON.parse(jsonMatch[1].trim()); } catch { /* fall through */ }
|
|
735
|
+
}
|
|
736
|
+
// Fallback: look for PASS/FAIL verdict
|
|
737
|
+
if (/\bGRUDGING PASS\b/i.test(output) || /\bPASS\b.*\b0 issues\b/i.test(output) || /no issues found/i.test(output)) {
|
|
738
|
+
return [];
|
|
739
|
+
}
|
|
740
|
+
// If we see FAIL or DEVIATION keywords, extract what we can
|
|
741
|
+
const issues = [];
|
|
742
|
+
const deviationRegex = /(?:DEVIATION|FAIL|CRITICAL|ISSUE)[:\s—-]+(.+)/gi;
|
|
743
|
+
let match;
|
|
744
|
+
while ((match = deviationRegex.exec(output)) !== null) {
|
|
745
|
+
issues.push({ description: match[1].trim(), severity: "medium" });
|
|
746
|
+
}
|
|
747
|
+
return issues;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
_defaultAutoFixPrompt(phase, issues) {
|
|
751
|
+
const issueList = issues.map((issue, i) =>
|
|
752
|
+
`${i + 1}. [${issue.severity || "medium"}] ${issue.component || "unknown"}: ${issue.description || issue.reason || "fix needed"}`
|
|
753
|
+
).join("\n");
|
|
754
|
+
|
|
755
|
+
return `The automated reviewer found these issues in the ${phase} components. Fix each one.
|
|
756
|
+
|
|
757
|
+
## Issues
|
|
758
|
+
${issueList}
|
|
759
|
+
|
|
760
|
+
## Rules
|
|
761
|
+
- Read the relevant design contract for each component to verify the correct values
|
|
762
|
+
- Fix ONLY the listed issues — do not modify other components
|
|
763
|
+
- After fixing, EXIT. Do not start servers or ask for review.`;
|
|
764
|
+
}
|
|
765
|
+
|
|
658
766
|
_defaultFixPrompt(phase, needsWork) {
|
|
659
767
|
const fixes = needsWork.map(item => {
|
|
660
768
|
const parts = [`Fix ${item.id}:`];
|
|
@@ -1,387 +1,44 @@
|
|
|
1
|
-
# GSD-T: Design Build —
|
|
1
|
+
# GSD-T: Design Build — Deterministic Design-to-Code Pipeline
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This command delegates to the **JavaScript orchestrator** for ironclad flow control. Do NOT attempt to run the build pipeline inline — the orchestrator handles Claude spawning, measurement, review gates, and feedback processing deterministically.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Step 1: Launch the Orchestrator
|
|
6
6
|
|
|
7
|
-
## Step 0: Validate Prerequisites
|
|
8
|
-
|
|
9
|
-
1. Read `.gsd-t/contracts/design/INDEX.md` — if missing, STOP: "Run `/user:gsd-t-design-decompose` first to create design contracts."
|
|
10
|
-
2. Read `.gsd-t/progress.md` for current state
|
|
11
|
-
3. Verify `scripts/gsd-t-design-review-server.js` exists (GSD-T package)
|
|
12
|
-
- Get the GSD-T install path: `npm root -g` → `{global_root}/@tekyzinc/gsd-t/scripts/`
|
|
13
|
-
- If not found: "Update GSD-T: `/user:gsd-t-version-update`"
|
|
14
|
-
|
|
15
|
-
## Step 1: Start Infrastructure
|
|
16
|
-
|
|
17
|
-
### 1a. Dev Server
|
|
18
|
-
|
|
19
|
-
Check if a dev server is running:
|
|
20
|
-
```bash
|
|
21
|
-
lsof -i :5173 2>/dev/null | head -2
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
If not running, detect and start:
|
|
25
|
-
```bash
|
|
26
|
-
# Check package.json for dev command
|
|
27
|
-
npm run dev &
|
|
28
|
-
# Wait for server to be ready
|
|
29
|
-
for i in $(seq 1 30); do curl -s http://localhost:5173 > /dev/null 2>&1 && break; sleep 1; done
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
Record the dev server port as `$DEV_PORT`.
|
|
33
|
-
|
|
34
|
-
### 1b. Review Server
|
|
35
|
-
|
|
36
|
-
Find the review server script:
|
|
37
|
-
```bash
|
|
38
|
-
GSD_ROOT=$(npm root -g)/@tekyzinc/gsd-t/scripts
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
Start the review server as a background process:
|
|
42
|
-
```bash
|
|
43
|
-
node $GSD_ROOT/gsd-t-design-review-server.js \
|
|
44
|
-
--target http://localhost:$DEV_PORT \
|
|
45
|
-
--project $PWD \
|
|
46
|
-
--port 3456 &
|
|
47
|
-
REVIEW_PID=$!
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
Verify it's running:
|
|
51
|
-
```bash
|
|
52
|
-
curl -s http://localhost:3456/review/api/status
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
### 1c. Launch Term 2 (Independent Review Session)
|
|
56
|
-
|
|
57
|
-
Write the review prompt to disk (Term 2 reads this, not Term 1's context):
|
|
58
|
-
```bash
|
|
59
|
-
cat > .gsd-t/design-review/review-prompt.md << 'PROMPT'
|
|
60
|
-
You are the design review agent (Term 2). You are an INDEPENDENT reviewer — you have NO knowledge of how components were built. Your job is to compare built output against design contracts.
|
|
61
|
-
|
|
62
|
-
## Your Loop
|
|
63
|
-
|
|
64
|
-
1. Poll `.gsd-t/design-review/queue/` every 5 seconds for new JSON files
|
|
65
|
-
2. For each queue item:
|
|
66
|
-
a. Read the queue JSON — it has component name, selector, measurements, source path
|
|
67
|
-
b. Read the matching design contract from `.gsd-t/contracts/design/`
|
|
68
|
-
c. Open the app at http://localhost:3456/ (proxied through review server)
|
|
69
|
-
d. Use Playwright to navigate to the component and evaluate it against the contract
|
|
70
|
-
e. AI Review — check what measurements can't catch:
|
|
71
|
-
- Does the visual hierarchy feel right?
|
|
72
|
-
- Are proportions correct even if individual measurements pass?
|
|
73
|
-
- Does the component look like the contract describes, holistically?
|
|
74
|
-
f. If CRITICAL issues found (wrong chart type, missing elements, wrong data model):
|
|
75
|
-
- Write rejection to `.gsd-t/design-review/rejected/{id}.json`:
|
|
76
|
-
`{ "id": "...", "reason": "...", "severity": "critical", "timestamp": "..." }`
|
|
77
|
-
- The review server will notify Term 1 automatically
|
|
78
|
-
g. If no critical issues → the component passes to human review (review UI handles this)
|
|
79
|
-
3. When `.gsd-t/design-review/shutdown.json` appears → exit cleanly
|
|
80
|
-
|
|
81
|
-
## Rules
|
|
82
|
-
- You write ZERO code. You ONLY review.
|
|
83
|
-
- You have NO context about how anything was built — judge purely by contract vs. output.
|
|
84
|
-
- Be harsh. Your value is in catching what the builder missed.
|
|
85
|
-
- Check `.gsd-t/contracts/design/elements/`, `widgets/`, and `pages/` for specs.
|
|
86
|
-
PROMPT
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
Launch a new terminal with an independent Claude session:
|
|
90
|
-
```bash
|
|
91
|
-
# macOS
|
|
92
|
-
osascript -e "tell application \"Terminal\" to do script \"cd $(pwd) && claude --print 'Read .gsd-t/design-review/review-prompt.md and execute the instructions. This is your only directive.'\""
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
```bash
|
|
96
|
-
# Linux (fallback)
|
|
97
|
-
gnome-terminal -- bash -c "cd $(pwd) && claude --print 'Read .gsd-t/design-review/review-prompt.md and execute the instructions. This is your only directive.'; exec bash"
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
Display to user:
|
|
101
|
-
```
|
|
102
|
-
Design Build — Infrastructure Ready
|
|
103
|
-
✓ Dev server: http://localhost:$DEV_PORT
|
|
104
|
-
✓ Review server: http://localhost:3456
|
|
105
|
-
✓ Review UI: http://localhost:3456/review
|
|
106
|
-
✓ Term 2: Independent review session launched
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
Auto-open the review UI:
|
|
110
|
-
```bash
|
|
111
|
-
open http://localhost:3456/review 2>/dev/null || xdg-open http://localhost:3456/review 2>/dev/null
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
## Step 2: Build Phase — Elements
|
|
115
|
-
|
|
116
|
-
Read all element contracts from `.gsd-t/contracts/design/elements/`. Sort by any `order` field or alphabetically.
|
|
117
|
-
|
|
118
|
-
For each element contract:
|
|
119
|
-
|
|
120
|
-
### 2a. Build the Element
|
|
121
|
-
|
|
122
|
-
Read the element contract. Build or update the component at the specified source path. Follow the contract exactly:
|
|
123
|
-
- Chart type, dimensions, colors, spacing, typography
|
|
124
|
-
- Props interface matching the contract's data model
|
|
125
|
-
- Import patterns (use existing design system components where specified)
|
|
126
|
-
|
|
127
|
-
### 2b. Render-Measure-Compare
|
|
128
|
-
|
|
129
|
-
After building, use Playwright to measure the rendered component against the contract specs:
|
|
130
|
-
|
|
131
|
-
```javascript
|
|
132
|
-
// In Playwright page.evaluate():
|
|
133
|
-
const el = document.querySelector(selector);
|
|
134
|
-
const s = getComputedStyle(el);
|
|
135
|
-
const rect = el.getBoundingClientRect();
|
|
136
|
-
// Measure each spec property from the contract
|
|
137
|
-
// Compare against expected values
|
|
138
|
-
// Build measurements array: [{ property, expected, actual, pass }]
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
### 2c. Queue for Review
|
|
142
|
-
|
|
143
|
-
Write the queue JSON to `.gsd-t/design-review/queue/{element-id}.json`:
|
|
144
|
-
```json
|
|
145
|
-
{
|
|
146
|
-
"id": "element-donut-chart",
|
|
147
|
-
"name": "DonutChart",
|
|
148
|
-
"type": "element",
|
|
149
|
-
"order": 1,
|
|
150
|
-
"selector": "svg[viewBox='0 0 200 200']",
|
|
151
|
-
"sourcePath": "src/components/elements/DonutChart.vue",
|
|
152
|
-
"route": "/",
|
|
153
|
-
"measurements": [
|
|
154
|
-
{ "property": "chart type", "expected": "donut", "actual": "donut", "pass": true },
|
|
155
|
-
...
|
|
156
|
-
]
|
|
157
|
-
}
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
### 2d. Check for Auto-Rejections
|
|
161
|
-
|
|
162
|
-
After queuing, check for immediate rejection from Term 2:
|
|
163
|
-
```bash
|
|
164
|
-
# Brief wait for Term 2 to process
|
|
165
|
-
sleep 3
|
|
166
|
-
ls .gsd-t/design-review/rejected/{element-id}.json 2>/dev/null
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
If rejected:
|
|
170
|
-
- Read the rejection reason
|
|
171
|
-
- Fix the element based on the rejection feedback
|
|
172
|
-
- Re-measure and re-queue (max 2 auto-rejection cycles per element)
|
|
173
|
-
|
|
174
|
-
### 2e. Continue Building
|
|
175
|
-
|
|
176
|
-
Continue building remaining elements. Don't wait for human review — queue them all.
|
|
177
|
-
|
|
178
|
-
## Step 3: Wait for Human Review (BLOCKING)
|
|
179
|
-
|
|
180
|
-
**This is a BLOCKING step. Do NOT proceed to Step 4 until the human submits their review. Do NOT skip this step. Do NOT treat this as optional.**
|
|
181
|
-
|
|
182
|
-
After all elements are built and queued, **STOP** and enter the poll loop:
|
|
183
|
-
|
|
184
|
-
```
|
|
185
|
-
Waiting for human review of elements...
|
|
186
|
-
Review UI: http://localhost:3456/review
|
|
187
|
-
{N} elements queued, awaiting submission
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
Poll for the review-complete signal — **actually run this bash loop**:
|
|
191
7
|
```bash
|
|
192
|
-
|
|
193
|
-
sleep 5
|
|
194
|
-
done
|
|
8
|
+
gsd-t design-build $ARGUMENTS
|
|
195
9
|
```
|
|
196
10
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
## Step 4: Process Feedback
|
|
11
|
+
That's it. The orchestrator handles everything:
|
|
200
12
|
|
|
201
|
-
|
|
13
|
+
1. Reads contracts from `.gsd-t/contracts/design/`
|
|
14
|
+
2. Starts dev server + review server
|
|
15
|
+
3. For each tier (elements → widgets → pages):
|
|
16
|
+
- Spawns Claude to build components from contracts
|
|
17
|
+
- Measures with Playwright
|
|
18
|
+
- Queues for human review
|
|
19
|
+
- **Blocks in a JS polling loop** until the human submits (ironclad gate)
|
|
20
|
+
- Processes feedback, applies fixes if needed
|
|
21
|
+
- Proceeds to next tier only after approval
|
|
202
22
|
|
|
203
|
-
|
|
23
|
+
## Options
|
|
204
24
|
|
|
205
|
-
|
|
206
|
-
- Mark element as complete
|
|
207
|
-
- No action needed
|
|
25
|
+
Pass any of these as `$ARGUMENTS`:
|
|
208
26
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
-
|
|
27
|
+
| Flag | Purpose |
|
|
28
|
+
|------|---------|
|
|
29
|
+
| `--resume` | Resume from last saved state after interruption |
|
|
30
|
+
| `--tier <name>` | Start from a specific tier (`elements`, `widgets`, `pages`) |
|
|
31
|
+
| `--project <dir>` | Target project directory (default: current directory) |
|
|
32
|
+
| `--dev-port <N>` | Dev server port (default: 5173) |
|
|
33
|
+
| `--review-port <N>` | Review server port (default: 3456) |
|
|
34
|
+
| `--timeout <sec>` | Claude timeout per tier in seconds (default: 600) |
|
|
35
|
+
| `--skip-measure` | Skip Playwright measurement (human-review only) |
|
|
218
36
|
|
|
219
|
-
|
|
220
|
-
- Read the comment — it describes a change to make
|
|
221
|
-
- Implement the change described in the comment
|
|
222
|
-
- Re-measure the element
|
|
223
|
-
- Re-queue for review
|
|
37
|
+
## Prerequisites
|
|
224
38
|
|
|
225
|
-
|
|
226
|
-
-
|
|
227
|
-
- Read the comment for additional context or fixes beyond the property changes
|
|
228
|
-
- Implement any additional changes
|
|
229
|
-
- Re-measure and re-queue
|
|
230
|
-
|
|
231
|
-
After processing all feedback:
|
|
232
|
-
- Clear the queue: `rm .gsd-t/design-review/queue/*.json`
|
|
233
|
-
- Delete the signal: `rm .gsd-t/design-review/review-complete.json`
|
|
234
|
-
- Clear feedback: `rm .gsd-t/design-review/feedback/*.json`
|
|
235
|
-
|
|
236
|
-
If any elements were re-queued → return to Step 3 (max 3 review cycles total).
|
|
237
|
-
|
|
238
|
-
## Step 5: Widget Assembly Phase
|
|
239
|
-
|
|
240
|
-
**GATE: Do NOT start this step until ALL elements from Step 2 have been reviewed and approved by the human in the review UI.**
|
|
241
|
-
|
|
242
|
-
1. Read widget contracts from `.gsd-t/contracts/design/widgets/`
|
|
243
|
-
2. For each widget:
|
|
244
|
-
a. Build the widget — MUST import approved element components, not rebuild them inline
|
|
245
|
-
b. Verify imports: grep the widget file for element imports
|
|
246
|
-
c. **Render-Measure-Compare** — use Playwright to measure the assembled widget against its contract specs:
|
|
247
|
-
```javascript
|
|
248
|
-
// In Playwright page.evaluate():
|
|
249
|
-
const widget = document.querySelector(widgetSelector);
|
|
250
|
-
const s = getComputedStyle(widget);
|
|
251
|
-
const rect = widget.getBoundingClientRect();
|
|
252
|
-
// Measure layout properties from widget contract:
|
|
253
|
-
// - grid-template-columns / grid-template-rows (column count, track sizes)
|
|
254
|
-
// - gap / row-gap / column-gap
|
|
255
|
-
// - padding, margin, border-radius
|
|
256
|
-
// - width, height, aspect-ratio
|
|
257
|
-
// Count direct children and verify against contract's expected child count
|
|
258
|
-
const children = widget.children;
|
|
259
|
-
const childRects = [...children].map(c => c.getBoundingClientRect());
|
|
260
|
-
// Verify children-per-row: count children whose top is within 2px of first child's top
|
|
261
|
-
const firstRowTop = childRects[0]?.top;
|
|
262
|
-
const childrenPerRow = childRects.filter(r => Math.abs(r.top - firstRowTop) < 2).length;
|
|
263
|
-
// Compare ALL measured values against contract specs
|
|
264
|
-
// Build measurements array: [{ property, expected, actual, pass }]
|
|
265
|
-
```
|
|
266
|
-
**Every measurement failure MUST appear in the queue JSON measurements array with `pass: false`.**
|
|
267
|
-
d. Write queue JSON to `.gsd-t/design-review/queue/{widget-id}.json` (same format as elements, with `"type": "widget"`)
|
|
268
|
-
e. Check for auto-rejections (sleep 3, check `rejected/` dir) — fix and re-queue if rejected (max 2 cycles)
|
|
269
|
-
3. After ALL widgets are built and queued, **STOP and poll for human review** — this is a BLOCKING wait, do NOT proceed until the human submits:
|
|
270
|
-
```
|
|
271
|
-
Waiting for human review of widgets...
|
|
272
|
-
Review UI: http://localhost:3456/review
|
|
273
|
-
{N} widgets queued, awaiting submission
|
|
274
|
-
```
|
|
275
|
-
```bash
|
|
276
|
-
while [ ! -f .gsd-t/design-review/review-complete.json ]; do
|
|
277
|
-
sleep 5
|
|
278
|
-
done
|
|
279
|
-
```
|
|
280
|
-
4. Process feedback — read `review-complete.json` and each feedback file, apply changes/fixes per the same rules as Step 4. Clear queue, delete signal, clear feedback after processing. If any widgets re-queued → return to sub-step 3 (max 3 review cycles).
|
|
281
|
-
|
|
282
|
-
## Step 6: Page Composition Phase
|
|
283
|
-
|
|
284
|
-
**GATE: Do NOT start this step until ALL widgets from Step 5 have been reviewed and approved by the human in the review UI.**
|
|
285
|
-
|
|
286
|
-
1. Read page contracts from `.gsd-t/contracts/design/pages/`
|
|
287
|
-
2. For each page:
|
|
288
|
-
a. Build the page — MUST import approved widget components
|
|
289
|
-
b. **Render-Measure-Compare** — use Playwright to verify the full page layout against its contract specs:
|
|
290
|
-
```javascript
|
|
291
|
-
// In Playwright page.evaluate():
|
|
292
|
-
// 1. GRID LAYOUT — verify column count matches contract
|
|
293
|
-
const grid = document.querySelector(pageGridSelector);
|
|
294
|
-
const gridStyle = getComputedStyle(grid);
|
|
295
|
-
const columns = gridStyle.gridTemplateColumns.split(' ').length;
|
|
296
|
-
// Compare against contract's expected column count (e.g., 2, not 4)
|
|
297
|
-
|
|
298
|
-
// 2. SECTION ORDERING — verify widgets appear in contract-specified order
|
|
299
|
-
const sections = [...grid.querySelectorAll('[data-section]')];
|
|
300
|
-
const sectionOrder = sections.map(s => s.dataset.section);
|
|
301
|
-
// Compare against contract's section order array
|
|
302
|
-
|
|
303
|
-
// 3. WIDGET DIMENSIONS — verify each widget's width relative to grid
|
|
304
|
-
const gridRect = grid.getBoundingClientRect();
|
|
305
|
-
const widgetRects = sections.map(s => s.getBoundingClientRect());
|
|
306
|
-
// For a 2-column grid: each widget should be ~50% of grid width (minus gap)
|
|
307
|
-
// For a 1-column widget spanning full width: should be ~100% of grid width
|
|
308
|
-
|
|
309
|
-
// 4. SPACING — verify gap, padding, margins match contract
|
|
310
|
-
// gap, rowGap, columnGap, padding
|
|
311
|
-
|
|
312
|
-
// 5. RESPONSIVE — if contract specifies breakpoints, measure at each viewport width
|
|
313
|
-
// Build measurements array: [{ property, expected, actual, pass }]
|
|
314
|
-
```
|
|
315
|
-
**Grid column count is a CRITICAL measurement. If the contract says 2 columns and the page renders 4, this is `severity: "critical"` and MUST be flagged.**
|
|
316
|
-
c. Write queue JSON to `.gsd-t/design-review/queue/{page-id}.json` (same format, with `"type": "page"`)
|
|
317
|
-
e. Check for auto-rejections (sleep 3, check `rejected/` dir) — fix and re-queue if rejected (max 2 cycles)
|
|
318
|
-
3. After ALL pages are built and queued, **STOP and poll for human review** — this is a BLOCKING wait, do NOT proceed until the human submits:
|
|
319
|
-
```
|
|
320
|
-
Waiting for human review of pages...
|
|
321
|
-
Review UI: http://localhost:3456/review
|
|
322
|
-
{N} pages queued, awaiting submission
|
|
323
|
-
```
|
|
324
|
-
```bash
|
|
325
|
-
while [ ! -f .gsd-t/design-review/review-complete.json ]; do
|
|
326
|
-
sleep 5
|
|
327
|
-
done
|
|
328
|
-
```
|
|
329
|
-
4. Process feedback — same rules as Step 4. Clear queue, delete signal, clear feedback. If any pages re-queued → return to sub-step 3 (max 3 review cycles).
|
|
330
|
-
|
|
331
|
-
## Step 7: Cleanup and Report
|
|
332
|
-
|
|
333
|
-
```bash
|
|
334
|
-
# Signal Term 2 to shut down
|
|
335
|
-
echo '{"shutdown": true}' > .gsd-t/design-review/shutdown.json
|
|
336
|
-
sleep 2
|
|
337
|
-
|
|
338
|
-
# Kill review server
|
|
339
|
-
kill $REVIEW_PID 2>/dev/null
|
|
340
|
-
|
|
341
|
-
# Kill dev server if we started it
|
|
342
|
-
# (only if we started it in Step 1a)
|
|
343
|
-
```
|
|
344
|
-
|
|
345
|
-
Report:
|
|
346
|
-
```
|
|
347
|
-
Design Build Complete
|
|
348
|
-
Elements: {N} built, {N} approved
|
|
349
|
-
Widgets: {N} built, {N} approved
|
|
350
|
-
Pages: {N} built, {N} approved
|
|
351
|
-
Review cycles: {N}
|
|
352
|
-
|
|
353
|
-
Changes applied from review: {N}
|
|
354
|
-
Comments addressed: {N}
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
Update `.gsd-t/progress.md` with completion status.
|
|
358
|
-
|
|
359
|
-
## Coordination Directory Structure
|
|
360
|
-
|
|
361
|
-
```
|
|
362
|
-
.gsd-t/design-review/
|
|
363
|
-
├── queue/ # Term 1 writes, Term 2 + human reads
|
|
364
|
-
│ ├── element-donut.json
|
|
365
|
-
│ └── element-bar.json
|
|
366
|
-
├── feedback/ # Human review writes, Term 1 reads
|
|
367
|
-
│ ├── element-donut.json
|
|
368
|
-
│ └── element-bar.json
|
|
369
|
-
├── rejected/ # Term 2 auto-rejects, Term 1 reads
|
|
370
|
-
│ └── element-bar.json
|
|
371
|
-
├── review-complete.json # Human submit signal → Term 1 polls
|
|
372
|
-
├── review-prompt.md # Term 2's instructions (no builder context)
|
|
373
|
-
├── shutdown.json # Term 1 signals Term 2 to exit
|
|
374
|
-
└── status.json # Review server state
|
|
375
|
-
```
|
|
39
|
+
- Design contracts must exist in `.gsd-t/contracts/design/` with an `INDEX.md`
|
|
40
|
+
- If no contracts exist, run `/user:gsd-t-design-decompose` first
|
|
376
41
|
|
|
377
|
-
##
|
|
42
|
+
## Why a JS Orchestrator?
|
|
378
43
|
|
|
379
|
-
|
|
380
|
-
- NEVER skip the review cycle — every component goes through Term 2 + human
|
|
381
|
-
- NEVER proceed to widgets before all elements are reviewed and approved by the human — the bash polling loop MUST block until `review-complete.json` appears
|
|
382
|
-
- NEVER proceed to pages before all widgets are reviewed and approved by the human — same blocking poll
|
|
383
|
-
- NEVER rebuild element functionality inline in widgets — always import
|
|
384
|
-
- NEVER skip or shortcut the bash polling loop — it MUST actually execute and block
|
|
385
|
-
- Max 3 review cycles per phase — if still failing, stop and present to user
|
|
386
|
-
- Auto-open the browser review UI so the user doesn't have to find it
|
|
387
|
-
- Kill all background processes on completion or error
|
|
44
|
+
Claude Code agents optimize for task completion and will skip any prompt instruction that asks them to pause indefinitely — including bash polling loops, `BLOCKING` headers, and `STOP` directives. Three separate attempts to enforce review gates via prompt instructions all failed. The JS orchestrator moves flow control out of prompts entirely into deterministic JavaScript.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tekyzinc/gsd-t",
|
|
3
|
-
"version": "2.71.
|
|
3
|
+
"version": "2.71.16",
|
|
4
4
|
"description": "GSD-T: Contract-Driven Development for Claude Code — 56 slash commands with headless CI/CD mode, graph-powered code analysis, real-time agent dashboard, execution intelligence, task telemetry, doc-ripple enforcement, backlog management, impact analysis, test sync, milestone archival, and PRD generation",
|
|
5
5
|
"author": "Tekyz, Inc.",
|
|
6
6
|
"license": "MIT",
|