@yemi33/minions 0.1.2448 → 0.1.2449

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (315) hide show
  1. package/bin/cli-api-client.js +1 -1
  2. package/bin/install-internal-minions.js +1382 -44
  3. package/bin/install-layout.js +150 -0
  4. package/bin/minions.js +460 -167
  5. package/dashboard/docs/typography.md +65 -12
  6. package/dashboard/js/command-center.js +58 -4
  7. package/dashboard/js/detail-panel.js +36 -0
  8. package/dashboard/js/memory-panel.js +59 -12
  9. package/dashboard/js/qa.js +179 -20
  10. package/dashboard/js/refresh.js +148 -12
  11. package/dashboard/js/render-dispatch.js +3 -4
  12. package/dashboard/js/render-inbox.js +2 -2
  13. package/dashboard/js/render-other.js +3 -3
  14. package/dashboard/js/render-pipelines.js +14 -0
  15. package/dashboard/js/render-plans.js +57 -9
  16. package/dashboard/js/render-prd.js +132 -23
  17. package/dashboard/js/render-prs.js +195 -166
  18. package/dashboard/js/render-schedules.js +63 -3
  19. package/dashboard/js/render-utils.js +3 -3
  20. package/dashboard/js/render-watches.js +19 -3
  21. package/dashboard/js/render-work-items.js +238 -30
  22. package/dashboard/js/settings.js +205 -54
  23. package/dashboard/js/utils.js +51 -1
  24. package/dashboard/pages/home.html +1 -1
  25. package/dashboard/pages/qa.html +1 -16
  26. package/dashboard/pages/work.html +40 -0
  27. package/dashboard/shared/cc-limits.js +79 -0
  28. package/dashboard/shared/pr-filters.js +21 -38
  29. package/dashboard/shared/project-git-summary.js +1 -1
  30. package/dashboard/shared/record-filters.js +169 -0
  31. package/dashboard/shared/watches-source.js +1 -1
  32. package/dashboard/shared/welcome-popup.js +1 -1
  33. package/dashboard/shared/wi-filters.js +302 -0
  34. package/dashboard/slim/body.html +1 -0
  35. package/dashboard/slim/js/command-send.js +26 -0
  36. package/dashboard/slim/js/modals-tiles.js +380 -39
  37. package/dashboard/slim/js/status.js +13 -21
  38. package/dashboard/slim/layout.html +1 -0
  39. package/dashboard/slim/panel-bootstrap.js +6 -2
  40. package/dashboard/slim/styles.css +38 -0
  41. package/dashboard/styles.css +159 -55
  42. package/dashboard-build.js +13 -2
  43. package/dashboard.js +956 -423
  44. package/docs/README.md +11 -6
  45. package/docs/api-errors.md +2 -2
  46. package/docs/architecture-review-2026-07-09.md +1 -1
  47. package/docs/architecture.excalidraw +2 -2
  48. package/docs/auto-discovery.md +18 -9
  49. package/docs/branch-derivation.md +4 -4
  50. package/docs/capture-demos.js +39 -2
  51. package/docs/ci-runner-canary.md +123 -0
  52. package/docs/claude-md-propagation.md +2 -2
  53. package/docs/cloud-agent-dispatch.md +204 -0
  54. package/docs/command-center.md +7 -7
  55. package/docs/completion-reports.md +43 -20
  56. package/docs/constants.md +10 -3
  57. package/docs/constellation-bridge.md +134 -6
  58. package/docs/constellation-style-telemetry.md +4 -4
  59. package/docs/contracts/capability-protocol.v1.json +165 -0
  60. package/docs/cooldown-merge-semantics.md +12 -12
  61. package/docs/copilot-cli-schema.md +7 -7
  62. package/docs/cross-repo-plans.md +10 -10
  63. package/docs/dead-code-audit-retractions.md +5 -5
  64. package/docs/default-branch-ci.md +173 -0
  65. package/docs/deprecated.json +31 -31
  66. package/docs/design-inbox-entries-schema.md +3 -3
  67. package/docs/design-language.md +1051 -0
  68. package/docs/design-state-storage.md +11 -11
  69. package/docs/diagnostics-crash-reports.md +9 -9
  70. package/docs/diagnostics-memory.md +5 -5
  71. package/docs/documentation-audit-2026-07-09.md +7 -7
  72. package/docs/engine-restart.md +90 -5
  73. package/docs/harness-mode.md +1 -1
  74. package/docs/internal-install.md +338 -39
  75. package/docs/kb-dedup-duplicate-pair-investigation.md +5 -5
  76. package/docs/kb-pr3223-cascade-archiving.md +1 -1
  77. package/docs/kb-pr696-merge-conflict-docs.md +6 -6
  78. package/docs/kb-sweep.md +35 -35
  79. package/docs/keep-processes.md +1 -1
  80. package/docs/live-checkout-mode.md +30 -30
  81. package/docs/managed-spawn.md +18 -14
  82. package/docs/named-agents.md +7 -7
  83. package/docs/plan-lifecycle.md +69 -2
  84. package/docs/pr-author-identity.md +114 -0
  85. package/docs/pr-auto-fix-dispatch.md +19 -4
  86. package/docs/pr-comment-followup.md +6 -6
  87. package/docs/pr-review-fix-loop.md +59 -10
  88. package/docs/process-termination.md +40 -0
  89. package/docs/proposals/repo-pool-for-live-checkout.md +13 -13
  90. package/docs/qa-runbook-lifecycle.md +367 -17
  91. package/docs/qa-runbooks.md +3 -3
  92. package/docs/rfc-completion-json.md +18 -18
  93. package/docs/runtime-adapters.md +26 -21
  94. package/docs/security.md +6 -6
  95. package/docs/self-improvement.md +4 -4
  96. package/docs/shared-lifecycle-module-map.md +473 -472
  97. package/docs/skills.md +52 -3
  98. package/docs/slim-ux/concepts.md +121 -116
  99. package/docs/specs/agent-configurability.md +18 -18
  100. package/docs/specs/agent-rename.md +18 -18
  101. package/docs/team-memory.md +38 -21
  102. package/docs/timeouts-and-liveness.md +118 -10
  103. package/docs/tutorials/01-install-and-connect.md +1 -1
  104. package/docs/watches.md +40 -39
  105. package/docs/workspace-manifests.md +4 -4
  106. package/docs/worktree-lifecycle.md +293 -14
  107. package/engine/README.md +46 -0
  108. package/engine/{ado-comment.js → ado/comment.js} +8 -8
  109. package/engine/{ado-git-auth.js → ado/git-auth.js} +4 -4
  110. package/engine/{ado.js → ado/index.js} +417 -63
  111. package/engine/{ado-status.js → ado/status.js} +6 -8
  112. package/engine/{ado-token.js → ado/token.js} +1 -1
  113. package/engine/{acp-transport.js → agents/acp-transport.js} +62 -22
  114. package/engine/{agent-worker-pool.js → agents/agent-worker-pool.js} +17 -8
  115. package/engine/{cc-worker-pool.js → agents/cc-worker-pool.js} +16 -6
  116. package/engine/{claude-md-context.js → agents/claude-md-context.js} +5 -5
  117. package/engine/{harness-context.js → agents/harness-context.js} +5 -5
  118. package/engine/{harness.js → agents/harness.js} +3 -3
  119. package/engine/{llm.js → agents/llm.js} +18 -14
  120. package/engine/{model-discovery.js → agents/model-discovery.js} +2 -2
  121. package/engine/{playbook.js → agents/playbook.js} +155 -22
  122. package/engine/{pooled-agent-process.js → agents/pooled-agent-process.js} +14 -12
  123. package/engine/{preflight.js → agents/preflight.js} +29 -10
  124. package/engine/{spawn-agent.js → agents/spawn-agent.js} +25 -14
  125. package/engine/{spawn-phase-watchdog.js → agents/spawn-phase-watchdog.js} +16 -7
  126. package/engine/{steering.js → agents/steering.js} +5 -5
  127. package/engine/{tools-inventory.js → agents/tools-inventory.js} +2 -2
  128. package/engine/{agent-api-validation.js → api/agent-api-validation.js} +2 -2
  129. package/engine/{api-validation.js → api/api-validation.js} +1 -1
  130. package/engine/api/bridge.js +787 -0
  131. package/engine/{cc-api-validation.js → api/cc-api-validation.js} +1 -1
  132. package/engine/api/companion.js +560 -0
  133. package/engine/{content-api-validation.js → api/content-api-validation.js} +2 -2
  134. package/engine/{pr-issue-validation.js → api/pr-issue-validation.js} +33 -6
  135. package/engine/{settings-validation.js → api/settings-validation.js} +32 -4
  136. package/engine/api-contracts/agent-content.js +4 -4
  137. package/engine/api-contracts/capability-manifest.js +236 -0
  138. package/engine/api-contracts/capability-protocol.js +333 -0
  139. package/engine/api-contracts/cc-ops.js +1 -1
  140. package/engine/api-contracts/config-runtime.js +5 -0
  141. package/engine/api-contracts/core.js +28 -1
  142. package/engine/api-contracts/index.js +100 -0
  143. package/engine/api-contracts/orchestration.js +18 -5
  144. package/engine/api-contracts/pull-requests.js +37 -6
  145. package/engine/api-contracts/qa-process.js +29 -6
  146. package/engine/api-contracts/work-plan-prd.js +21 -1
  147. package/engine/cloud/contract.js +212 -0
  148. package/engine/cloud/index.js +159 -0
  149. package/engine/{execution-model.js → core/execution-model.js} +1 -1
  150. package/engine/{features.js → core/features.js} +4 -4
  151. package/engine/{operator-identity.js → core/operator-identity.js} +1 -1
  152. package/engine/{queries.js → core/queries.js} +201 -36
  153. package/engine/{safe-expr.js → core/safe-expr.js} +1 -1
  154. package/engine/{shared.js → core/shared.js} +1637 -175
  155. package/engine/{stdio-timestamps.js → core/stdio-timestamps.js} +1 -1
  156. package/engine/{untrusted-fence.js → core/untrusted-fence.js} +3 -3
  157. package/engine/db/index.js +11 -2
  158. package/engine/db/migrations/002-dispatches.js +3 -3
  159. package/engine/db/migrations/003-work-items.js +1 -1
  160. package/engine/db/migrations/004-pull-requests.js +1 -1
  161. package/engine/db/migrations/006-metrics.js +1 -1
  162. package/engine/db/migrations/007-watches.js +2 -2
  163. package/engine/db/migrations/008-small-state.js +1 -1
  164. package/engine/db/migrations/009-qa.js +1 -1
  165. package/engine/db/migrations/010-pr-links.js +1 -1
  166. package/engine/db/migrations/011-remaining-state.js +1 -1
  167. package/engine/db/migrations/012-steering-deliveries.js +2 -2
  168. package/engine/db/migrations/013-backfill-broken-note-links.js +1 -1
  169. package/engine/db/migrations/014-pr-fix-target-prefs.js +2 -2
  170. package/engine/db/migrations/015-plans-prds.js +0 -0
  171. package/engine/db/migrations/018-sql-only-cutover.js +2 -2
  172. package/engine/db/migrations/021-archived-work-items.js +1 -1
  173. package/engine/db/migrations/022-global-cc-session.js +1 -1
  174. package/engine/db/migrations/023-engine-state.js +1 -1
  175. package/engine/db/migrations/025-malformed-work-item-phantoms.js +1 -1
  176. package/engine/db/migrations/027-review-learning-lifecycle.js +1 -1
  177. package/engine/db/migrations/029-repair-reused-versions.js +20 -0
  178. package/engine/db/migrations/031-pr-author-identity.js +137 -0
  179. package/engine/{consolidation.js → memory/consolidation.js} +6 -6
  180. package/engine/{kb-sweep-runner.js → memory/kb-sweep-runner.js} +2 -2
  181. package/engine/{kb-sweep.js → memory/kb-sweep.js} +9 -7
  182. package/engine/{memory-retrieval.js → memory/memory-retrieval.js} +46 -4
  183. package/engine/{memory-store.js → memory/memory-store.js} +3 -3
  184. package/engine/{promotion.js → memory/promotion.js} +3 -3
  185. package/engine/{review-learning-backfill.js → memory/review-learning-backfill.js} +6 -6
  186. package/engine/{review-learning.js → memory/review-learning.js} +10 -5
  187. package/engine/{diagnostics-memory.js → observability/diagnostics-memory.js} +1 -1
  188. package/engine/{logs-store.js → observability/logs-store.js} +5 -5
  189. package/engine/{metrics-store.js → observability/metrics-store.js} +4 -4
  190. package/engine/{check-status.js → operations/check-status.js} +3 -3
  191. package/engine/{cli.js → operations/cli.js} +271 -113
  192. package/engine/{distribution.js → operations/distribution.js} +5 -6
  193. package/engine/{cleanup.js → orchestration/cleanup.js} +72 -45
  194. package/engine/{cooldown.js → orchestration/cooldown.js} +5 -5
  195. package/engine/{dispatch-events.js → orchestration/dispatch-events.js} +2 -2
  196. package/engine/{dispatch.js → orchestration/dispatch.js} +129 -36
  197. package/engine/orchestration/failed-scheduled-cleanup.js +274 -0
  198. package/engine/{lifecycle.js → orchestration/lifecycle.js} +198 -90
  199. package/engine/{meeting.js → orchestration/meeting.js} +6 -16
  200. package/engine/{pipeline.js → orchestration/pipeline.js} +12 -12
  201. package/engine/{pre-dispatch-eval.js → orchestration/pre-dispatch-eval.js} +10 -9
  202. package/engine/{routing.js → orchestration/routing.js} +3 -3
  203. package/engine/{schedule-bootstrap.js → orchestration/schedule-bootstrap.js} +4 -4
  204. package/engine/{scheduler.js → orchestration/scheduler.js} +38 -8
  205. package/engine/{timeout.js → orchestration/timeout.js} +158 -109
  206. package/engine/{db-events.js → persistence/db-events.js} +2 -2
  207. package/engine/{dispatch-store.js → persistence/dispatch-store.js} +7 -7
  208. package/engine/{inbox-store.js → persistence/inbox-store.js} +2 -2
  209. package/engine/{note-link-backfill.js → persistence/note-link-backfill.js} +4 -4
  210. package/engine/{pr-fix-target-store.js → persistence/pr-fix-target-store.js} +8 -8
  211. package/engine/{pull-requests-store.js → persistence/pull-requests-store.js} +21 -7
  212. package/engine/{small-state-store.js → persistence/small-state-store.js} +31 -31
  213. package/engine/persistence/state-operations.js +350 -0
  214. package/engine/{steering-store.js → persistence/steering-store.js} +6 -6
  215. package/engine/{issues.js → planning/issues.js} +2 -2
  216. package/engine/{plan-prd-validation.js → planning/plan-prd-validation.js} +8 -2
  217. package/engine/planning/prd-result-sidecar.js +190 -0
  218. package/engine/{prd-store.js → planning/prd-store.js} +17 -17
  219. package/engine/{project-discovery.js → planning/project-discovery.js} +5 -5
  220. package/engine/{projects.js → planning/projects.js} +10 -10
  221. package/engine/{resolve-area.js → planning/resolve-area.js} +1 -1
  222. package/engine/{work-item-validation.js → planning/work-item-validation.js} +39 -3
  223. package/engine/{work-items-store.js → planning/work-items-store.js} +29 -21
  224. package/engine/{keep-process-sweep.js → processes/keep-process-sweep.js} +57 -17
  225. package/engine/{managed-spawn-launcher.js → processes/managed-spawn-launcher.js} +3 -3
  226. package/engine/{managed-spawn.js → processes/managed-spawn.js} +97 -46
  227. package/engine/{process-utils.js → processes/process-utils.js} +599 -55
  228. package/engine/{abandoned-pr-reconciliation.js → providers/abandoned-pr-reconciliation.js} +17 -7
  229. package/engine/{comment-classifier.js → providers/comment-classifier.js} +85 -17
  230. package/engine/{comment-format.js → providers/comment-format.js} +5 -5
  231. package/engine/{gh-comment.js → providers/gh-comment.js} +15 -15
  232. package/engine/{gh-token.js → providers/gh-token.js} +4 -4
  233. package/engine/{github.js → providers/github.js} +131 -54
  234. package/engine/{pr-action.js → providers/pr-action.js} +13 -12
  235. package/engine/{pr-clone-keep.js → providers/pr-clone-keep.js} +7 -7
  236. package/engine/{pr-devbox.js → providers/pr-devbox.js} +6 -6
  237. package/engine/{pr-fix-target.js → providers/pr-fix-target.js} +13 -13
  238. package/engine/{pr-remote-patch.js → providers/pr-remote-patch.js} +4 -4
  239. package/engine/{pr-resolve.js → providers/pr-resolve.js} +7 -7
  240. package/engine/{pr-temp-clone.js → providers/pr-temp-clone.js} +5 -5
  241. package/engine/{pr-track.js → providers/pr-track.js} +11 -13
  242. package/engine/{shared-branch-pr-reconcile.js → providers/shared-branch-pr-reconcile.js} +4 -4
  243. package/engine/qa/auto-prd-qa.js +313 -0
  244. package/engine/{qa-from-prd.js → qa/from-prd.js} +42 -12
  245. package/engine/qa/prd-session.js +240 -0
  246. package/engine/{qa-process-validation.js → qa/process-validation.js} +14 -9
  247. package/engine/{qa-runbooks.js → qa/runbooks.js} +1 -1
  248. package/engine/{qa-runs.js → qa/runs.js} +286 -15
  249. package/engine/{qa-sessions.js → qa/sessions.js} +595 -49
  250. package/engine/qa/visual-journey.js +654 -0
  251. package/engine/{qa-runners.js → qa-runners/index.js} +7 -7
  252. package/engine/qa-runners/maestro.js +3 -3
  253. package/engine/qa-runners/playwright.js +2 -2
  254. package/engine/{restart-health.js → recovery/restart-health.js} +48 -4
  255. package/engine/recovery/stop-stack.js +607 -0
  256. package/engine/{supervisor.js → recovery/supervisor.js} +105 -175
  257. package/engine/{watchdog.js → recovery/watchdog.js} +136 -13
  258. package/engine/runtimes/claude.js +14 -12
  259. package/engine/runtimes/codex.js +8 -6
  260. package/engine/runtimes/copilot.js +17 -16
  261. package/engine/{watch-actions.js → watches/actions.js} +13 -13
  262. package/engine/{watches.js → watches/index.js} +43 -32
  263. package/engine/{watches-store.js → watches/store.js} +4 -4
  264. package/engine/{create-pr-worktree.js → worktrees/create-pr.js} +1 -1
  265. package/engine/{worktree-gc.js → worktrees/gc.js} +70 -22
  266. package/engine/worktrees/inventory.js +671 -0
  267. package/engine/{live-checkout.js → worktrees/live-checkout.js} +4 -4
  268. package/engine/{worktree-pool.js → worktrees/pool.js} +2 -2
  269. package/engine/{worktree-preflight.js → worktrees/preflight.js} +1 -0
  270. package/engine/worktrees/quarantine-refs.js +173 -0
  271. package/engine.js +1137 -208
  272. package/minions.js +147 -77
  273. package/package.json +10 -6
  274. package/playbooks/_pr-description-audit.md +110 -78
  275. package/playbooks/build-fix-complex.md +2 -0
  276. package/playbooks/fix.md +16 -12
  277. package/playbooks/implement-shared.md +2 -0
  278. package/playbooks/implement.md +19 -20
  279. package/playbooks/plan-to-prd.md +18 -3
  280. package/playbooks/qa-session-draft.md +136 -1
  281. package/playbooks/qa-session-execute.md +80 -2
  282. package/playbooks/qa-session-setup.md +17 -1
  283. package/playbooks/qa-validate.md +1 -1
  284. package/playbooks/setup.md +2 -0
  285. package/playbooks/shared-rules.md +25 -32
  286. package/playbooks/templates/followup-dispatch.md +4 -3
  287. package/playbooks/verify.md +1 -1
  288. package/prompts/cc-system.md +19 -27
  289. package/watch-plugins/README.md +92 -0
  290. package/watch-plugins/ado-author-prs.js +336 -0
  291. package/watch-plugins/gh-author-prs.js +375 -0
  292. package/watch-plugins/http.js +474 -0
  293. package/watch-plugins/teams-channel.js +869 -0
  294. package/docs/dev-composite-workflow.md +0 -101
  295. package/docs/pr-screenshots/pr-886/after-single-header.png +0 -0
  296. package/docs/pr-screenshots/pr-886/before-duplicate-header.png +0 -0
  297. package/docs/pr-screenshots/pr-895/01-cancellation-reason-detail.png +0 -0
  298. package/docs/pr-screenshots/pr-899/worker-pool-worktrees-AFTER.png +0 -0
  299. package/docs/pr-screenshots/pr-899/worker-pool-worktrees-BEFORE.png +0 -0
  300. package/docs/pr-screenshots/pr-901/projects-tab-default.png +0 -0
  301. package/docs/pr-screenshots/pr-901/projects-tab-fmf-selected.png +0 -0
  302. package/docs/pr-screenshots/pr-916/model-picker-AFTER-crop.png +0 -0
  303. package/docs/pr-screenshots/pr-916/model-picker-AFTER.png +0 -0
  304. package/docs/pr-screenshots/pr-916/model-picker-BEFORE-crop.png +0 -0
  305. package/docs/pr-screenshots/pr-916/model-picker-BEFORE.png +0 -0
  306. package/docs/pr-screenshots/pr-916/model-picker-dropdown-AFTER.png +0 -0
  307. package/docs/pr-screenshots/pr-979/auto-fix-pane-AFTER.png +0 -0
  308. package/docs/pr-screenshots/pr-979/auto-fix-pane-BEFORE.png +0 -0
  309. package/docs/pr-screenshots/pr-985/pr-column-em-dash-AFTER.png +0 -0
  310. package/docs/pr-screenshots/pr-985/pr-column-em-dash-BEFORE.png +0 -0
  311. package/docs/visual-evidence-ci.md +0 -103
  312. package/engine/bridge.js +0 -379
  313. package/engine/quarantine-refs.js +0 -103
  314. package/engine/state-operations.js +0 -178
  315. /package/engine/{steering-constraints.js → agents/steering-constraints.js} +0 -0
@@ -0,0 +1,1051 @@
1
+ # Minions Design Language
2
+
3
+ > A code-derived specification of the Minions dashboard visual and interaction
4
+ > system. Every rule below is traceable to a concrete token, selector, HTML
5
+ > fragment, or JavaScript renderer in `dashboard/`. Treat this as both an
6
+ > **implementation guide** (convert another app to the Minions language) and an
7
+ > **audit standard** (grade new and existing Minions UI objectively).
8
+
9
+ **Scope.** This covers the two live front-ends served by `dashboard.js`:
10
+
11
+ - **Classic UX** — the default multi-page SPA. Canonical stylesheet
12
+ `dashboard/styles.css`; shell `dashboard/layout.html`; page fragments
13
+ `dashboard/pages/*.html`; renderers `dashboard/js/*.js`.
14
+ - **Slim UX** — a feature-flagged (`slim-ux`) single-screen cockpit. Self-contained
15
+ stylesheet `dashboard/slim/styles.css`; shell `dashboard/slim/layout.html` +
16
+ `dashboard/slim/body.html`; renderers `dashboard/slim/js/*.js`.
17
+
18
+ Both are assembled into one HTML string at startup by `dashboard-build.js` and
19
+ served from disk on each request (no build step). The two stylesheets are
20
+ **served independently** and intentionally duplicate their token blocks; a
21
+ tripwire test keeps the shared values in lockstep (see
22
+ [§13 Known inconsistencies](#13-known-inconsistencies--legacy-exceptions)).
23
+
24
+ > **Golden rule.** The design language lives in CSS custom properties declared
25
+ > in the `:root` blocks of the two stylesheets. New UI MUST consume tokens, never
26
+ > raw hex or raw `Npx`. Ad-hoc values are the single most common audit failure.
27
+
28
+ ---
29
+
30
+ ## Table of contents
31
+
32
+ 1. [Design principles & personality](#1-design-principles--personality)
33
+ 2. [Color system](#2-color-system)
34
+ 3. [Typography](#3-typography)
35
+ 4. [Spacing, radii, shadows, motion](#4-spacing-radii-shadows-motion)
36
+ 5. [Layout & page shells](#5-layout--page-shells)
37
+ 6. [Components](#6-components)
38
+ 7. [Status & content language](#7-status--content-language)
39
+ 8. [Interaction states & accessibility](#8-interaction-states--accessibility)
40
+ 9. [Classic vs Slim: shared foundations vs intentional differences](#9-classic-vs-slim-shared-foundations-vs-intentional-differences)
41
+ 10. [Reusable implementation patterns](#10-reusable-implementation-patterns)
42
+ 11. [Migration playbook (adopt Minions in another app)](#11-migration-playbook)
43
+ 12. [Audit rubric & checklist](#12-audit-rubric--checklist)
44
+ 13. [Known inconsistencies & legacy exceptions](#13-known-inconsistencies--legacy-exceptions)
45
+
46
+ ---
47
+
48
+ ## 1. Design principles & personality
49
+
50
+ Minions is **mission control for an autonomous agent fleet** — a dense,
51
+ always-on operator dashboard, not a marketing surface. The visual language is
52
+ derived directly from its implementation and reflects five principles:
53
+
54
+ 1. **Dark, terminal-adjacent, GitHub-native.** The palette is GitHub's Primer
55
+ dark scale (`#0d1117` canvas, `#161b22` surface, `#30363d` border, `#58a6ff`
56
+ blue). Operators live in GitHub, ADO, and terminals all day; the dashboard
57
+ sits beside them without a jarring context switch. There is **no light theme**
58
+ — dark is the only mode (see [§2](#2-color-system)).
59
+
60
+ 2. **Information density over whitespace.** Tight spacing (`2/4/6/8px` base
61
+ rhythm), a `12px` type floor for readable metadata, uppercase micro-labels,
62
+ and `tabular-nums` for aligned counters. The sidebar is a fixed `150px`; cards
63
+ and tables pack many rows per viewport. Compare a consumer app's airy
64
+ `24px` gutters — Minions deliberately uses `--space-4: 8px` as its workhorse.
65
+
66
+ 3. **Color is semantic, never decorative.** Each accent hue maps to a fixed
67
+ meaning: **green = success/create/go**, **red = failure/destroy/stop**,
68
+ **yellow/amber = in-progress/attention**, **blue = active/primary/info**,
69
+ **purple = merged/agent/review**, **muted grey = idle/neutral/disabled**. A
70
+ badge's color IS its status. Never pick a hue for looks (see
71
+ [§7](#7-status--content-language)).
72
+
73
+ 4. **State is always legible at a glance.** Every agent, work item, PR, plan,
74
+ dispatch, and engine has a color-coded badge plus, where it matters, motion
75
+ (a pulsing dot = live/working, a dashed border = stale/needs-attention). An
76
+ operator should read fleet health from across the room.
77
+
78
+ 5. **Progressive disclosure through panels & modals.** The surface stays calm;
79
+ detail lives one click deep. Cards open modals (`.modal-bg` / `.modal`), the
80
+ modal stack supports depth up to 5 (`.modal-stack-chip`), and the Command
81
+ Center is a right-hand drawer, not a page.
82
+
83
+ **Interaction philosophy.** Optimistic UI (flip a toggle, then await the API),
84
+ non-blocking async refresh (`/api/status` polled every 4s), hover-to-preview /
85
+ click-to-open, and keyboard-reachable focus rings on every interactive element.
86
+ Destructive actions route through a promise-based confirm dialog
87
+ (`dashboard/js/confirm-dialog.js`), never `window.confirm`.
88
+
89
+ ---
90
+
91
+ ## 2. Color system
92
+
93
+ ### 2.1 Source of truth
94
+
95
+ All colors are CSS custom properties in the `:root` block of each stylesheet.
96
+ **Classic:** `dashboard/styles.css:10-12`. **Slim:** `dashboard/slim/styles.css:13-25`.
97
+ Never write a raw hex outside these blocks; the only sanctioned literals are the
98
+ `rgba(...)` tint fills derived from these hues (see [§2.4](#24-tint-fills)) and
99
+ the one audited scoped token block slim declares for the reused detail panel
100
+ (`dashboard/slim/styles.css:1859-1865` — see [§2.3](#23-palette-tokens-that-diverge-classic-vs-slim)
101
+ and §13 item 10).
102
+
103
+ ### 2.2 Core palette (shared foundation)
104
+
105
+ These seven tokens are **byte-identical** across Classic and Slim and form the
106
+ neutral + primary spine:
107
+
108
+ | Token | Hex | Semantic role |
109
+ | ------------ | --------- | --------------------------------------------------------- |
110
+ | `--bg` | `#0d1117` | App canvas / page background, input backgrounds |
111
+ | `--surface` | `#161b22` | Header, sidebar, modals, table sticky headers, raised bars|
112
+ | `--border` | `#30363d` | Hairline borders, dividers, default (idle) button borders |
113
+ | `--muted` | `#8b949e` | Secondary text, metadata, timestamps, disabled/idle state |
114
+ | `--blue` | `#58a6ff` | Primary action, active state, links, info, focus rings |
115
+ | `--green` | `#3fb950` | Success, create/add, approve, build-pass, "go" |
116
+ | `--red` | `#f85149` | Failure, destroy/delete, changes-requested, build-fail |
117
+
118
+ ### 2.3 Palette tokens that diverge Classic vs Slim
119
+
120
+ These carry the **same semantic role** but different hex. The table describes
121
+ slim's **global** `:root` (`dashboard/slim/styles.css:13-56`) — see the scoped
122
+ token island below before auditing a rule. Do **not** assume parity — the
123
+ divergence is real and load-bearing (see
124
+ [§13](#13-known-inconsistencies--legacy-exceptions)):
125
+
126
+ | Token | Classic (`styles.css`) | Slim `:root` (`slim/styles.css`) | Role |
127
+ | ------------ | ---------------------- | -------------------------------- | -------------------------------------- |
128
+ | `--surface2` | `#21262d` | `#1c2128` | Cards, chips, nested panels, row hover |
129
+ | `--text` | `#e6edf3` | `#c9d1d9` | Primary body text |
130
+ | in-progress | `--yellow: #d29922` | `--amber: #d29922` | Working / building (same hex, diff name)|
131
+ | `--orange` | `#e3b341` | `#ea580c` | Attention / stale / fan-out |
132
+ | `--purple` | `#bc8cff` | *(not in slim `:root`)* | Merged / agent / review / archive |
133
+
134
+ > **Audit note.** Slim names its in-progress token `--amber` and Classic names it
135
+ > `--yellow`, but both resolve to `#d29922`. Slim's `--orange` (`#ea580c`, a hotter
136
+ > red-orange) and Classic's `--orange` (`#e3b341`, a golden amber) genuinely
137
+ > differ. Reference the token by name; never hard-code the hex.
138
+
139
+ > 🔑 **Scoped token island — slim is not token-poor everywhere.** Beyond its
140
+ > global `:root`, slim re-declares the **full Classic token set** scoped to the
141
+ > reused agent detail panel: `--yellow`, `--purple`, `--orange` (at the *Classic*
142
+ > `#e3b341`, not slim's `#ea580c`), `--radius-sm..--radius-full`,
143
+ > `--transition-fast/base/slow`, and `--space-1..--space-9` at the *Classic* 2px
144
+ > values — all under `#detail-panel, #detail-overlay`
145
+ > (`dashboard/slim/styles.css:1859-1865`). It exists so the panel rules mirrored
146
+ > verbatim from Classic (`dashboard/slim/styles.css:1867-1974`) resolve to
147
+ > Classic values **without** disturbing slim's global palette. Consequence for
148
+ > auditors: `var(--purple)` is *valid* in a panel descendant — e.g.
149
+ > `.dispatch-type.review` (`:1914`) and `.dispatch-type.decompose` (`:1923`) —
150
+ > and *invalid* anywhere else in slim. Always resolve the selector's scope before
151
+ > flagging a slim token reference (§13 item 10).
152
+
153
+ ### 2.4 Tint fills
154
+
155
+ Accent backgrounds are the accent hue at **~15% opacity** with a **solid 1px
156
+ border** at full opacity. This "ghost chip" pattern is the entire badge/pill
157
+ system. Canonical values (`dashboard/styles.css:769-774`):
158
+
159
+ ```css
160
+ .badge-blue { background: rgba(88,166,255,0.15); color: var(--blue); border: 1px solid var(--blue); }
161
+ .badge-green { background: rgba(63,185,80,0.15); color: var(--green); border: 1px solid var(--green); }
162
+ .badge-red { background: rgba(248,81,73,0.15); color: var(--red); border: 1px solid var(--red); }
163
+ .badge-yellow { background: rgba(210,153,34,0.15); color: var(--yellow); border: 1px solid var(--yellow); }
164
+ .badge-purple { background: rgba(188,140,255,0.15);color: var(--purple); border: 1px solid var(--purple); }
165
+ .badge-muted { background: var(--surface); color: var(--muted); border: 1px solid var(--border); }
166
+ ```
167
+
168
+ Hover tints on action buttons use **~10%** opacity of the same hue
169
+ (`.btn-add:hover { background: rgba(63,185,80,0.1); }`,
170
+ `dashboard/styles.css:568`). Emphasis variants step up to `0.22`–`0.25` and swap
171
+ to a **dashed** or **2px** border (`.pr-badge.build-escalated`,
172
+ `dashboard/styles.css:441`).
173
+
174
+ > The `rgba()` literals restate the hex of the corresponding token. This is the
175
+ > one sanctioned exception to "no raw color" — CSS cannot yet apply alpha to a
176
+ > custom property without `color-mix`, which the codebase does not use. When you
177
+ > add a tinted surface, reuse the exact `rgba` triplet already used for that hue
178
+ > elsewhere; do not introduce a new opacity step without cause.
179
+
180
+ ### 2.5 Off-palette accent literals (observed, constrained)
181
+
182
+ A handful of dispatch/type chips use raw hex for hues with no token, in
183
+ `dashboard/styles.css:1089-1098`:
184
+
185
+ - `#a855f7` — `plan` / `plan-to-prd` dispatch type (violet, distinct from `--purple`)
186
+ - `#38bdf8` — `docs` dispatch type (sky)
187
+ - `#14b8a6` — `setup` dispatch type (teal)
188
+
189
+ **Observed vs canonical:** these are intentional type-taxonomy colors with no
190
+ `:root` token. Recommended canonical guidance: if you add a new dispatch/work
191
+ type chip, follow the same tint pattern (`rgba(...,0.15)` fill + solid text
192
+ color) and, if the hue recurs, promote it to a `:root` token. Do not scatter new
193
+ raw hex for one-off UI.
194
+
195
+ ### 2.6 Light/dark & contrast
196
+
197
+ There is **no light mode**. All contrast targets assume the dark canvas.
198
+ Expectations:
199
+
200
+ - Body text `--text` on `--bg`/`--surface`: comfortably exceeds WCAG AA (7:1+).
201
+ - `--muted` (`#8b949e`) on `--bg` is the **contrast floor** (~AA for normal text
202
+ ≥ the `12px` type floor). Do **not** put `--muted` below `12px` for
203
+ reading text — that combination fails; the type system enforces a `12px` floor
204
+ precisely for this reason (see [§3.4](#34-the-12px-readability-floor)).
205
+ - Accent text (`--blue`, `--green`, `--red`, etc.) is only used at badge/label
206
+ weight (`font-weight: 600`, uppercase) on its matching `0.15` tint, which
207
+ preserves legibility. Never use a `0.15` tint as text color.
208
+
209
+ **Prohibited:**
210
+ - Raw hex or named CSS colors (`#fff`, `white`, `black`) outside the `:root`
211
+ blocks — with the audited exceptions `#fff` for text-on-solid-accent buttons
212
+ (`.btn-primary`, `dashboard/styles.css:539`) and the `rgba(0,0,0,…)` shadow /
213
+ modal-backdrop scrims.
214
+ - Introducing a second neutral scale, a light theme, or a gradient background
215
+ (the only gradients are the intentional "Try Slim UX" shimmer animation,
216
+ `dashboard/styles.css:966`).
217
+
218
+ ---
219
+
220
+ ## 3. Typography
221
+
222
+ ### 3.1 Font families
223
+
224
+ - **UI:** `'Segoe UI', system-ui, sans-serif` (Classic body,
225
+ `dashboard/styles.css:72`; Slim adds `-apple-system, BlinkMacSystemFont`,
226
+ `dashboard/slim/styles.css:86`).
227
+ - **Monospace:** `Consolas, monospace` for IDs, branch names, feature IDs, code
228
+ (`.mono`, `dashboard/styles.css:795`; `.feat-id`, `:510`).
229
+ - **Inline code:** `--text-code: 0.9em` — the one relative token, so code reads
230
+ 10% smaller than its surrounding text (`dashboard/styles.css:49`).
231
+
232
+ ### 3.2 Size primitives (single source of truth)
233
+
234
+ Declared once per stylesheet (`dashboard/styles.css:31-33`), documented in
235
+ `dashboard/docs/typography.md`. **Never hand-roll a `font-size: Npx`** — a
236
+ tripwire test (`test/unit/dashboard-font-size-tokens.test.js`) fails CI if a raw
237
+ px/em font-size leaks in.
238
+
239
+ | Token | Base | Common use |
240
+ | ---------------- | ----- | ------------------------------------------------------- |
241
+ | `--text-xs` | 10px | Reserved dense chrome only (below the readability floor) |
242
+ | `--text-sm` | 12px | Status badges, chip / tag labels |
243
+ | `--text-base` | 12px | Captions, secondary metadata, dense table cells |
244
+ | `--text-md` | 13px | Detail tables, button labels, slim chat body |
245
+ | `--text-lg` | 14px | Body text, modal textarea, card titles |
246
+ | `--text-xl` | 16px | Main `<body>` font, secondary headers, chat input |
247
+ | `--text-2xl` | 18px | Section headers, page headers, modal h3 |
248
+ | `--text-stat` | 22px | Stat counter readouts, member emoji glyphs |
249
+ | `--text-stat-lg` | 28px | Larger stat readouts, completion type emoji |
250
+ | `--text-display` | 32px | Page hero, markdown H1, agent-detail emoji |
251
+ | `--text-code` | 0.9em | Inline `<code>` (relative) |
252
+
253
+ Each primitive is `calc(<base>px * var(--minions-text-scale))`. Classic pins
254
+ `--minions-text-scale: 1.05` (a global +5% bump); Slim pins it to `1`. The base
255
+ px values **must** match between the two stylesheets.
256
+
257
+ ### 3.3 Role aliases (preferred for new code)
258
+
259
+ Semantic names that point at primitives (`dashboard/styles.css:42-49`). Prefer
260
+ these — they survive a size redesign without callsite edits:
261
+
262
+ | Role token | Alias → size | Intended use |
263
+ | --------------------- | ------------------- | ---------------------------------------- |
264
+ | `--text-role-display` | `--text-display` | Page hero, dashboard title |
265
+ | `--text-heading` | `--text-2xl` (18px) | Section + modal headers |
266
+ | `--text-subheading` | `--text-xl` (16px) | Secondary headers, card titles |
267
+ | `--text-body` | `--text-lg` (14px) | Default body text |
268
+ | `--text-meta` | `--text-md` (13px) | Captions, timestamps, secondary metadata |
269
+ | `--text-caption` | `--text-base` (12px)| Small labels, table cells |
270
+ | `--text-micro` | `--text-sm` (12px) | Chip / tag pill labels |
271
+ | `--text-code` | `0.9em` | Inline code |
272
+
273
+ Utility classes mirror each role: `.text-display .text-heading .text-subheading
274
+ .text-body .text-meta .text-caption .text-micro .text-code`
275
+ (`dashboard/styles.css:80-87`). They set **only** `font-size`; weight, color, and
276
+ line-height stay with the caller.
277
+
278
+ ### 3.4 The 12px readability floor
279
+
280
+ `--text-sm` was raised to a **12px** base so it clears the readability floor in
281
+ both stylesheets (Classic renders `12 × 1.05 = 12.6px`, Slim `12px`). `--text-xs`
282
+ (10px) stays below the floor and is retained only at two genuinely dense chrome
283
+ callsites (`.token-chart-labels span`, `.qa-phase-arrow`). See
284
+ `dashboard/docs/typography.md` §"12px readability floor". **Do not** introduce
285
+ new `--text-xs` reading text.
286
+
287
+ ### 3.5 Weight, case, and treatment conventions
288
+
289
+ - **Section headers:** `text-transform: uppercase; letter-spacing: 1px; color:
290
+ var(--muted); font-weight: 600` at `--text-base` (`section h2`,
291
+ `dashboard/styles.css:114`). Uppercase micro-labels are a signature of the
292
+ language.
293
+ - **Table headers:** uppercase, `letter-spacing: 0.5px`, `--muted`,
294
+ `font-weight: 500`, `--text-sm` (`.table th`, `:778`; `.pr-table th`, `:381`).
295
+ - **Badges/pills:** uppercase, `font-weight: 600`, `--text-sm` (`.badge`, `:764`;
296
+ `.pr-badge`, `:428`).
297
+ - **Body:** default weight, `--text` color; **bold** = `font-weight: 600` via
298
+ `.font-bold` (`:796`) — the design uses 600 as its "bold", not 700, except for
299
+ emphasis badges (`.dispatch-stat-num` 700, `:1073`).
300
+ - **Numeric readouts:** `font-variant-numeric: tabular-nums` for timestamps and
301
+ counters (`.timestamp`, `:98`; `.modal-stack-chip`, `:1210`).
302
+
303
+ ### 3.6 Truncation & wrapping
304
+
305
+ - Single-line ellipsis via `.truncate` (`white-space:nowrap; overflow:hidden;
306
+ text-overflow:ellipsis`, `:797`) — used for agent actions, dispatch tasks, PR
307
+ titles/branches, project branches. Always pair a truncated element with a
308
+ `title` attribute carrying the full text (see `_renderProjectBranch` note,
309
+ `dashboard/styles.css:1523`).
310
+ - Multi-line content that must wrap uses `white-space: pre-wrap; word-break:
311
+ break-word` (`.modal-qa-a`, `:137`).
312
+
313
+ ### 3.7 Responsive type scaling
314
+
315
+ Two independent mechanisms:
316
+
317
+ 1. **User zoom** — `[data-font-size]` on `<html>` drives `--minions-font-scale`
318
+ applied as body-level `zoom` (`small 1.0 / medium 1.08 / large 1.18 / xlarge
319
+ 1.30`, `dashboard/styles.css:66-69,72`). Set in `<head>` before first paint
320
+ from `localStorage['minions:font-size']` (`dashboard/layout.html:13-19`).
321
+ 2. **Global bump** — `--minions-text-scale` (Classic `1.05`) is an actual
322
+ font-size change applied uniformly to every primitive.
323
+
324
+ At `≤640px`, headers, stats, and tables shrink to smaller tokens (`.pr-table`
325
+ → `--text-base`, headers → `--text-xl`, `dashboard/styles.css:1470-1483`).
326
+
327
+ ---
328
+
329
+ ## 4. Spacing, radii, shadows, motion
330
+
331
+ ### 4.1 Spacing scale
332
+
333
+ **Classic** — 9-step, `2px`-anchored (`dashboard/styles.css:15-16`):
334
+
335
+ `--space-1:2 --space-2:4 --space-3:6 --space-4:8 --space-5:10 --space-6:12
336
+ --space-7:16 --space-8:20 --space-9:24`
337
+
338
+ **Slim (global `:root`)** — 5-step, `4px`-anchored `4/8/12/16/20` rhythm
339
+ (`dashboard/slim/styles.css:30`):
340
+
341
+ `--space-1:4 --space-2:8 --space-3:12 --space-4:16 --space-5:20`
342
+
343
+ > **Critical divergence.** The `--space-N` tokens mean **different pixel values**
344
+ > in the two stylesheets. `--space-4` = `8px` in Classic but `16px` in Slim. Never
345
+ > copy a spacing rule between the two without re-mapping. Slim declares its own
346
+ > scale precisely because it ships CSS independently. `--space-4: 8px` is the
347
+ > Classic workhorse gutter (cards, flex gaps, table cells).
348
+
349
+ > **Exception — the scoped token island.** Inside `#detail-panel,
350
+ > #detail-overlay` slim re-declares the **Classic** 9-step 2px scale
351
+ > (`--space-1..--space-9`, `dashboard/slim/styles.css:1863-1864`), so panel
352
+ > descendants resolve `--space-4` to `8px`, not `16px`. Panel rules mirrored from
353
+ > Classic into that subtree are therefore correct **unmapped** — the re-mapping
354
+ > rule above governs slim's *global* surfaces only (§2.3, §13 item 10).
355
+
356
+ ### 4.2 Border radius (Classic, `dashboard/styles.css:52`)
357
+
358
+ `--radius-sm:4px --radius-md:6px --radius-lg:8px --radius-xl:10px
359
+ --radius-full:50%`
360
+
361
+ - Buttons, inputs, small chips → `--radius-sm`.
362
+ - Cards, dispatch items → `--radius-md` / `--radius-lg`.
363
+ - Badges/pills → `--radius-xl` (`10px`, near-pill).
364
+ - Dots/avatars → `--radius-full`.
365
+
366
+ Slim's **global** `:root` collapses this to a single `--radius: 6px`
367
+ (`dashboard/slim/styles.css:25`), but slim re-declares the whole
368
+ `--radius-sm..--radius-full` ladder at the Classic values scoped to
369
+ `#detail-panel, #detail-overlay` (`dashboard/slim/styles.css:1861`). So
370
+ `var(--radius-xl)` resolves inside the slim detail panel and nowhere else in
371
+ slim — check the selector's scope before flagging it (§2.3, §13 item 10).
372
+
373
+ ### 4.3 Shadows (Classic, `dashboard/styles.css:55-57`)
374
+
375
+ `--shadow-sm: 0 1px 3px rgba(0,0,0,0.2)` · `--shadow-md: 0 2px 8px rgba(0,0,0,0.3)`
376
+ · `--shadow-lg: 0 4px 16px rgba(0,0,0,0.4)`
377
+
378
+ Shadows are **restrained** — used for hover lift on cards (`.agent-card:hover`,
379
+ `:122`), floating buttons (`.ask-selection-btn`, `:157`), and elevated menus.
380
+ The primary elevation signal is **border-color change**, not shadow. Focus glow
381
+ uses a blue ring: `box-shadow: 0 0 0 3px rgba(88,166,255,0.12)`
382
+ (`.cmd-input-wrap:focus-within`, `:811`).
383
+
384
+ ### 4.4 Motion (Classic, `dashboard/styles.css:60`)
385
+
386
+ `--transition-fast:0.15s --transition-base:0.2s --transition-slow:0.3s`
387
+
388
+ - Hover/color transitions → `--transition-fast` / `--transition-base`.
389
+ - Card transforms → `--transition-slow`.
390
+ - **Live-state animations** are the semantic motion vocabulary:
391
+ - `pulse` (2s opacity, `:97`) — the live "engine running" dot (`.pulse`),
392
+ working badges, stuck warnings.
393
+ - `dotPulse` (1.2s, `:145`) — loading "typing" dots (QA, notif-processing).
394
+ - `notifPulse` (2s, `:1251`) — red unread notification dot.
395
+ - `planPulse` / `wipPulse` / `prdWipPulse` / `plSegPulse` — WIP emphasis on
396
+ plan/PRD/pipeline nodes.
397
+ - **Reduced motion:** honor `@media (prefers-reduced-motion: reduce)`
398
+ (`.try-slim-ux-btn` animation disabled, `:982`). One deliberate exception is
399
+ documented inline at `:1000` (the Command Center sweep). New decorative
400
+ animation MUST respect the reduced-motion query.
401
+
402
+ ---
403
+
404
+ ## 5. Layout & page shells
405
+
406
+ ### 5.1 Classic shell (`dashboard/layout.html`)
407
+
408
+ ```
409
+ <body> (flex column, height 100%, overflow hidden, zoom = font-scale)
410
+ ├─ <header> sticky, --surface, bottom border, title + engine badge + tools
411
+ ├─ .engine-alert cross-page alert strip
412
+ ├─ .paused-banner sticky kill-switch banner (pollingPaused / autoFixPaused)
413
+ ├─ #cc-drawer Command Center right drawer (420px, fixed)
414
+ └─ .page-layout flex row, flex:1, overflow hidden
415
+ ├─ .sidebar 150px fixed, --surface, right border, sticky
416
+ └─ .page-content flex:1, scrollable; holds .page fragments (one .active)
417
+ ```
418
+
419
+ Key rules:
420
+ - **Header** — `dashboard/styles.css:89-98`. Sticky, `z-index:100`. `h1` is
421
+ `--text-2xl`, `font-weight:600`, `--blue`, `letter-spacing:0.5px`, with a muted
422
+ `<span>` suffix.
423
+ - **Sidebar** — `:105-111`. Fixed `150px`; links are `--muted` → `--text` on
424
+ hover, active link gets a `3px` `--blue` left border + `--surface2` bg + 600
425
+ weight. Each link carries a `.sidebar-count` pill and optional `.notif-badge`.
426
+ - **Pages** — `.page { display:none }`, `.page.active { display:block }` (`:112`).
427
+ Single-page-at-a-time; sidebar nav swaps `.active`.
428
+ - **Section** — `section { padding: 20px 24px; border-bottom: 1px solid
429
+ var(--border) }` (`:101`); `section h2` is the uppercase muted micro-header
430
+ with an optional `.count` chip (`:114-115`).
431
+ - **Embed mode** — `?embed=1` adds `.embed` to `<html>` to strip chrome (header,
432
+ sidebar, banners) so a page can be iframed as just its panel
433
+ (`dashboard/layout.html:20-32`).
434
+
435
+ ### 5.2 Slim shell (`dashboard/slim/layout.html` + `body.html`)
436
+
437
+ A three-region cockpit — **ACTIONS / STATUS / HISTORY** — built from `.panel`
438
+ grid areas (`dashboard/slim/styles.css:204-276`), a `.topbar` (46px, `:123-132`),
439
+ a chat surface (`.chat-*`), and member/tile grids. It is a **single screen**, not
440
+ a paged SPA. See `docs/slim-ux/concepts.md`.
441
+
442
+ ### 5.3 Cards, panels, grids
443
+
444
+ - **Card base** `.card` — `--surface2` bg, `1px --border`, `--radius-lg`, padding
445
+ `12px 16px`, hover → `--blue` border (`dashboard/styles.css:522-528`). The
446
+ universal container primitive; specific cards (`.agent-card`, `.plan-card`,
447
+ `.archive-card`) extend it.
448
+ - **Agent grid** — 2-column (`.agents { grid-template-columns: 1fr 1fr }`,
449
+ `:117`), collapses to 1 column at `≤640px` (`:1475`).
450
+ - **Hover lift** — cards raise via `transform: translateY(-1px)` + `--shadow-md` +
451
+ accent border (`.agent-card:hover`, `:122`).
452
+
453
+ ### 5.4 Tables
454
+
455
+ Two table systems:
456
+ - **Generic** `.table` (`:777-780`) — full-width, `border-collapse`, `--text-md`
457
+ body, uppercase muted `th`, `--surface2` row hover.
458
+ - **PR/work-item** `.pr-table` (`:354-384`) — adds sticky headers
459
+ (`thead th { position: sticky; top: 0; background: var(--surface) }`, `:380`),
460
+ horizontal scroll wrapper (`.pr-table-wrap { overflow-x: auto }`, `:353`), a
461
+ fixed-layout `--prs` variant with `min-width: 1600px` and per-column ellipsis
462
+ (`:361-372`), and a mobile card-reflow for `.quarantine-ref-table` at `≤640px`
463
+ (`:1484-1508`).
464
+
465
+ Tables are dense: `td` padding `8px 10px`, `1px --border` row dividers, no zebra
466
+ striping — hover is the row-affordance.
467
+
468
+ ---
469
+
470
+ ## 6. Components
471
+
472
+ ### 6.1 Buttons
473
+
474
+ Minions has a **deliberately consolidated** button taxonomy. Each family is a
475
+ single source of truth with size variants; **do not** re-create the inline style
476
+ blob for a new button — extend the primitive. All families share: `--surface2`
477
+ or transparent bg, `--radius-sm`, `--transition` on hover, a `~10%` hue-tint
478
+ hover, a `2px` accent `:focus-visible` outline, and a `0.4` opacity disabled
479
+ state.
480
+
481
+ | Class | Identity | Use | Source |
482
+ | -------------------- | --------------------------------- | ------------------------------------------ | ------ |
483
+ | `.btn` | neutral bordered, `--surface` | generic default button | `:531` |
484
+ | `.btn-primary` | solid `--blue`, `#fff` text | inline primary | `:539` |
485
+ | `.btn-success/-danger/-warning` | colored border/text | semantic outline variants | `:542` |
486
+ | `.btn-add` (+`-lg`) | green border/text, no glyph rules | "+ Add / + New" creators | `:558` |
487
+ | `.btn-action` (+`-md/-lg`) | green border/text | verb actions (Approve/Verify/Save/Run) | `:592` |
488
+ | `.btn-destructive` (+`-md/-lg`) | red border/text | Delete / Abort / Remove / Unpin | `:625` |
489
+ | `.pr-info-pill` | blue border/text (↗) | open-PR-detail affordance in PR rows | `:652` |
490
+ | `.btn-primary-solid` | solid blue, `font-weight:600` | gating CTA (Start QA, Save runbook) | `:673` |
491
+ | `.btn-primary-lg` | solid blue, normal weight | modal Save / Create / Re-execute | `:697` |
492
+ | `.btn-ghost` | transparent, muted, bordered | secondary next to a primary CTA | `:717` |
493
+ | `.empty-state-add` | dashed border, green, CTA chip | empty-state "+ Add Project" | `:736` |
494
+
495
+ **Rule:** color encodes intent — green = create/affirm, red = destroy, blue =
496
+ primary/navigate, muted ghost = secondary. Size variant suffixes (`-md`, `-lg`)
497
+ change only padding/size, never color. Positional spacing (`margin-left`) lives
498
+ at the callsite, not on the button (`dashboard/styles.css:555`).
499
+
500
+ ### 6.2 Badges & status pills
501
+
502
+ - **`.badge`** (`:764-774`) — generic uppercase pill, `--radius-xl`, `--text-sm`,
503
+ 600 weight, 6 color variants (`-blue -green -red -yellow -purple -muted`) using
504
+ the `0.15` tint pattern.
505
+ - **`.pr-badge`** (`:428-443`) — the PR/work-item status pill with a **status
506
+ vocabulary of classes** rather than color names:
507
+ `draft active approved rejected needs-review review-escalated merged building
508
+ build-pass build-fail no-build build-stale build-escalated needs-attention`.
509
+ See [§7](#7-status--content-language) for the state→class→color mapping.
510
+ - **`.status-badge`** (`:128-131`) — agent status (`idle` muted / `working`
511
+ amber-pulsing / `done` green).
512
+ - **`.dispatch-type.*`** (`:1081-1098`) — per-work-type chips (implement, review,
513
+ fix, test, plan, docs, setup, …), each a fixed tint.
514
+
515
+ Emphasis: escalated / needs-attention states step the tint to `0.22–0.25` and
516
+ switch to a **dashed** or **2px** border + `font-weight:700` (`:434, :441`).
517
+ Motion (`animation: pulse`) marks live/critical states (`building`,
518
+ `dispatch-stuck`).
519
+
520
+ ### 6.3 Forms & inputs
521
+
522
+ - Inputs/textareas: `--bg` background, `1px --border`, `--radius-sm`, `--text`
523
+ color, `font-family: inherit`; focus swaps border to `--blue` and drops the
524
+ native outline (`.modal-qa-input`, `:152-153`).
525
+ - The **Command Center composer** (`.cmd-input-wrap`, `:806-838`) is the richest
526
+ input: a `2px` border that lights `--blue` with a `3px` glow on
527
+ `:focus-within`, a transparent syntax-highlight layer under the textarea
528
+ (`.cmd-highlight-layer` colorizes `@mentions`, `/commands`, `!priority`,
529
+ `#project`, `--flags`), and intent chips (`.cmd-chip`) beneath.
530
+ - Toggles: `.settings-switch` and `.pr-automation-switch` are custom checkbox
531
+ switches with `:focus-visible` rings on the track (`:1455, :493`).
532
+
533
+ ### 6.4 Dialogs & modals
534
+
535
+ - **Backdrop** `.modal-bg` — `position:fixed; inset:0; background:
536
+ rgba(0,0,0,0.75); z-index:400`; `.open` sets `display:flex` centered
537
+ (`:1185-1186`).
538
+ - **Card** `.modal` — `--surface`, `1px --border`, `--radius-xl`, `width:95%;
539
+ max-width:1100px; max-height:80vh`, flex column (`:1187`). `.modal-wide` widens
540
+ to `1500px`.
541
+ - **Header** — flex row, bottom border, `h3` is `--text-xl` `--blue`
542
+ (`:1189-1190`); a `.modal-close` muted × and optional `.modal-copy` chrome.
543
+ - **Modal stack** — supports depth (back button `.modal-back-btn` +
544
+ `.modal-stack-chip` showing `n/5`, `:1200-1211`).
545
+ - **Confirm dialog** — promise-based, reuses `.modal-bg`/`.modal` sized down to
546
+ `.confirm-card` (max 480px), right-aligned action row (`:1234-1244`). Replaces
547
+ `window.confirm`; behavior in `dashboard/js/confirm-dialog.js`.
548
+
549
+ ### 6.5 Command Center (drawer)
550
+
551
+ A right-hand `420px` fixed drawer (`#cc-drawer`, `dashboard/layout.html:60`) with
552
+ its own overlay scrim, the rich composer (§6.3), streaming tool chips
553
+ (`renderToolChip`, `dashboard/js/render-utils.js:13`), and action/message chips.
554
+ It is a **surface, not a page** — the primary conversational control plane.
555
+
556
+ ### 6.6 Empty / loading / error states
557
+
558
+ - **Empty** — `.empty { color: var(--muted); font-style: italic; font-size:
559
+ var(--text-md); padding: 8px 0 }` (`:802`); `.tile-empty` in slim
560
+ (`slim/styles.css:889`). Empty-state CTAs use the dashed `.empty-state-add`
561
+ chip.
562
+ - **Loading** — animated dot triads (`.modal-qa-loading .dot-pulse span`,
563
+ `:141-144`; `.notif-badge.processing`, `:1247`) using `dotPulse`. Live
564
+ activity uses the `pulse` dot.
565
+ - **Error** — red accent: `.modal-copy.is-danger` (`:1196`), agent-detail error
566
+ state with a `.btn-primary-lg` Retry (`:694`), `.dispatch-stuck` dashed-red
567
+ pulsing warning (`:1102`). Error text uses `--red`; never a raw red.
568
+
569
+ ### 6.7 Notification badges
570
+
571
+ `.notif-badge` (`:1245-1250`) — absolutely positioned dot on sidebar links / CC
572
+ tabs. `.done` = `8px` red `notifPulse` dot (unread completion); `.processing` =
573
+ three blue `dotPulse` dots (in-flight).
574
+
575
+ ---
576
+
577
+ ## 7. Status & content language
578
+
579
+ Status is the heart of the design. The mapping from **domain state → CSS class →
580
+ color → meaning** is defined in the renderers and MUST be reused, not re-derived.
581
+
582
+ ### 7.1 Work-item status (`dashboard/js/render-work-items.js:200-201, 810`)
583
+
584
+ | Status | `.pr-badge` class | Color | Meaning |
585
+ | --------------------- | ----------------- | ---------------- | -------------------- |
586
+ | `pending` / `queued` | `active` | blue | waiting to dispatch |
587
+ | `dispatched` | `building` | yellow (pulsing) | agent running |
588
+ | `done` / `decomposed` | `approved` | green | complete |
589
+ | `failed` | `rejected` | red | failed |
590
+ | `cancelled` | `draft` | muted | cancelled/removed |
591
+
592
+ ### 7.2 PR review status (`dashboard/js/render-prs.js:130, 480`)
593
+
594
+ | `reviewStatus` | class | Color |
595
+ | --------------------- | ---------- | ---------------- |
596
+ | `approved` | `approved` | green |
597
+ | `changes-requested` / `rejected` | `rejected` | red |
598
+ | `waiting` | `building` | yellow (pulsing) |
599
+ | everything else / `pending` | `draft` | muted |
600
+
601
+ Minions-sourced review labels append `(minions)` and the "reviewing (minions)"
602
+ wording (`render-prs.js:131`).
603
+
604
+ ### 7.3 PR build status (`dashboard/js/render-prs.js:133-134, 484-487`)
605
+
606
+ | `buildStatus` | class | Color |
607
+ | --------------------- | ------------ | --------------------- |
608
+ | `passing` | `build-pass` | green |
609
+ | `failing` | `build-fail` | red |
610
+ | `running` | `building` | yellow (pulsing) |
611
+ | `none` | `no-build` | muted |
612
+ | `_buildStatusStale` | `build-stale`| orange, **dashed** border |
613
+
614
+ Stale/escalated states are visually distinct (dashed / 2px border) so an operator
615
+ never confuses a **confirmed** failure with a **cached/stale** one — a
616
+ load-bearing product rule (build fixes are not dispatched from stale failures).
617
+
618
+ ### 7.4 Agent status (`.status-badge`, `dashboard/styles.css:129-131`)
619
+
620
+ `idle` (muted) · `working` (amber, pulsing) · `done` (green). Agent cards also
621
+ recolor their border: `.agent-card.working` yellow, `.agent-card.done` green
622
+ (`:123-124`).
623
+
624
+ ### 7.5 Dispatch type taxonomy (`.dispatch-type.*`, `:1082-1098`)
625
+
626
+ Each work type has a fixed chip color: `implement`/`meeting` blue,
627
+ `review`/`decompose` purple, `fix` yellow, `analyze`/`ask`/`verify` green, `test`
628
+ orange, `explore`/`manual` muted, `plan` violet `#a855f7`, `docs` sky `#38bdf8`,
629
+ `setup` teal `#14b8a6`, `evaluate` red.
630
+
631
+ ### 7.6 Content & label conventions
632
+
633
+ - **IDs & branches** — monospace (`.mono`), often muted; truncate with a `title`.
634
+ - **Timestamps** — `--muted`, `tabular-nums`, relative ("3 min ago").
635
+ - **Section headers** — UPPERCASE, muted, letter-spaced.
636
+ - **Counts** — pill chips (`.sidebar-count`, `.count`) with tabular numerals.
637
+ - **Status verbs** — imperative and terminal: `approved`, `failing`, `merged`,
638
+ `needs-attention`, `dispatched`. Match the engine constants (`WI_STATUS`,
639
+ `PR_STATUS`, `DISPATCH_RESULT` in `engine/core/shared.js`) — do not invent UI-only
640
+ status words. Never surface a raw enum the user can't read; humanize but keep
641
+ the canonical term recognizable.
642
+
643
+ ---
644
+
645
+ ## 8. Interaction states & accessibility
646
+
647
+ ### 8.1 The four interactive states
648
+
649
+ Every interactive element defines all four:
650
+
651
+ 1. **Default** — `--border` hairline, `--muted` or `--text` label.
652
+ 2. **Hover** — border → accent or `--text`; background → `~10%` hue tint or
653
+ `--surface2`; cards add `translateY(-1px)` + shadow.
654
+ 3. **Focus** — `:focus-visible { outline: 2px solid <accent>; outline-offset:
655
+ 1–2px }` on **every** button family (`:569, :603, :636, :664, :685, :708,
656
+ :728, :749`), plus the `3px` blue glow on rich inputs. **Never** remove focus
657
+ without a visible replacement.
658
+ 4. **Disabled** — `opacity: 0.4; cursor: not-allowed; pointer-events: none`
659
+ (uniform across every button family).
660
+
661
+ ### 8.2 Accessibility requirements
662
+
663
+ - **Hit targets ≥ 24×24px** — enforced via `min-height/min-width` on small
664
+ affordances (command-chip close, `dashboard/styles.css:854`; `.empty-state-add`
665
+ `min 24×24`, `:738`).
666
+ - **12px readability floor** — enforced by the type system (§3.4); do not put
667
+ reading text below it.
668
+ - **Truncated text carries `title`** — full value always reachable (§3.6).
669
+ - **Reduced motion** — honor `prefers-reduced-motion` for decorative animation
670
+ (§4.4).
671
+ - **Focus-visible on all controls**, including custom switches (track gets the
672
+ ring, `:493, :1455`).
673
+ - **Contrast** — accent-on-tint at badge weight; muted text kept ≥ 12px (§2.6).
674
+ - **Semantic markers** — `cursor: help` on informational badges that expose a
675
+ `title` explanation (`.needs-attention`, `:443`; `.dispatch-stuck`, `:1102`).
676
+ - **XSS discipline (implementation a11y/security)** — dashboard JS prefers
677
+ `insertAdjacentHTML('beforeend', …)` / DOM construction over `innerHTML +=`
678
+ (preserves event handlers); an ESLint gate
679
+ (`eslint-plugin-no-unsanitized`, `npm run lint`) blocks unsanitized sinks. All
680
+ user/agent text is escaped via `escapeHtml` before interpolation
681
+ (`dashboard/js/render-work-items.js:202`).
682
+
683
+ ---
684
+
685
+ ## 9. Classic vs Slim: shared foundations vs intentional differences
686
+
687
+ **Do not assume parity.** The two front-ends share a language but ship
688
+ independent CSS. This is the authoritative reconciliation.
689
+
690
+ ### 9.1 Genuinely shared (kept in lockstep by a tripwire)
691
+
692
+ - The **7 core palette tokens** `--bg --surface --border --muted --blue --green
693
+ --red` (identical hex).
694
+ - The **typography size primitives** (base px identical; tripwire
695
+ `test/unit/dashboard-font-size-tokens.test.js`) and **role aliases**.
696
+ - The **`.text-*` utility classes** (duplicated verbatim,
697
+ `dashboard/slim/styles.css:61-68`).
698
+ - The **dark, GitHub-Primer aesthetic**, semantic-color philosophy, uppercase
699
+ micro-labels, and pulse-for-live motion vocabulary.
700
+ - **`.status-line`** and **`.status-badge`** definitions appear in both — but
701
+ slim's copies (`slim/styles.css:1900-1906`) live inside the mirrored
702
+ detail-panel block and depend on the scoped token island (`.status-badge.working`
703
+ resolves `var(--yellow)` only under `#detail-panel` / `#detail-overlay`). They
704
+ are duplicated *presentation*, not a globally shared slim primitive (§9.2 †).
705
+
706
+ ### 9.2 Intentional differences (do NOT "fix" toward parity)
707
+
708
+ | Aspect | Classic | Slim (global `:root`) |
709
+ | --------------------- | ------------------------------------ | -------------------------------------- |
710
+ | `--minions-text-scale`| `1.05` (global +5% bump) | `1` (pinned — excluded from bump) |
711
+ | Spacing scale | 9-step, 2px base (`--space-1..9`) | 5-step, 4px base (`--space-1..5`) † |
712
+ | `--surface2` | `#21262d` | `#1c2128` |
713
+ | `--text` | `#e6edf3` | `#c9d1d9` |
714
+ | In-progress token | `--yellow` | `--amber` (same hex `#d29922`) † |
715
+ | `--orange` | `#e3b341` (golden) | `#ea580c` (hot) † |
716
+ | `--purple` | declared | not in `:root` † |
717
+ | Radius | 5 tokens (`--radius-sm..full`) | single `--radius: 6px` † |
718
+ | Shell | paged SPA (sidebar + `.page`) | single-screen cockpit (`.panel` grid) |
719
+ | Line-height baseline | none on body | `line-height: 1.5` on body |
720
+ | Scrollbars | `6px` thin (`:799`) | `10px` themed + thin nested (`:98-120`)|
721
+
722
+ > † **Global `:root` only — mind the scoped token island.** The Slim column
723
+ > describes `dashboard/slim/styles.css:13-56`. Slim *also* re-declares the
724
+ > Classic `--yellow` / `--purple` / `--orange` (Classic `#e3b341`) /
725
+ > `--radius-sm..full` / `--transition-*` / `--space-1..9` (Classic 2px) set
726
+ > scoped to `#detail-panel, #detail-overlay`
727
+ > (`dashboard/slim/styles.css:1859-1865`), so the detail-panel rules mirrored
728
+ > verbatim from Classic (`:1867-1974`) resolve correctly without disturbing
729
+ > slim's global palette. **Inside that subtree the Classic values apply.** Do not
730
+ > flag a mirrored panel rule that uses `var(--purple)`, `var(--radius-xl)`, or
731
+ > Classic `--space-*` arithmetic as a token violation — resolve the selector's
732
+ > scope first (§13 item 10).
733
+
734
+ ### 9.3 Reuse-before-fork rule
735
+
736
+ When adding behavior to Slim, first check whether Classic already implements it
737
+ and extract a **shared helper** rather than forking a divergent copy (CLAUDE.md
738
+ "Slim UX vs Classic UX — reuse, don't fork"). A divergent parallel copy of an
739
+ existing renderer is grounds for review rejection. The token *duplication* is
740
+ sanctioned (independent stylesheets); *logic* duplication is not.
741
+
742
+ ---
743
+
744
+ ## 10. Reusable implementation patterns
745
+
746
+ Concrete, traceable references for converting or auditing UI.
747
+
748
+ ### 10.1 Token consumption
749
+
750
+ ```css
751
+ /* GOOD — every value is a token */
752
+ .my-chip {
753
+ background: var(--surface2);
754
+ border: 1px solid var(--border);
755
+ border-radius: var(--radius-sm);
756
+ padding: var(--space-2) var(--space-5);
757
+ font-size: var(--text-micro);
758
+ color: var(--muted);
759
+ }
760
+ /* BAD — raw hex + raw px (fails audit + font-size tripwire) */
761
+ .my-chip { background:#1c2128; padding:4px 10px; font-size:12px; color:#8b949e; }
762
+ ```
763
+
764
+ ### 10.2 Status badge
765
+
766
+ ```js
767
+ // Reuse the class vocabulary, never re-pick colors:
768
+ const cls = s === 'failed' ? 'rejected'
769
+ : s === 'dispatched' ? 'building'
770
+ : s === 'done' ? 'approved'
771
+ : s === 'cancelled' ? 'draft' : 'active';
772
+ return `<span class="pr-badge ${cls}">${escapeHtml(s)}</span>`;
773
+ // (dashboard/js/render-work-items.js:200-202, 810)
774
+ ```
775
+
776
+ ### 10.3 Button — extend, don't inline
777
+
778
+ ```html
779
+ <!-- GOOD -->
780
+ <button class="btn-action btn-action-lg" onclick="save()">Save</button>
781
+ <!-- BAD — re-creating the green blob -->
782
+ <button style="color:var(--green);border:1px solid var(--green);padding:2px 10px">Save</button>
783
+ ```
784
+
785
+ ### 10.4 DOM-safe rendering
786
+
787
+ ```js
788
+ el.insertAdjacentHTML('beforeend', html); // preserves handlers; passes lint
789
+ // el.innerHTML += html; // BANNED — drops handlers, XSS risk
790
+ text = escapeHtml(userValue); // ALWAYS escape untrusted text
791
+ ```
792
+
793
+ ### 10.5 Truncate + title
794
+
795
+ ```js
796
+ `<span class="truncate" title="${escapeHtml(full)}">${escapeHtml(full)}</span>`
797
+ ```
798
+
799
+ ### 10.6 Reference index (files & symbols)
800
+
801
+ | Concern | File | Anchor |
802
+ | ----------------------- | ----------------------------------------------- | ----------------------------------- |
803
+ | Tokens (Classic) | `dashboard/styles.css` | `:root` `:1-61` |
804
+ | Tokens (Slim) | `dashboard/slim/styles.css` | `:root` `:13-56` |
805
+ | Tokens (Slim, scoped) | `dashboard/slim/styles.css` | `#detail-panel, #detail-overlay` `:1859-1865` |
806
+ | Typography contract | `dashboard/docs/typography.md` | whole file |
807
+ | Type tripwire | `test/unit/dashboard-font-size-tokens.test.js` | raw-px gate |
808
+ | Buttons | `dashboard/styles.css` | `:519-761` |
809
+ | Badges / pr-badge | `dashboard/styles.css` | `:428-443, 763-774` |
810
+ | Tables | `dashboard/styles.css` | `:354-384, 776-780` |
811
+ | Modals / confirm | `dashboard/styles.css` | `:1185-1244` |
812
+ | Command Center composer | `dashboard/styles.css` | `:804-853` |
813
+ | Status renderers | `dashboard/js/render-prs.js`, `render-work-items.js` | status→class maps |
814
+ | Shared render helpers | `dashboard/js/render-utils.js` | `renderToolChip`, `escapeHtml` uses |
815
+ | Shell / embed mode | `dashboard/layout.html` | `:38-60, :20-32` |
816
+ | Slim shell | `dashboard/slim/layout.html`, `body.html` | `.panel` grid |
817
+ | Lint gate | `npm run lint` | `eslint-plugin-no-unsanitized` |
818
+
819
+ ---
820
+
821
+ ## 11. Migration playbook
822
+
823
+ Adopting the Minions language in another application, in dependency order.
824
+
825
+ **Phase 0 — Establish tokens.**
826
+ 1. Copy the `:root` token block (`dashboard/styles.css:1-61`) into your global
827
+ stylesheet: the 7 core palette tokens, accent tokens, the full type primitive
828
+ + role-alias system, the 9-step spacing scale, radii, shadows, transitions.
829
+ 2. Set the body baseline: `background: var(--bg); color: var(--text);
830
+ font-family: 'Segoe UI', system-ui, sans-serif; font-size: var(--text-xl)`.
831
+ 3. Add the `.text-*` utility classes and (optionally) the `[data-font-size]`
832
+ zoom mechanism from `dashboard/layout.html:13-19`.
833
+
834
+ **Phase 1 — Token mapping.** Map your existing values to tokens:
835
+
836
+ | Your value | Minions token |
837
+ | ------------------------- | ------------------- |
838
+ | page background | `--bg` |
839
+ | card / panel background | `--surface2` |
840
+ | raised bar / header | `--surface` |
841
+ | primary text | `--text` |
842
+ | secondary/meta text | `--muted` |
843
+ | hairline border | `--border` |
844
+ | primary action / link | `--blue` |
845
+ | success / create | `--green` |
846
+ | error / destroy | `--red` |
847
+ | in-progress / warning | `--yellow` |
848
+ | gutter (default) | `--space-4` (8px) |
849
+ | card padding | `--space-6 --space-7` |
850
+ | body font | `--text-body` |
851
+ | labels | `--text-micro` |
852
+
853
+ Replace every raw hex and raw `font-size` as you go; run a grep for `#[0-9a-f]{3,6}`
854
+ and `font-size:\s*\d` to find stragglers.
855
+
856
+ **Phase 2 — Component conversion.** Port the primitives in this order (each
857
+ depends on tokens only): `.card` → `.btn*` families → `.badge`/`.pr-badge` →
858
+ `.table`/`.pr-table` → `.modal`/confirm → inputs/switches → empty/loading/error
859
+ states → Command-Center-style composer (if you need a conversational surface).
860
+ Reuse the exact class vocabulary and status→class maps from §7 and §10.
861
+
862
+ **Phase 3 — Interaction & states.** Add the four interactive states (§8.1) to
863
+ every control: hover tint, `:focus-visible` accent outline, `0.4` disabled,
864
+ `--transition-fast` timing. Wire the `pulse`/`dotPulse` live-state animations and
865
+ respect `prefers-reduced-motion`.
866
+
867
+ **Phase 4 — Responsive validation.** Add the `≤1100px` and `≤640px` breakpoints
868
+ (`dashboard/styles.css:1460-1514`): grids collapse to 1 column, stats wrap,
869
+ tables shrink type and gain horizontal scroll, modals go to `95vw`. Verify at
870
+ `320 / 640 / 1100 / 1440px`.
871
+
872
+ **Phase 5 — Accessibility.** Confirm §8.2: ≥24px hit targets, 12px reading floor,
873
+ `title` on all truncation, focus-visible everywhere, contrast on the dark canvas,
874
+ escaped user text, sanitized HTML sinks.
875
+
876
+ **Phase 6 — Visual-regression.** Capture Playwright screenshots of each converted
877
+ surface at a normal viewport (BEFORE/AFTER for conversions). Minions gates this
878
+ in CI (`docs/visual-evidence-ci.md`); mirror that discipline. Run the type
879
+ tripwire equivalent to prevent raw-px regressions.
880
+
881
+ ---
882
+
883
+ ## 12. Audit rubric & checklist
884
+
885
+ Grade a Minions UI change objectively. **Severity:** 🔴 blocker (must fix before
886
+ merge) · 🟡 major (fix or file follow-up) · 🔵 minor (nit).
887
+
888
+ ### 12.1 Tokens & color
889
+
890
+ - 🔴 Any raw hex / named CSS color outside a `:root` block (except audited
891
+ `#fff`-on-accent and `rgba(0,0,0,…)` scrims/shadows, and the sanctioned slim
892
+ scoped token island at `dashboard/slim/styles.css:1859-1865` — §13 item 10).
893
+ - 🔴 A color chosen for looks rather than its semantic role (§2.2, §7).
894
+ - 🟡 A new tint fill with a novel opacity step instead of the established
895
+ `0.10 / 0.15 / 0.22–0.25` ladder.
896
+ - 🟡 A hue that recurs ≥3× still hard-coded instead of promoted to a token.
897
+
898
+ ### 12.2 Typography
899
+
900
+ - 🔴 Raw `font-size: Npx` / `Nem` in CSS, inline style, or JS template (fails
901
+ `test/unit/dashboard-font-size-tokens.test.js`).
902
+ - 🟡 Reading text below the 12px floor (`--text-xs` used for anything but the two
903
+ allowlisted dense-chrome callsites).
904
+ - 🔵 New code using a size primitive where a role alias exists.
905
+
906
+ ### 12.3 Spacing / layout
907
+
908
+ - 🟡 Raw `Npx` padding/margin/gap where a `--space-*` token fits.
909
+ - 🔴 Copying a `--space-*` rule between Classic and Slim without re-mapping (the
910
+ scales differ — §4.1). **Not** a violation inside `#detail-panel,
911
+ #detail-overlay` in slim, where the Classic 9-step scale is re-declared on
912
+ purpose (§9.2 †, §13 item 10) — verify the selector's scope before flagging.
913
+ - 🟡 A new card/panel not built on `.card` (or a slim `.panel`).
914
+
915
+ ### 12.4 Components & states
916
+
917
+ - 🔴 A new button that inlines the color/border blob instead of extending a
918
+ `.btn-*` family (§6.1).
919
+ - 🔴 Missing `:focus-visible` outline on an interactive element.
920
+ - 🟡 Missing hover and/or disabled state.
921
+ - 🟡 A status rendered with an ad-hoc class instead of the `.pr-badge` / status
922
+ vocabulary and the §7 maps.
923
+ - 🔵 Hit target < 24px without a `min-width/min-height` bump.
924
+
925
+ ### 12.5 Accessibility & safety
926
+
927
+ - 🔴 `innerHTML +=` / unsanitized HTML sink (fails `npm run lint`).
928
+ - 🔴 Unescaped user/agent text interpolated into markup.
929
+ - 🟡 Truncated text without a `title`.
930
+ - 🟡 Decorative animation ignoring `prefers-reduced-motion`.
931
+
932
+ ### 12.6 Classic/Slim discipline
933
+
934
+ - 🔴 Forking an existing Classic renderer into a divergent Slim copy instead of
935
+ extracting a shared helper (§9.3).
936
+ - 🟡 "Normalizing" an intentional Classic/Slim token difference toward false
937
+ parity (§9.2).
938
+
939
+ ### 12.7 Common anti-patterns (fast reference)
940
+
941
+ | Anti-pattern | Correct pattern |
942
+ | ----------------------------------------------- | -------------------------------------- |
943
+ | `style="color:#3fb950"` | `class="text-green"` / token |
944
+ | `font-size:14px` | `var(--text-body)` / `.text-body` |
945
+ | `padding:8px` | `var(--space-4)` |
946
+ | new `<button style="border:1px solid green…">` | `class="btn-action"` |
947
+ | `innerHTML += row` | `insertAdjacentHTML('beforeend', row)` |
948
+ | `window.confirm('Sure?')` | `confirm-dialog.js` promise |
949
+ | raw enum text `"changes_requested"` in UI | humanized label + `.pr-badge rejected` |
950
+
951
+ ### 12.8 Worked review examples
952
+
953
+ - **REJECT:** a PR adds `<span style="background:#21262d;padding:4px;font-size:12px">`.
954
+ → 🔴 raw hex, raw px, raw padding. Fix: `class="card text-caption"` with
955
+ `--space-*` padding.
956
+ - **APPROVE:** a PR adds a new work-type chip as
957
+ `.dispatch-type.migrate { background: rgba(88,166,255,0.15); color: var(--blue); }`
958
+ extending the existing pattern with a token color. 🟢 (Would be 🟡 if it
959
+ introduced a novel raw hue used only once.)
960
+ - **REQUEST CHANGES:** a Slim panel re-implements the PR status→class logic
961
+ inline instead of importing the shared helper. → 🔴 fork; extract shared.
962
+
963
+ ---
964
+
965
+ ## 13. Known inconsistencies & legacy exceptions
966
+
967
+ Labelled **observed** (what the code does today) vs **canonical** (recommended
968
+ guidance). Do not silently normalize these away.
969
+
970
+ 1. **Duplicated token blocks, by design.** Classic and Slim declare separate
971
+ `:root` blocks. *Observed:* intentional (independent stylesheets served from
972
+ disk). *Canonical:* keep them duplicated but rely on
973
+ `test/unit/dashboard-font-size-tokens.test.js` to keep the type values in
974
+ lockstep; there is **no** tripwire for the palette/spacing tokens, so
975
+ palette/spacing drift is possible — verify manually when touching either
976
+ `:root`. Slim additionally carries a **third**, scoped declaration block for
977
+ the reused detail panel — see item 10.
978
+
979
+ 2. **`--yellow` (Classic) vs `--amber` (Slim), same hex.** *Observed:* two names,
980
+ one value `#d29922`. *Canonical:* reference the token that exists in the
981
+ stylesheet you're editing; do not cross-import the name.
982
+
983
+ 3. **`--orange` differs in hue between the two.** Classic `#e3b341` (golden),
984
+ Slim `#ea580c` (hot). *Observed:* genuine difference. *Canonical:* accept it;
985
+ both signal "attention/fan-out/stale" in their own surface.
986
+
987
+ 4. **`--space-*` tokens mean different pixels.** Classic 2px-base 9-step vs Slim
988
+ 4px-base 5-step. *Observed & canonical:* never copy spacing rules across
989
+ without re-mapping.
990
+
991
+ 5. **Off-palette dispatch-type hues** (`#a855f7`, `#38bdf8`, `#14b8a6`,
992
+ `dashboard/styles.css:1089-1098`). *Observed:* raw hex with no token.
993
+ *Canonical:* acceptable as a fixed type taxonomy; promote to tokens if reused
994
+ outside the dispatch chips.
995
+
996
+ 6. **Two table systems** (`.table` and `.pr-table`). *Observed:* `.pr-table`
997
+ duplicates much of `.table` plus sticky-header/scroll behavior. *Canonical:*
998
+ use `.pr-table` for data grids (PRs, work items) and `.table` for simple
999
+ tables; do not add a third system.
1000
+
1001
+ 7. **`--text-xs` below the 12px floor.** *Observed:* retained at two dense-chrome
1002
+ callsites only. *Canonical:* never use it for reading text.
1003
+
1004
+ 8. **Legacy `.layout` grid** is superseded by `.page-layout`
1005
+ (`dashboard/styles.css:100` comments "Replaced by page-layout"). *Observed:*
1006
+ the old `.layout` rule and its `≤1100px` media rule (`:1461`) still exist.
1007
+ *Canonical:* build on `.page-layout`; treat `.layout` as dead.
1008
+
1009
+ 9. **Inline styles in `layout.html`.** *Observed:* the header toolbar buttons
1010
+ carry inline `style="…"` blobs (`dashboard/layout.html:41-47`) with token
1011
+ values. *Canonical:* new controls should use the button primitives (§6.1)
1012
+ rather than inline blobs; these header buttons predate the consolidation.
1013
+
1014
+ 10. **Slim's scoped Classic-token island (`#detail-panel, #detail-overlay`).**
1015
+ *Observed:* `dashboard/slim/styles.css:1859-1865` re-declares the full
1016
+ Classic token set — `--yellow: #d29922`, `--purple: #bc8cff`,
1017
+ `--orange: #e3b341` (the *Classic* golden value, shadowing slim's global hot
1018
+ `#ea580c`), `--radius-sm..--radius-full`, `--transition-fast/base/slow`, and
1019
+ `--space-1..--space-9` at the Classic 2px values — scoped to the two panel
1020
+ roots. It exists because the agent detail-panel markup and JS are reused
1021
+ **verbatim** from Classic (`dashboard/layout.html` +
1022
+ `dashboard/js/{render-agents,detail-panel,live-stream,charter-editor,…}.js`),
1023
+ while slim and Classic ship separate top-level stylesheets with no shared CSS
1024
+ partial (W-mrz11djx00050594, rationale comment at
1025
+ `dashboard/slim/styles.css:1845-1858`). Mirroring the classic rule bodies
1026
+ unchanged (`:1867-1974`) is only sound because the tokens they reference
1027
+ resolve to Classic values inside that subtree.
1028
+
1029
+ *Canonical:* keep it. It is the cheapest way to reuse Classic panel CSS
1030
+ without forking it (§9.3) or globally re-pointing slim's palette. Two
1031
+ consequences bind every auditor and contributor:
1032
+ - **Scope before flagging.** `var(--purple)`, `var(--radius-xl)`, and Classic
1033
+ `--space-*` arithmetic are *valid* inside a panel descendant and *invalid*
1034
+ elsewhere in slim. A blanket "slim has no `--purple`" rejection is a
1035
+ false positive (§2.3, §9.2 †, §12.1, §12.3).
1036
+ - **Don't widen it.** Add tokens to the island only when a newly mirrored
1037
+ Classic panel rule needs them; never promote them to slim's global `:root`
1038
+ (that would silently re-skin slim), and never author *new* slim-native UI
1039
+ against island tokens.
1040
+
1041
+ ---
1042
+
1043
+ ## Validation
1044
+
1045
+ Every claim above was verified against source at the paths/line ranges cited.
1046
+ This is a **documentation-only** artifact — no UI behavior changed. When the
1047
+ implementation evolves, update the cited anchors here and re-run
1048
+ `test/unit/dashboard-font-size-tokens.test.js` (the one automated guard this doc
1049
+ depends on).
1050
+
1051
+ Authored with [Minions](https://icy-water-0224cc51e.2.azurestaticapps.net/minions).