@yemi33/minions 0.1.2149 → 0.1.2151
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/minions.js +99 -6
- package/dashboard/docs/typography-audit.md +128 -0
- package/dashboard/docs/typography.md +114 -0
- package/dashboard/js/render-agents.js +1 -1
- package/dashboard/js/render-work-items.js +21 -13
- package/dashboard/js/utils.js +1 -1
- package/dashboard/slim/body.html +2 -2
- package/dashboard/slim/styles.css +112 -77
- package/dashboard/styles.css +37 -3
- package/dashboard.js +67 -10
- package/docs/README.md +1 -1
- package/docs/auto-discovery.md +2 -1
- package/docs/branch-derivation.md +68 -0
- package/docs/cooldown-merge-semantics.md +4 -4
- package/docs/design-state-storage.md +5 -5
- package/docs/kb-sweep.md +2 -2
- package/docs/managed-spawn.md +1 -1
- package/docs/timeouts-and-liveness.md +120 -0
- package/docs/watches.md +9 -9
- package/docs/worktree-lifecycle.md +164 -0
- package/engine/cli.js +8 -5
- package/engine/dispatch.js +26 -1
- package/package.json +6 -1
package/bin/minions.js
CHANGED
|
@@ -64,6 +64,10 @@ const PKG_ROOT = path.resolve(__dirname, '..');
|
|
|
64
64
|
const shared = require(path.join(PKG_ROOT, 'engine', 'shared'));
|
|
65
65
|
const { openUrlInBrowser } = shared;
|
|
66
66
|
const { waitForRestartHealth, formatRestartHealthError } = require(path.join(PKG_ROOT, 'engine', 'restart-health'));
|
|
67
|
+
// Dev-mode dashboard port. `--dev` mode (see argv parser below) retargets
|
|
68
|
+
// MINIONS_HOME at PKG_ROOT and routes the dashboard onto this port so a dev
|
|
69
|
+
// checkout coexists with a global npm install on the normal port (7331).
|
|
70
|
+
const DEFAULT_DEV_DASH_PORT = 7332;
|
|
67
71
|
|
|
68
72
|
/**
|
|
69
73
|
* Resolve the dashboard port for any CLI command. Precedence (W-mq5nwl9l):
|
|
@@ -71,7 +75,7 @@ const { waitForRestartHealth, formatRestartHealthError } = require(path.join(PKG
|
|
|
71
75
|
* ACTUALLY bound to. Wins over the chain so status/dash/restart-health
|
|
72
76
|
* probes the right port even after an EADDRINUSE auto-fallback.
|
|
73
77
|
* 2. Explicit `--port N` CLI flag if present in `argv`.
|
|
74
|
-
* 3. env MINIONS_DASHBOARD_PORT.
|
|
78
|
+
* 3. env MINIONS_DASHBOARD_PORT (dev mode pre-seeds this with devPort).
|
|
75
79
|
* 4. config.json#engine.dashboardPort.
|
|
76
80
|
* 5. shared.DEFAULT_DASHBOARD_PORT (7331).
|
|
77
81
|
*
|
|
@@ -578,7 +582,50 @@ function resolveMinionsHome(forInit = false) {
|
|
|
578
582
|
return shared.resolveMinionsHome(forInit);
|
|
579
583
|
}
|
|
580
584
|
|
|
581
|
-
|
|
585
|
+
// `--dev` runs the in-place checkout AS the runtime root, on a separate
|
|
586
|
+
// dashboard port (default 7332) so it can coexist with a global npm install on
|
|
587
|
+
// 7331. Only valid when bin/minions.js sits inside a git checkout — the
|
|
588
|
+
// `.git` probe stops the global shim (whose PKG_ROOT lives under
|
|
589
|
+
// node_modules/) from accidentally activating dev mode.
|
|
590
|
+
//
|
|
591
|
+
// We strip --dev / --dev-port <n> from argv BEFORE the cmd/rest split so the
|
|
592
|
+
// flag can appear in any position (`minions --dev help`, `minions help --dev`,
|
|
593
|
+
// `minions --dev start --foo` all work). Otherwise `--dev` as the first arg
|
|
594
|
+
// would be misread as the subcommand.
|
|
595
|
+
const { cmd, rest, devMode, devPort } = (() => {
|
|
596
|
+
const raw = process.argv.slice(2);
|
|
597
|
+
const out = [];
|
|
598
|
+
let dev = process.env.MINIONS_DEV === '1';
|
|
599
|
+
let port = null;
|
|
600
|
+
for (let i = 0; i < raw.length; i++) {
|
|
601
|
+
const arg = raw[i];
|
|
602
|
+
if (arg === '--dev') { dev = true; continue; }
|
|
603
|
+
if (arg === '--dev-port') {
|
|
604
|
+
const next = raw[i + 1];
|
|
605
|
+
const n = Number(next);
|
|
606
|
+
if (!Number.isInteger(n) || n <= 0 || n > 65535) {
|
|
607
|
+
console.error(`\n --dev-port requires an integer 1-65535, got: ${next || '(missing)'}\n`);
|
|
608
|
+
process.exit(2);
|
|
609
|
+
}
|
|
610
|
+
port = n; dev = true; i++; continue;
|
|
611
|
+
}
|
|
612
|
+
if (arg && arg.startsWith('--dev-port=')) {
|
|
613
|
+
const n = Number(arg.slice('--dev-port='.length));
|
|
614
|
+
if (!Number.isInteger(n) || n <= 0 || n > 65535) {
|
|
615
|
+
console.error(`\n --dev-port=<n> requires an integer 1-65535, got: ${arg}\n`);
|
|
616
|
+
process.exit(2);
|
|
617
|
+
}
|
|
618
|
+
port = n; dev = true; continue;
|
|
619
|
+
}
|
|
620
|
+
out.push(arg);
|
|
621
|
+
}
|
|
622
|
+
if (dev && port == null) {
|
|
623
|
+
const envPort = Number(process.env.MINIONS_DEV_PORT);
|
|
624
|
+
if (Number.isInteger(envPort) && envPort > 0 && envPort <= 65535) port = envPort;
|
|
625
|
+
}
|
|
626
|
+
const [firstCmd, ...restArgs] = out;
|
|
627
|
+
return { cmd: firstCmd, rest: restArgs, devMode: dev, devPort: port || DEFAULT_DEV_DASH_PORT };
|
|
628
|
+
})();
|
|
582
629
|
let force = rest.includes('--force');
|
|
583
630
|
const skipScan = rest.includes('--skip-scan');
|
|
584
631
|
const skipStart = rest.includes('--skip-start') || rest.includes('--no-start');
|
|
@@ -587,8 +634,46 @@ const skipStart = rest.includes('--skip-start') || rest.includes('--no-start');
|
|
|
587
634
|
// when the heuristic mis-fires (e.g. you closed your dashboard tab seconds before
|
|
588
635
|
// running restart and its final heartbeat was still inside the 45s window).
|
|
589
636
|
const forceOpen = rest.includes('--open') || process.env.MINIONS_FORCE_OPEN === '1';
|
|
590
|
-
|
|
637
|
+
// When --dev is set, PKG_ROOT MUST be a git checkout. Without the .git probe a
|
|
638
|
+
// user who typed `minions --dev` from any directory would silently retarget the
|
|
639
|
+
// globally-installed package as its own dev workspace.
|
|
640
|
+
if (devMode && !fs.existsSync(path.join(PKG_ROOT, '.git'))) {
|
|
641
|
+
console.error(`
|
|
642
|
+
--dev requires running from a git checkout of minions.
|
|
643
|
+
This bin/minions.js lives under: ${PKG_ROOT}
|
|
644
|
+
No .git directory was found there — refusing to retarget runtime files.
|
|
645
|
+
|
|
646
|
+
To use dev mode, clone the repo and run from inside it:
|
|
647
|
+
git clone https://github.com/opg-microsoft/minions.git
|
|
648
|
+
cd minions && node bin/minions.js --dev restart
|
|
649
|
+
`);
|
|
650
|
+
process.exit(2);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
const MINIONS_HOME = devMode ? PKG_ROOT : resolveMinionsHome(cmd === 'init');
|
|
591
654
|
process.env.MINIONS_HOME = MINIONS_HOME;
|
|
655
|
+
// When --dev is set, force the dashboard chain onto the dev port (7332 by
|
|
656
|
+
// default or --dev-port N) so a dev checkout coexists with a global install
|
|
657
|
+
// on 7331. We seed MINIONS_DASHBOARD_PORT so resolveDashboardPort()'s env
|
|
658
|
+
// step treats devPort as the fallback while still respecting an existing dev
|
|
659
|
+
// runtime-file at PKG_ROOT/engine/dashboard-port.json (runtime-file wins).
|
|
660
|
+
if (devMode) process.env.MINIONS_DASHBOARD_PORT = String(devPort);
|
|
661
|
+
// Effective dashboard port for this CLI invocation. Honors the W-mq5nwl9l
|
|
662
|
+
// chain (runtime-file → --port → env → config → DEFAULT_DASHBOARD_PORT/7331);
|
|
663
|
+
// --dev overrides via the env seed above. Consumed by the watchdog tick
|
|
664
|
+
// callsite below and propagated to children via MINIONS_PORT.
|
|
665
|
+
// Named DASHBOARD_PORT (not the deprecated DASH-underscore-PORT identifier) so
|
|
666
|
+
// the W-mq5nwl9l source-inspection guard in
|
|
667
|
+
// test/unit/dashboard-port-resolution.test.js keeps holding: that test pins
|
|
668
|
+
// that no static `\bDASH_PORT\b` survives, because anything other than the
|
|
669
|
+
// dynamic chain re-introduces the 7331 bug W-mq5nwl9l was opened to fix.
|
|
670
|
+
const DASHBOARD_PORT = resolveDashboardPort(rest).port;
|
|
671
|
+
// Propagate the port to every child (engine, dashboard, delegated subcommands).
|
|
672
|
+
// engine/cli.js status reads MINIONS_PORT; dashboard.js reads PORT. We export
|
|
673
|
+
// MINIONS_PORT for the engine + watch-actions / pipeline / managed-spawn
|
|
674
|
+
// loopback callers that already honor it, and pass PORT explicitly when
|
|
675
|
+
// spawning dashboard.js.
|
|
676
|
+
process.env.MINIONS_PORT = String(DASHBOARD_PORT);
|
|
592
677
|
const POST_UPDATE_INIT_TIMEOUT_MS = 120000;
|
|
593
678
|
const POST_UPDATE_RESTART_TIMEOUT_MS = 60000;
|
|
594
679
|
|
|
@@ -1051,8 +1136,16 @@ if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {
|
|
|
1051
1136
|
minions watchdog uninstall Remove the scheduled task (idempotent)
|
|
1052
1137
|
minions watchdog status Show registration + last-run details from the OS scheduler
|
|
1053
1138
|
minions watchdog tick One-shot probe + recovery (used by the scheduler; safe to run by hand)
|
|
1054
|
-
|
|
1055
|
-
|
|
1139
|
+
${fs.existsSync(path.join(PKG_ROOT, '.git')) ? `
|
|
1140
|
+
Dev mode (this checkout, contributors only):
|
|
1141
|
+
minions --dev <cmd> Run against this checkout instead of ~/.minions/
|
|
1142
|
+
MINIONS_HOME=<checkout>, dashboard on :7332.
|
|
1143
|
+
Coexists with the global install on :7331.
|
|
1144
|
+
minions --dev --dev-port <n> <cmd> Override the dev dashboard port.
|
|
1145
|
+
npm run dev Shortcut for: node bin/minions.js --dev restart
|
|
1146
|
+
` : ''}
|
|
1147
|
+
Runtime root: ${MINIONS_HOME}${devMode ? ' [DEV]' : ''}${devMode ? `
|
|
1148
|
+
Dashboard port: ${DASHBOARD_PORT} [DEV]` : ''}
|
|
1056
1149
|
`);
|
|
1057
1150
|
} else if (cmd === 'init') {
|
|
1058
1151
|
init();
|
|
@@ -1365,7 +1458,7 @@ if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {
|
|
|
1365
1458
|
watchdog.tick({
|
|
1366
1459
|
minionsHome: MINIONS_HOME,
|
|
1367
1460
|
minionsBin,
|
|
1368
|
-
dashPort:
|
|
1461
|
+
dashPort: DASHBOARD_PORT,
|
|
1369
1462
|
readEnginePid,
|
|
1370
1463
|
isPortListening,
|
|
1371
1464
|
isStopIntentSet: shared.isStopIntentSet || (() => false),
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Dashboard typography audit (W-mq1c8og40003a7cf)
|
|
2
|
+
|
|
3
|
+
This document is the pre-migration audit that justified the token set
|
|
4
|
+
now declared in `dashboard/styles.css` + `dashboard/slim/styles.css`.
|
|
5
|
+
See `dashboard/docs/typography.md` for the contract.
|
|
6
|
+
|
|
7
|
+
## Findings
|
|
8
|
+
|
|
9
|
+
### Baseline before this WI
|
|
10
|
+
|
|
11
|
+
| Surface | font-size declarations | Already on tokens? |
|
|
12
|
+
| ----------------------------- | ---------------------: | ----------------------------- |
|
|
13
|
+
| `dashboard/styles.css` (main) | 179 | ✅ all `var(--text-*)` except `.prd-item-id { font-size: 0.9em }` (slipped past PR #66 tripwire because the pattern was px-only) |
|
|
14
|
+
| `dashboard/layout.html` | 20 | ✅ all `var(--text-*)` |
|
|
15
|
+
| `dashboard/pages/*.html` | 32 | ✅ all `var(--text-*)` |
|
|
16
|
+
| `dashboard/js/*.js` | ~530 | ✅ all `var(--text-*)` except `utils.js:263` `font-size:0.9em` (same em-pattern blind spot as above) |
|
|
17
|
+
| `dashboard/slim/styles.css` | 82 | ❌ all literals (slim was explicitly out of scope for PR #66) |
|
|
18
|
+
| `dashboard/slim/body.html` | 2 | ❌ inline literals |
|
|
19
|
+
| **Total** | **~845** | **~84 raw / non-tokenized** |
|
|
20
|
+
|
|
21
|
+
The main dashboard was already ~99% tokenized by PR #66
|
|
22
|
+
(`refactor(dashboard): anchor typography scale at 12px, migrate raw px
|
|
23
|
+
to tokens`). Two `0.9em` declarations slipped past its `\d+\s*px`
|
|
24
|
+
tripwire because the regex didn't catch the em form.
|
|
25
|
+
|
|
26
|
+
The slim UX prototype (`dashboard/slim/*`) was deliberately out of scope
|
|
27
|
+
for PR #66 — slim is a feature-flagged playground anchored at 16px
|
|
28
|
+
body, and its CSS is served from disk on every request so the human can
|
|
29
|
+
hand-edit it (`dashboard/slim/styles.css` header comment).
|
|
30
|
+
|
|
31
|
+
### Distinct font-size values seen across the audit
|
|
32
|
+
|
|
33
|
+
(Unique px values, sorted.)
|
|
34
|
+
|
|
35
|
+
| px | Count | Maps to | Where |
|
|
36
|
+
| -: | -----:| ------------------ | ---------------------------------------------------------- |
|
|
37
|
+
| 10 | many | `--text-xs` | main dashboard chips/badges |
|
|
38
|
+
| 11 | many | `--text-sm` | main dashboard status badges |
|
|
39
|
+
| 12 | ~30 | `--text-base` | main dashboard captions + most slim chips/labels |
|
|
40
|
+
| 13 | ~26 | `--text-md` | main dashboard default + slim default |
|
|
41
|
+
| 14 | ~10 | `--text-lg` | main dashboard body + slim body |
|
|
42
|
+
| 15 | 1 | `--text-lg` (round −1) | slim `.topbar-title` |
|
|
43
|
+
| 16 | ~7 | `--text-xl` | main dashboard body, slim chat input + msg |
|
|
44
|
+
| 17 | 1 | `--text-2xl` (round +1) | slim `.agent-detail-name` |
|
|
45
|
+
| 18 | many | `--text-2xl` | main dashboard h2 / modal headers |
|
|
46
|
+
| 20 | 1 | `--text-2xl` (round −2) | slim `.icon-btn` |
|
|
47
|
+
| 22 | many | `--text-stat` | main + slim stat readouts |
|
|
48
|
+
| 24 | 1 | `--text-stat` (round −2) | slim `.member-emoji` (decorative emoji glyph) |
|
|
49
|
+
| 28 | many | `--text-stat-lg` | main + slim large readouts |
|
|
50
|
+
| 32 | many | `--text-display` | main dashboard hero / H1 |
|
|
51
|
+
| 34 | 1 | `--text-display` (round −2) | slim `.agent-detail-emoji` (decorative emoji) |
|
|
52
|
+
| em | | | |
|
|
53
|
+
| 0.9em | 3 | `--text-code` | inline `<code>` in main + slim |
|
|
54
|
+
|
|
55
|
+
### Rounding decisions (5 sites)
|
|
56
|
+
|
|
57
|
+
The task contract says: *"prefer rounding to the nearest token over
|
|
58
|
+
inventing a new token. If rounding would visibly change the UI in a way
|
|
59
|
+
you can't justify, flag it in the PR description."*
|
|
60
|
+
|
|
61
|
+
All five rounded sites are within ±2px of their assigned token:
|
|
62
|
+
|
|
63
|
+
| Selector | Was | Now (token) | Δ | Justification |
|
|
64
|
+
| ------------------------------------- | --: | ------------------ | ---: | -------------------------------------------------------------- |
|
|
65
|
+
| `slim .topbar-title` | 15 | 14 `--text-lg` | −1px | Topbar title — 1px reduction not perceptible at glance |
|
|
66
|
+
| `slim .agent-detail-name` | 17 | 18 `--text-2xl` | +1px | Detail-modal heading — 1px increase matches modal-h3 elsewhere |
|
|
67
|
+
| `slim .icon-btn` | 20 | 18 `--text-2xl` | −2px | Hamburger / close icon — emoji-like glyph |
|
|
68
|
+
| `slim .member-emoji` | 24 | 22 `--text-stat` | −2px | Decorative emoji avatar |
|
|
69
|
+
| `slim .agent-detail-emoji` | 34 | 32 `--text-display`| −2px | Decorative emoji in detail modal |
|
|
70
|
+
|
|
71
|
+
Each rounded site has a `/* Rounded from Npx → Mpx … */` inline comment.
|
|
72
|
+
All five sit inside the feature-flagged slim UX, so the blast radius is
|
|
73
|
+
minimal even if the human decides to revert. If any rounding visibly
|
|
74
|
+
breaks the slim layout, define a one-off custom property scoped to that
|
|
75
|
+
selector and reference it instead of expanding the global token set.
|
|
76
|
+
|
|
77
|
+
### Tokens chosen — and rejected
|
|
78
|
+
|
|
79
|
+
**Chosen** (7 role tokens + 1 code token, all alias size primitives):
|
|
80
|
+
|
|
81
|
+
`--text-role-display`, `--text-heading`, `--text-subheading`,
|
|
82
|
+
`--text-body`, `--text-meta`, `--text-caption`, `--text-micro`,
|
|
83
|
+
`--text-code`.
|
|
84
|
+
|
|
85
|
+
**Rejected**:
|
|
86
|
+
|
|
87
|
+
- `--text-h1` / `--text-h2` / `--text-h3` — semantic HTML headings don't
|
|
88
|
+
map 1:1 to visual roles in this dashboard (`<h1>` in `header` is 18px;
|
|
89
|
+
PRD section h1s render at 32px). Role names beat heading names.
|
|
90
|
+
- `--text-label` — would overlap with `--text-caption`. Same role.
|
|
91
|
+
- `--text-large` / `--text-medium` / `--text-small` — pure-size names
|
|
92
|
+
duplicate the primitives without adding semantics.
|
|
93
|
+
- Slim-specific tokens (e.g. `--text-slim-body: 15px`) — would let slim
|
|
94
|
+
drift further from main. Better to round once and converge.
|
|
95
|
+
- A separate `--icon-*` family for the decorative emoji glyphs — only
|
|
96
|
+
3 sites, all single-use, and they all happen to round cleanly to an
|
|
97
|
+
existing primitive. Not worth a parallel token family yet.
|
|
98
|
+
|
|
99
|
+
### Out of scope (deliberately)
|
|
100
|
+
|
|
101
|
+
- font-weight, line-height, letter-spacing, color — explicitly out of
|
|
102
|
+
scope per the task contract; this WI is size-only.
|
|
103
|
+
- font-family — no change.
|
|
104
|
+
- SVG `font-size='90'` inside the favicon `data:image/svg+xml` URL —
|
|
105
|
+
data URLs can't resolve CSS variables. Allowlisted.
|
|
106
|
+
- The 4 `:root[data-font-size="…"] { --minions-font-scale: … }` rules
|
|
107
|
+
declare the dashboard zoom scale; they're attribute selectors, not
|
|
108
|
+
text-size declarations. Allowlisted.
|
|
109
|
+
|
|
110
|
+
## Regression guard
|
|
111
|
+
|
|
112
|
+
Extended `test/unit/dashboard-font-size-tokens.test.js`:
|
|
113
|
+
|
|
114
|
+
- Already covered: main `dashboard/styles.css`, `dashboard/layout.html`,
|
|
115
|
+
`dashboard/pages/*.html`, `dashboard/js/*.js` for raw `Npx` font-sizes.
|
|
116
|
+
- New in this WI:
|
|
117
|
+
- Extended file set to include `dashboard/slim/styles.css` and
|
|
118
|
+
`dashboard/slim/body.html`.
|
|
119
|
+
- Added pattern for `font-size: Nem|Nrem|N%` (catches the `0.9em`
|
|
120
|
+
blind spot that hid the two pre-existing inline-code declarations).
|
|
121
|
+
- Allowlisted the `--text-code: 0.9em` token declaration itself.
|
|
122
|
+
- Asserted that slim's typography tokens match the main ones
|
|
123
|
+
(drift-detection between the two stylesheets).
|
|
124
|
+
|
|
125
|
+
The test runs in `npm test` and `npm run test:sequential`. ESLint
|
|
126
|
+
remains scoped to XSS-prevention (`eslint.config.js`); we don't add a
|
|
127
|
+
second lint plugin for CSS literals because the pure-Node tripwire
|
|
128
|
+
already covers every file type.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Dashboard typography tokens
|
|
2
|
+
|
|
3
|
+
> Single source of truth for text sizing across the Minions dashboard.
|
|
4
|
+
> Centralized in W-mq1c8og40003a7cf (follow-up to PR #66 size primitives
|
|
5
|
+
> and PR #80 `.btn-add` consolidation).
|
|
6
|
+
|
|
7
|
+
## Why this exists
|
|
8
|
+
|
|
9
|
+
The dashboard SPA renders text from ~30 modules (`dashboard/js/render-*.js`,
|
|
10
|
+
`dashboard/pages/*.html`, `dashboard/styles.css`, plus the slim UX prototype
|
|
11
|
+
in `dashboard/slim/`). Without a shared token system, every callsite picks
|
|
12
|
+
its own pixel size. Cards drift, captions disagree, headers don't line up
|
|
13
|
+
across pages, and changing the design language costs ~700 edits.
|
|
14
|
+
|
|
15
|
+
Two pieces solve this together:
|
|
16
|
+
|
|
17
|
+
1. **Size primitives** (`--text-xs` ... `--text-display`) — raw px values.
|
|
18
|
+
The 10 declared sizes cover every visual role the dashboard currently
|
|
19
|
+
uses. Anchored at `--text-base = 12px`. PR #66 migrated ~664 callsites
|
|
20
|
+
from raw `Npx` to these tokens.
|
|
21
|
+
2. **Role aliases** (`--text-heading`, `--text-body`, etc.) — semantic
|
|
22
|
+
names that point at size primitives. Added in W-mq1c8og40003a7cf so
|
|
23
|
+
new code talks in roles, not px. The size primitive stays the single
|
|
24
|
+
source of truth; role names are aliases.
|
|
25
|
+
|
|
26
|
+
## Files this lives in
|
|
27
|
+
|
|
28
|
+
| File | What it owns |
|
|
29
|
+
| ------------------------------- | ---------------------------------------------------------------------------------------------------------- |
|
|
30
|
+
| `dashboard/styles.css` | Canonical token declarations (`:root`), utility classes, and main-dashboard CSS rules. |
|
|
31
|
+
| `dashboard/slim/styles.css` | Duplicate token declarations + utility classes for the feature-flagged slim UX (self-contained stylesheet). |
|
|
32
|
+
| `dashboard/docs/typography.md` | This file — the contract. |
|
|
33
|
+
| `dashboard/docs/typography-audit.md` | The pre-migration audit + rounding decisions. |
|
|
34
|
+
| `test/unit/dashboard-font-size-tokens.test.js` | Tripwire that fails CI if raw `Npx` / `Nem` font-sizes leak back in. |
|
|
35
|
+
|
|
36
|
+
A tripwire asserts the slim token VALUES match the main values so the
|
|
37
|
+
two stay in lockstep.
|
|
38
|
+
|
|
39
|
+
## Size primitives
|
|
40
|
+
|
|
41
|
+
| Token | Value | Common use |
|
|
42
|
+
| ------------------ | ----- | --------------------------------------------------------- |
|
|
43
|
+
| `--text-xs` | 10px | Smallest legitimate text; sidebar badges, count pills |
|
|
44
|
+
| `--text-sm` | 11px | Status badges, chip / tag labels |
|
|
45
|
+
| `--text-base` | 12px | Captions, secondary metadata, dense table cells |
|
|
46
|
+
| `--text-md` | 13px | Default for slim chat / detail tables / button labels |
|
|
47
|
+
| `--text-lg` | 14px | Body text, modal textarea, card titles |
|
|
48
|
+
| `--text-xl` | 16px | Main `<body>` font, secondary headers, chat input |
|
|
49
|
+
| `--text-2xl` | 18px | Section headers, page headers, modal h3 |
|
|
50
|
+
| `--text-stat` | 22px | Stat counter readouts, member emoji glyphs |
|
|
51
|
+
| `--text-stat-lg` | 28px | Larger stat readouts, completions-card type emoji |
|
|
52
|
+
| `--text-display` | 32px | Page hero / markdown H1, agent-detail emoji |
|
|
53
|
+
| `--text-code` | 0.9em | Inline `<code>` — relative so it scales with parent text |
|
|
54
|
+
|
|
55
|
+
> `--text-code` is intentionally relative (`em`, not `px`). Inline code
|
|
56
|
+
> should read 10% smaller than whatever surrounds it. This is the one
|
|
57
|
+
> token that breaks the "all px" rule and is explicitly allowlisted in
|
|
58
|
+
> the tripwire.
|
|
59
|
+
|
|
60
|
+
## Role aliases (preferred for new code)
|
|
61
|
+
|
|
62
|
+
Use the role token when adding new UI. It survives a future size-scale
|
|
63
|
+
redesign without callsite edits.
|
|
64
|
+
|
|
65
|
+
| Role token | Aliases | Intended use |
|
|
66
|
+
| ---------------------- | ------------------- | ----------------------------------------- |
|
|
67
|
+
| `--text-role-display` | `--text-display` | Page hero, dashboard title |
|
|
68
|
+
| `--text-heading` | `--text-2xl` (18px) | Section + modal headers |
|
|
69
|
+
| `--text-subheading` | `--text-xl` (16px) | Secondary headers, card titles |
|
|
70
|
+
| `--text-body` | `--text-lg` (14px) | Default body text, paragraphs |
|
|
71
|
+
| `--text-meta` | `--text-md` (13px) | Captions, timestamps, secondary metadata |
|
|
72
|
+
| `--text-caption` | `--text-base` (12px)| Small labels, table cells |
|
|
73
|
+
| `--text-micro` | `--text-sm` (11px) | Chip / tag pill labels |
|
|
74
|
+
| `--text-code` | (`0.9em`) | Inline code |
|
|
75
|
+
|
|
76
|
+
## Utility classes
|
|
77
|
+
|
|
78
|
+
Each role token has a matching utility class for callsites that can't
|
|
79
|
+
write a CSS rule (inline `class="text-meta"`, template strings, etc.).
|
|
80
|
+
Only `font-size` is set — weight, color, line-height stay with the caller.
|
|
81
|
+
|
|
82
|
+
```html
|
|
83
|
+
<span class="text-meta">3 min ago</span>
|
|
84
|
+
<h3 class="text-heading">Active dispatches</h3>
|
|
85
|
+
<code class="text-code">npm run lint</code>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Available classes: `.text-display`, `.text-heading`, `.text-subheading`,
|
|
89
|
+
`.text-body`, `.text-meta`, `.text-caption`, `.text-micro`, `.text-code`.
|
|
90
|
+
|
|
91
|
+
## How to add a new text style
|
|
92
|
+
|
|
93
|
+
1. Pick an existing role token. The 7 roles + 1 code token cover every
|
|
94
|
+
case the audit found.
|
|
95
|
+
2. If your value doesn't fit any role, round to the nearest primitive
|
|
96
|
+
and document it inline. Inventing a new token requires changing
|
|
97
|
+
`dashboard/styles.css`, `dashboard/slim/styles.css`, this doc, AND
|
|
98
|
+
the tripwire.
|
|
99
|
+
3. NEVER hand-roll `font-size: 14px` in CSS / inline style / JS template
|
|
100
|
+
string. The tripwire test `test/unit/dashboard-font-size-tokens.test.js`
|
|
101
|
+
fails CI.
|
|
102
|
+
|
|
103
|
+
## Exceptions / allowlist
|
|
104
|
+
|
|
105
|
+
These declarations are intentional and exempt from the tripwire:
|
|
106
|
+
|
|
107
|
+
- `:root[data-font-size="…"]` rules in `dashboard/styles.css` — these
|
|
108
|
+
declare the user-level zoom scale, not text size.
|
|
109
|
+
- SVG `font-size='90'` inside the `data:image/svg+xml` favicon link in
|
|
110
|
+
`dashboard/layout.html` and `dashboard/slim/layout.html` — runtime
|
|
111
|
+
data URL, CSS variables don't resolve inside it.
|
|
112
|
+
- The `--text-*` token declarations themselves in `:root` blocks.
|
|
113
|
+
|
|
114
|
+
Everything else MUST go through a token.
|
|
@@ -82,7 +82,7 @@ async function openAgentDetail(id) {
|
|
|
82
82
|
// guarantees no HTML interpretation even if the escape function were ever bypassed.
|
|
83
83
|
const nameEl = document.getElementById('detail-agent-name');
|
|
84
84
|
const emojiSpan = document.createElement('span');
|
|
85
|
-
emojiSpan.style.fontSize = '
|
|
85
|
+
emojiSpan.style.fontSize = 'var(--text-stat)';
|
|
86
86
|
emojiSpan.textContent = agent.emoji || '';
|
|
87
87
|
// Runtime tag \u2014 uses the inline-SVG logo from the same RUNTIME_TAGS map the
|
|
88
88
|
// card uses, so the visual is consistent. The container's user-controlled
|
|
@@ -15,6 +15,10 @@ const _WI_ENRICHMENT_FIELDS = [
|
|
|
15
15
|
'_pr', '_prUrl', '_pendingReason', '_skipReason', '_blockedBy',
|
|
16
16
|
'_humanFeedback', '_reopened', '_managedSpawnPartial', '_securityFlag',
|
|
17
17
|
'_artifacts', 'referencesCount', 'acceptanceCriteriaCount',
|
|
18
|
+
// W-mq5xg5e9000nec0e — slim shape marker so the detail/edit modals know
|
|
19
|
+
// to hydrate the full description via GET /api/work-items/<id> rather than
|
|
20
|
+
// pre-filling textareas with the truncated snippet.
|
|
21
|
+
'_descriptionTruncated',
|
|
18
22
|
// W-mq08kuog001110a6 — _preDispatchEval IS persisted to disk by
|
|
19
23
|
// engine/dispatch.js#_persistInvalidWorkItem, but the slim /api/status
|
|
20
24
|
// overlay may drop it on later passes. Carry it across the overlay so
|
|
@@ -302,17 +306,19 @@ function renderWorkItems(items, opts) {
|
|
|
302
306
|
async function editWorkItem(id, source) {
|
|
303
307
|
const cached = allWorkItems.find(i => i.id === id);
|
|
304
308
|
if (!cached) return;
|
|
305
|
-
// Hydrate the full record before rendering the form. The
|
|
306
|
-
//
|
|
307
|
-
//
|
|
308
|
-
//
|
|
309
|
-
//
|
|
310
|
-
//
|
|
311
|
-
//
|
|
312
|
-
//
|
|
309
|
+
// Hydrate the full record before rendering the form. The bulk
|
|
310
|
+
// /api/work-items list endpoint truncates `description` and replaces
|
|
311
|
+
// `references` / `acceptanceCriteria` arrays with `referencesCount` /
|
|
312
|
+
// `acceptanceCriteriaCount` integers (W-mphejzmj000718bf / W-mq5xg5e9000nec0e
|
|
313
|
+
// — see dashboard.js slimWorkItemForList). Without hydration the textareas
|
|
314
|
+
// would pre-fill with truncated or empty values, and Save would POST those
|
|
315
|
+
// back to handleWorkItemsUpdate, which treats them as defined
|
|
316
|
+
// (dashboard.js:5157, :5164-5165) and silently wipes the stored values:
|
|
317
|
+
// data-loss on every pencil-edit of a pending/failed WI.
|
|
313
318
|
// See PR #2816 review feedback.
|
|
314
319
|
let item = cached;
|
|
315
320
|
const needsHydration = !cached.description ||
|
|
321
|
+
cached._descriptionTruncated === true ||
|
|
316
322
|
(cached.acceptanceCriteriaCount > 0 && !Array.isArray(cached.acceptanceCriteria)) ||
|
|
317
323
|
(cached.referencesCount > 0 && !Array.isArray(cached.references));
|
|
318
324
|
if (needsHydration) {
|
|
@@ -802,12 +808,14 @@ function openWorkItemDetail(id) {
|
|
|
802
808
|
if (!cached) return;
|
|
803
809
|
|
|
804
810
|
// Render the modal immediately from the cached (slim) record so the click
|
|
805
|
-
// feels instant. The /api/
|
|
806
|
-
// PR link, artifacts, etc. — everything except the heavy free-text
|
|
807
|
-
// (description, full acceptanceCriteria, full references)
|
|
808
|
-
// W-mphejzmj000718bf. We then hydrate the
|
|
809
|
-
// GET /api/work-items/<id> and re-render in
|
|
811
|
+
// feels instant. The bulk /api/work-items list is the source of badges,
|
|
812
|
+
// status, PR link, artifacts, etc. — everything except the heavy free-text
|
|
813
|
+
// fields (description hard-capped, full acceptanceCriteria, full references)
|
|
814
|
+
// — see W-mphejzmj000718bf / W-mq5xg5e9000nec0e. We then hydrate the
|
|
815
|
+
// missing/truncated fields from GET /api/work-items/<id> and re-render in
|
|
816
|
+
// place.
|
|
810
817
|
const needsHydration = !cached.description ||
|
|
818
|
+
cached._descriptionTruncated === true ||
|
|
811
819
|
(cached.acceptanceCriteriaCount > 0 && !Array.isArray(cached.acceptanceCriteria)) ||
|
|
812
820
|
(cached.referencesCount > 0 && !Array.isArray(cached.references));
|
|
813
821
|
|
package/dashboard/js/utils.js
CHANGED
|
@@ -280,7 +280,7 @@ function _renderMdCore(s) {
|
|
|
280
280
|
}).join('\n');
|
|
281
281
|
|
|
282
282
|
html = html.replace(/`([^`\n]+)`/g, function(_, code) {
|
|
283
|
-
codeSlots.push('<code style="background:var(--bg);padding:1px 4px;border-radius:3px;font-size:
|
|
283
|
+
codeSlots.push('<code style="background:var(--bg);padding:1px 4px;border-radius:3px;font-size:var(--text-code)">' + code + '</code>');
|
|
284
284
|
return '\x00CB' + (codeSlots.length - 1) + '\x00';
|
|
285
285
|
});
|
|
286
286
|
|
package/dashboard/slim/body.html
CHANGED
|
@@ -300,10 +300,10 @@
|
|
|
300
300
|
<div class="modal-body">
|
|
301
301
|
<p>Point to a local git repository. Minions will read its remote, name, and main branch automatically.</p>
|
|
302
302
|
<div style="display:flex; gap:8px; align-items:stretch">
|
|
303
|
-
<input id="slim-add-project-path" type="text" placeholder="C:\path\to\repo" style="flex:1; padding:8px 10px; background:var(--bg); border:1px solid var(--border); border-radius:var(--radius); color:var(--text); font-size:
|
|
303
|
+
<input id="slim-add-project-path" type="text" placeholder="C:\path\to\repo" style="flex:1; padding:8px 10px; background:var(--bg); border:1px solid var(--border); border-radius:var(--radius); color:var(--text); font-size:var(--text-md); font-family:inherit">
|
|
304
304
|
<button id="slim-add-project-browse" class="btn-secondary" type="button">Browse…</button>
|
|
305
305
|
</div>
|
|
306
|
-
<div id="slim-add-project-msg" style="margin-top:10px; font-size:
|
|
306
|
+
<div id="slim-add-project-msg" style="margin-top:10px; font-size:var(--text-base); min-height:16px"></div>
|
|
307
307
|
</div>
|
|
308
308
|
<div class="modal-footer">
|
|
309
309
|
<button id="slim-add-project-cancel" class="btn-secondary" type="button">Cancel</button>
|