vxkyyy commited on
Commit
1595c92
·
1 Parent(s): f41a080

feat: rewarding build UX + Docker fixes + web docs cleanup

Browse files

Docker:
- Dockerfile: add ENV PYTHONPATH=/app, mkdir designs/ & artifacts/,
make sby install resilient (pip first, then git clone fallback)
- docker-compose.yml: add ./designs:/app/designs volume, add healthcheck
pointing at GET /health

Web docs:
- Remove raw README from in-app docs index; replace with user-friendly
Getting Started (USER_GUIDE.md) and Cloud Deployment guide

Build UX (rewarding / less frustrating):
- StageProgressBar: each active/completed stage now shows a brief
encouraging description (e.g. 'Writing synthesizable Verilog')
- Milestone stages (RTL_GEN, VERIFICATION, HARDENING, SIGNOFF) get a
gold dot badge when completed
- HumanInLoopBuild: bottom status bar now shows stage-aware contextual
message instead of generic 'Building autonomously'
- HumanInLoopBuild: milestone celebration toast slides in when a key
stage is approved (auto-dismisses after 5s)
- hitl.css: stage description subtitle, milestone badge, toast-in
animation, indicator top-alignment fix

Dockerfile CHANGED
@@ -17,13 +17,16 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
17
  yosys \
18
  && rm -rf /var/lib/apt/lists/*
19
 
20
- # Install SymbiYosys from source
21
- RUN git clone --depth 1 https://github.com/YosysHQ/sby /tmp/sby \
22
- && cd /tmp/sby && make install \
23
- && rm -rf /tmp/sby
24
 
25
  WORKDIR /app
26
 
 
 
 
27
  # Install Python dependencies first (layer cache)
28
  COPY requirements.txt .
29
  RUN pip install --no-cache-dir --upgrade pip && \
@@ -33,6 +36,9 @@ RUN pip install --no-cache-dir --upgrade pip && \
33
  # Copy application code
34
  COPY . .
35
 
 
 
 
36
  # HuggingFace Spaces runs as non-root user 1000
37
  RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
38
  USER appuser
 
17
  yosys \
18
  && rm -rf /var/lib/apt/lists/*
19
 
20
+ # Install SymbiYosys (try pip first, fall back to source)
21
+ RUN pip install --no-cache-dir symbiyosys 2>/dev/null || \
22
+ (git clone --depth 1 https://github.com/YosysHQ/sby /tmp/sby \
23
+ && cd /tmp/sby && make install && rm -rf /tmp/sby) || true
24
 
25
  WORKDIR /app
26
 
27
+ # Ensure module resolution works from /app
28
+ ENV PYTHONPATH=/app
29
+
30
  # Install Python dependencies first (layer cache)
31
  COPY requirements.txt .
32
  RUN pip install --no-cache-dir --upgrade pip && \
 
36
  # Copy application code
37
  COPY . .
38
 
39
+ # Create runtime directories that the build pipeline writes into
40
+ RUN mkdir -p /app/designs /app/artifacts
41
+
42
  # HuggingFace Spaces runs as non-root user 1000
43
  RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
44
  USER appuser
docker-compose.yml CHANGED
@@ -12,4 +12,11 @@ services:
12
  volumes:
13
  - ./artifacts:/app/artifacts
14
  - ./training:/app/training
 
15
  restart: unless-stopped
 
 
 
 
 
 
 
12
  volumes:
13
  - ./artifacts:/app/artifacts
14
  - ./training:/app/training
15
+ - ./designs:/app/designs
16
  restart: unless-stopped
17
+ healthcheck:
18
+ test: ["CMD", "curl", "-sf", "http://localhost:7860/health"]
19
+ interval: 30s
20
+ timeout: 10s
21
+ retries: 3
22
+ start_period: 20s
server/api.py CHANGED
@@ -251,11 +251,11 @@ def _repo_root() -> str:
251
  def _docs_index() -> Dict[str, Dict[str, str]]:
252
  root = _repo_root()
253
  return {
254
- "readme": {
255
- "title": "README",
256
  "section": "Product",
257
- "path": os.path.join(root, "README.md"),
258
- "summary": "Full platform overview, flow, quality gates, and upgrade details.",
259
  },
260
  "web_guide": {
261
  "title": "Web App Guide",
@@ -269,11 +269,11 @@ def _docs_index() -> Dict[str, Dict[str, str]]:
269
  "path": os.path.join(root, "docs", "INSTALL.md"),
270
  "summary": "Installation and environment setup steps.",
271
  },
272
- "user_guide": {
273
- "title": "User Guide",
274
- "section": "Usage",
275
- "path": os.path.join(root, "docs", "USER_GUIDE.md"),
276
- "summary": "Operator guide for build flows and outputs.",
277
  },
278
  }
279
 
 
251
  def _docs_index() -> Dict[str, Dict[str, str]]:
252
  root = _repo_root()
253
  return {
254
+ "getting_started": {
255
+ "title": "Getting Started",
256
  "section": "Product",
257
+ "path": os.path.join(root, "docs", "USER_GUIDE.md"),
258
+ "summary": "Quick-start guide build your first chip in minutes.",
259
  },
260
  "web_guide": {
261
  "title": "Web App Guide",
 
269
  "path": os.path.join(root, "docs", "INSTALL.md"),
270
  "summary": "Installation and environment setup steps.",
271
  },
272
+ "cloud_deploy": {
273
+ "title": "Cloud Deployment",
274
+ "section": "Setup",
275
+ "path": os.path.join(root, "docs", "CLOUD_DEPLOY.md"),
276
+ "summary": "Deploy AgentIC on HuggingFace Spaces or any cloud.",
277
  },
278
  }
279
 
web/src/components/StageProgressBar.tsx CHANGED
@@ -17,6 +17,27 @@ const STAGES = [
17
  { key: 'SIGNOFF', label: 'Signoff' },
18
  ];
19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  interface Props {
21
  currentStage: string;
22
  completedStages: Set<string>;
@@ -42,6 +63,7 @@ export const StageProgressBar: React.FC<Props> = ({
42
  const isFailed = stage.key === failedStage;
43
  const isWaiting = isCurrent && waitingForApproval;
44
  const isSkipped = skippedStages?.has(stage.key) && !isCompleted && !isCurrent && !isFailed;
 
45
 
46
  let status = 'pending';
47
  if (isSkipped) status = 'skipped';
@@ -50,8 +72,13 @@ export const StageProgressBar: React.FC<Props> = ({
50
  if (isWaiting) status = 'waiting';
51
  if (isFailed) status = 'failed';
52
 
 
 
53
  return (
54
- <div key={stage.key} className={`hitl-sidebar-stage hitl-stage--${status}`}>
 
 
 
55
  <div className="hitl-sidebar-indicator">
56
  {isCompleted && (
57
  <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
@@ -75,7 +102,17 @@ export const StageProgressBar: React.FC<Props> = ({
75
  <span className="hitl-sidebar-dot-empty" />
76
  )}
77
  </div>
78
- <span className="hitl-sidebar-stage-name">{stage.label}</span>
 
 
 
 
 
 
 
 
 
 
79
  </div>
80
  );
81
  })}
 
17
  { key: 'SIGNOFF', label: 'Signoff' },
18
  ];
19
 
20
+ // Brief, encouraging descriptions shown when a stage is active or just completed
21
+ const STAGE_DESCRIPTIONS: Record<string, string> = {
22
+ INIT: 'Setting up build context',
23
+ SPEC: 'Translating your idea into chip spec',
24
+ RTL_GEN: 'Writing synthesizable Verilog',
25
+ RTL_FIX: 'Resolving any RTL issues',
26
+ VERIFICATION: 'Running simulation testbench',
27
+ FORMAL_VERIFY: 'Proving correctness mathematically',
28
+ COVERAGE_CHECK: 'Checking test coverage',
29
+ REGRESSION: 'Running regression suite',
30
+ SDC_GEN: 'Generating timing constraints',
31
+ FLOORPLAN: 'Laying out chip floorplan',
32
+ HARDENING: 'Physical design & routing',
33
+ CONVERGENCE_REVIEW:'Checking timing convergence',
34
+ ECO_PATCH: 'Patching for final sign-off',
35
+ SIGNOFF: 'Final LVS/DRC checks',
36
+ };
37
+
38
+ // Key milestones displayed with a special accent in the sidebar
39
+ const MILESTONES = new Set(['RTL_GEN', 'VERIFICATION', 'HARDENING', 'SIGNOFF']);
40
+
41
  interface Props {
42
  currentStage: string;
43
  completedStages: Set<string>;
 
63
  const isFailed = stage.key === failedStage;
64
  const isWaiting = isCurrent && waitingForApproval;
65
  const isSkipped = skippedStages?.has(stage.key) && !isCompleted && !isCurrent && !isFailed;
66
+ const isMilestone = MILESTONES.has(stage.key);
67
 
68
  let status = 'pending';
69
  if (isSkipped) status = 'skipped';
 
72
  if (isWaiting) status = 'waiting';
73
  if (isFailed) status = 'failed';
74
 
75
+ const showDesc = (isCurrent || isCompleted) && !isSkipped && !isFailed;
76
+
77
  return (
78
+ <div
79
+ key={stage.key}
80
+ className={`hitl-sidebar-stage hitl-stage--${status}${isMilestone ? ' hitl-stage--milestone' : ''}`}
81
+ >
82
  <div className="hitl-sidebar-indicator">
83
  {isCompleted && (
84
  <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
 
102
  <span className="hitl-sidebar-dot-empty" />
103
  )}
104
  </div>
105
+ <div className="hitl-sidebar-stage-info">
106
+ <span className="hitl-sidebar-stage-name">{stage.label}</span>
107
+ {showDesc && (
108
+ <span className="hitl-sidebar-stage-desc">
109
+ {STAGE_DESCRIPTIONS[stage.key]}
110
+ </span>
111
+ )}
112
+ </div>
113
+ {isMilestone && isCompleted && (
114
+ <span className="hitl-sidebar-milestone-dot" title="Milestone reached" />
115
+ )}
116
  </div>
117
  );
118
  })}
web/src/hitl.css CHANGED
@@ -377,7 +377,7 @@
377
 
378
  .hitl-sidebar-stage {
379
  display: flex;
380
- align-items: center;
381
  gap: 0.625rem;
382
  padding: 0.4375rem 1.25rem;
383
  border-left: 2px solid transparent;
@@ -391,6 +391,7 @@
391
  align-items: center;
392
  justify-content: center;
393
  flex-shrink: 0;
 
394
  }
395
 
396
  .hitl-sidebar-dot-empty {
@@ -415,6 +416,45 @@
415
  transition: color 150ms;
416
  }
417
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
418
  /* Stage status modifiers */
419
  .hitl-stage--completed .hitl-sidebar-stage-name {
420
  color: var(--text-secondary);
@@ -438,6 +478,70 @@
438
  font-weight: 500;
439
  }
440
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
441
  .hitl-stage--failed {
442
  border-left-color: var(--red);
443
  }
 
377
 
378
  .hitl-sidebar-stage {
379
  display: flex;
380
+ align-items: flex-start;
381
  gap: 0.625rem;
382
  padding: 0.4375rem 1.25rem;
383
  border-left: 2px solid transparent;
 
391
  align-items: center;
392
  justify-content: center;
393
  flex-shrink: 0;
394
+ margin-top: 0.125rem;
395
  }
396
 
397
  .hitl-sidebar-dot-empty {
 
416
  transition: color 150ms;
417
  }
418
 
419
+ /* Stage info wrapper (name + description) */
420
+ .hitl-sidebar-stage-info {
421
+ display: flex;
422
+ flex-direction: column;
423
+ gap: 0.1rem;
424
+ min-width: 0;
425
+ flex: 1;
426
+ }
427
+
428
+ .hitl-sidebar-stage-desc {
429
+ font-size: 0.6875rem;
430
+ color: var(--text-tertiary);
431
+ opacity: 0.7;
432
+ white-space: nowrap;
433
+ overflow: hidden;
434
+ text-overflow: ellipsis;
435
+ line-height: 1.3;
436
+ }
437
+
438
+ /* Milestone dot badge (gold star after milestone stages complete) */
439
+ .hitl-sidebar-milestone-dot {
440
+ width: 6px;
441
+ height: 6px;
442
+ border-radius: 50%;
443
+ background: #f6ad55;
444
+ flex-shrink: 0;
445
+ box-shadow: 0 0 4px rgba(246,173,85,0.6);
446
+ }
447
+
448
+ /* Milestone stage: subtly bolder left bar on active */
449
+ .hitl-stage--milestone.hitl-stage--active {
450
+ border-left-color: #f6ad55;
451
+ }
452
+
453
+ /* Stage completion flash for milestone stages */
454
+ .hitl-stage--milestone.hitl-stage--completed .hitl-sidebar-stage-name {
455
+ color: var(--text-secondary);
456
+ }
457
+
458
  /* Stage status modifiers */
459
  .hitl-stage--completed .hitl-sidebar-stage-name {
460
  color: var(--text-secondary);
 
478
  font-weight: 500;
479
  }
480
 
481
+ /* Milestone toast ─────────────────────────────────────────────────── */
482
+ .hitl-milestone-toast {
483
+ position: absolute;
484
+ top: 0.875rem;
485
+ right: 1rem;
486
+ z-index: 50;
487
+ display: flex;
488
+ align-items: flex-start;
489
+ gap: 0.625rem;
490
+ background: var(--bg-card, #1a1917);
491
+ border: 1px solid rgba(246,173,85,0.35);
492
+ border-left: 3px solid #f6ad55;
493
+ border-radius: 8px;
494
+ padding: 0.625rem 0.875rem;
495
+ min-width: 240px;
496
+ max-width: 320px;
497
+ box-shadow: 0 8px 24px rgba(0,0,0,0.4);
498
+ cursor: pointer;
499
+ animation: hitl-toast-in 280ms cubic-bezier(0.22,1,0.36,1) both;
500
+ }
501
+
502
+ @keyframes hitl-toast-in {
503
+ from { opacity: 0; transform: translateX(16px); }
504
+ to { opacity: 1; transform: translateX(0); }
505
+ }
506
+
507
+ .hitl-milestone-toast-icon {
508
+ font-size: 0.875rem;
509
+ color: #f6ad55;
510
+ flex-shrink: 0;
511
+ padding-top: 0.06rem;
512
+ }
513
+
514
+ .hitl-milestone-toast-body {
515
+ display: flex;
516
+ flex-direction: column;
517
+ gap: 0.1875rem;
518
+ flex: 1;
519
+ min-width: 0;
520
+ }
521
+
522
+ .hitl-milestone-toast-title {
523
+ font-size: 0.8125rem;
524
+ font-weight: 600;
525
+ color: var(--text-primary, #e8e5e0);
526
+ }
527
+
528
+ .hitl-milestone-toast-msg {
529
+ font-size: 0.75rem;
530
+ color: var(--text-secondary, #9a9590);
531
+ line-height: 1.45;
532
+ }
533
+
534
+ .hitl-milestone-toast-close {
535
+ background: none;
536
+ border: none;
537
+ font-size: 0.9rem;
538
+ color: var(--text-tertiary);
539
+ cursor: pointer;
540
+ padding: 0;
541
+ flex-shrink: 0;
542
+ line-height: 1;
543
+ }
544
+
545
  .hitl-stage--failed {
546
  border-left-color: var(--red);
547
  }
web/src/pages/HumanInLoopBuild.tsx CHANGED
@@ -13,6 +13,32 @@ const PIPELINE_STAGES = [
13
  ];
14
  const TOTAL = PIPELINE_STAGES.length;
15
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  // Human-readable stage names
17
  const STAGE_LABELS: Record<string, string> = {
18
  INIT: 'Initialization', SPEC: 'Specification', RTL_GEN: 'RTL Generation',
@@ -121,6 +147,10 @@ export const HumanInLoopBuild = () => {
121
  // Thinking indicator (Improvement 1)
122
  const [thinkingData, setThinkingData] = useState<{ agent_name: string; message: string } | null>(null);
123
 
 
 
 
 
124
  useEffect(() => {
125
  if (prompt.length > 8) {
126
  setDesignName(slugify(prompt));
@@ -297,6 +327,13 @@ export const HumanInLoopBuild = () => {
297
  next.add(approvalData.stage_name);
298
  return next;
299
  });
 
 
 
 
 
 
 
300
  } catch (e: any) {
301
  setError(e?.response?.data?.detail || 'Failed to approve');
302
  }
@@ -571,6 +608,18 @@ export const HumanInLoopBuild = () => {
571
  </div>
572
  </header>
573
 
 
 
 
 
 
 
 
 
 
 
 
 
574
  {/* Body: sidebar + main */}
575
  <div className="hitl-build-body">
576
  <StageProgressBar
@@ -598,8 +647,13 @@ export const HumanInLoopBuild = () => {
598
  <footer className="hitl-bottombar">
599
  <span className="hitl-bottombar-msg">
600
  {thinkingData && <span className="hitl-thinking-pulse" />}
601
- {waitingForApproval ? 'Awaiting approval' : thinkingData ? thinkingData.message : 'Building autonomously'} · Step {stepNum} of {TOTAL}
602
- {estMinutes > 0 ? ` · ~${estMinutes} min remaining` : ''}
 
 
 
 
 
603
  </span>
604
  <div className="hitl-bottombar-progress">
605
  <div className="hitl-bottombar-track">
 
13
  ];
14
  const TOTAL = PIPELINE_STAGES.length;
15
 
16
+ // Contextual messages for the bottom status bar — what the agent is doing right now
17
+ const STAGE_ENCOURAGEMENTS: Record<string, string> = {
18
+ INIT: 'Setting up your build environment…',
19
+ SPEC: 'Translating your description into a chip specification…',
20
+ RTL_GEN: 'Writing Verilog — your chip is taking shape…',
21
+ RTL_FIX: 'Fixing any RTL issues automatically…',
22
+ VERIFICATION: 'Running simulation — making sure your logic is correct…',
23
+ FORMAL_VERIFY: 'Proving your chip is correct with formal methods…',
24
+ COVERAGE_CHECK: 'Measuring how thoroughly the tests cover your design…',
25
+ REGRESSION: 'Running the full regression suite…',
26
+ SDC_GEN: 'Generating timing constraints for physical design…',
27
+ FLOORPLAN: 'Planning the physical layout of your chip…',
28
+ HARDENING: 'Running place-and-route — turning RTL into real silicon…',
29
+ CONVERGENCE_REVIEW:'Checking that timing is met across all corners…',
30
+ ECO_PATCH: 'Applying final tweaks for clean sign-off…',
31
+ SIGNOFF: 'Almost there — running final LVS/DRC checks…',
32
+ };
33
+
34
+ // Milestone stages that deserve a special celebration toast
35
+ const MILESTONE_TOASTS: Record<string, { title: string; msg: string }> = {
36
+ RTL_GEN: { title: 'RTL Complete', msg: 'Your chip can now run instructions. Verilog is ready.' },
37
+ VERIFICATION: { title: 'Verification Passed', msg: 'All simulation tests passed. Design is logically correct.' },
38
+ HARDENING: { title: 'Silicon Layout Done', msg: 'Place-and-route complete. Your chip has a physical form.' },
39
+ SIGNOFF: { title: 'Chip Signed Off', msg: 'All checks passed. Ready for tape-out.' },
40
+ };
41
+
42
  // Human-readable stage names
43
  const STAGE_LABELS: Record<string, string> = {
44
  INIT: 'Initialization', SPEC: 'Specification', RTL_GEN: 'RTL Generation',
 
147
  // Thinking indicator (Improvement 1)
148
  const [thinkingData, setThinkingData] = useState<{ agent_name: string; message: string } | null>(null);
149
 
150
+ // Milestone toast: shown briefly when a key stage completes
151
+ const [milestoneToast, setMilestoneToast] = useState<{ title: string; msg: string } | null>(null);
152
+ const milestoneTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
153
+
154
  useEffect(() => {
155
  if (prompt.length > 8) {
156
  setDesignName(slugify(prompt));
 
327
  next.add(approvalData.stage_name);
328
  return next;
329
  });
330
+ // Fire milestone toast if this is a key stage
331
+ const toast = MILESTONE_TOASTS[approvalData.stage_name];
332
+ if (toast) {
333
+ if (milestoneTimerRef.current) clearTimeout(milestoneTimerRef.current);
334
+ setMilestoneToast(toast);
335
+ milestoneTimerRef.current = setTimeout(() => setMilestoneToast(null), 5000);
336
+ }
337
  } catch (e: any) {
338
  setError(e?.response?.data?.detail || 'Failed to approve');
339
  }
 
608
  </div>
609
  </header>
610
 
611
+ {/* Milestone celebration toast */}
612
+ {milestoneToast && (
613
+ <div className="hitl-milestone-toast" onClick={() => setMilestoneToast(null)}>
614
+ <span className="hitl-milestone-toast-icon">✦</span>
615
+ <div className="hitl-milestone-toast-body">
616
+ <span className="hitl-milestone-toast-title">{milestoneToast.title}</span>
617
+ <span className="hitl-milestone-toast-msg">{milestoneToast.msg}</span>
618
+ </div>
619
+ <button className="hitl-milestone-toast-close">×</button>
620
+ </div>
621
+ )}
622
+
623
  {/* Body: sidebar + main */}
624
  <div className="hitl-build-body">
625
  <StageProgressBar
 
647
  <footer className="hitl-bottombar">
648
  <span className="hitl-bottombar-msg">
649
  {thinkingData && <span className="hitl-thinking-pulse" />}
650
+ {waitingForApproval
651
+ ? 'Your review is needed inspect the stage output above'
652
+ : thinkingData
653
+ ? thinkingData.message
654
+ : (STAGE_ENCOURAGEMENTS[currentStage] || 'Building autonomously…')}
655
+ {' · '}{pct}% complete
656
+ {estMinutes > 0 ? ` · ~${estMinutes} min left` : ''}
657
  </span>
658
  <div className="hitl-bottombar-progress">
659
  <div className="hitl-bottombar-track">