@yemi33/minions 0.1.2340 → 0.1.2341

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.
@@ -166,6 +166,18 @@ function _liveValidationTypesArray(p) {
166
166
  return arr.filter(function(t) { return typeof t === 'string' && t.length > 0; });
167
167
  }
168
168
 
169
+ // Expose the hybrid constants + normalizer on window so the Settings page
170
+ // (dashboard/js/settings.js) can reuse the SAME single source of truth for the
171
+ // hybrid work-item-type list, default type, and liveValidationType normalizer
172
+ // rather than duplicating them (CLAUDE.md "no duplicate solutions"). Both files
173
+ // concatenate into one dashboard <script> scope, but attaching to window makes
174
+ // the cross-file dependency explicit and order-independent.
175
+ try {
176
+ window.CHECKOUT_MODE_HYBRID_TYPES = CHECKOUT_MODE_HYBRID_TYPES;
177
+ window.CHECKOUT_MODE_HYBRID_DEFAULT_TYPE = CHECKOUT_MODE_HYBRID_DEFAULT_TYPE;
178
+ window._liveValidationTypesArray = _liveValidationTypesArray;
179
+ } catch (e) { /* non-browser (unit test require) */ }
180
+
169
181
  function _closeCheckoutModeMenu() {
170
182
  const existing = document.getElementById('checkout-mode-menu');
171
183
  if (existing) existing.remove();
@@ -276,18 +276,55 @@ async function openSettings() {
276
276
  // where worktrees are unworkable. p.checkoutMode is resolved server-side
277
277
  // (honors the legacy worktreeMode field). Chip is hidden by default and
278
278
  // toggled reactively below.
279
+ // W-mr9mx93d000x1dda — third option "Hybrid dispatch": checkoutMode:'live'
280
+ // PLUS a liveValidation block (coding WIs author in isolated worktrees; only
281
+ // the chosen validation type(s) run in-place on the live checkout, with an
282
+ // optional auto-dispatch-after-coding toggle). This mirrors the Projects-bar
283
+ // picker (render-other.js _openCheckoutModeMenu / _renderCheckoutModeHybridStep)
284
+ // so both surfaces configure the SAME liveValidation shape identically.
279
285
  var currentWtMode = (p.checkoutMode === 'live') ? 'live' : 'worktree';
280
- var wtModeSearch = 'checkout mode worktree isolated live dispatch';
286
+ var lvTypes = (window._liveValidationTypesArray ? window._liveValidationTypesArray(p) : []);
287
+ // 3-way UI mode: worktree / live / hybrid. Hybrid = live + >=1 validation type.
288
+ var currentCheckoutMode = (currentWtMode === 'live')
289
+ ? (lvTypes.length > 0 ? 'hybrid' : 'live')
290
+ : 'worktree';
291
+ var hybridTypes = window.CHECKOUT_MODE_HYBRID_TYPES || [];
292
+ var hybridDefaultType = window.CHECKOUT_MODE_HYBRID_DEFAULT_TYPE || 'test';
293
+ var hybridPreselect = lvTypes.length > 0 ? lvTypes : [hybridDefaultType];
294
+ var hybridAutoPre = p.liveValidationAutoDispatch === true;
295
+ var wtModeSearch = 'checkout mode worktree isolated live hybrid dispatch validation';
296
+ var hybridTypeChecks = hybridTypes.map(function(t) {
297
+ var checkedAttr = hybridPreselect.indexOf(t) !== -1 ? ' checked' : '';
298
+ return '<label style="display:inline-flex;align-items:center;gap:4px;margin:2px 10px 2px 0;font-size:var(--text-sm);color:var(--text)">' +
299
+ '<input type="checkbox" data-live-validation-type="' + escHtml(p.name) + '" value="' + escHtml(t) + '"' + checkedAttr + '>' +
300
+ '<span>' + escHtml(t) + '</span>' +
301
+ '</label>';
302
+ }).join('');
303
+ var hybridPanel =
304
+ '<div data-checkout-hybrid-panel="' + escHtml(p.name) + '" style="' + (currentCheckoutMode === 'hybrid' ? '' : 'display:none;') + 'margin-top:6px">' +
305
+ '<div style="padding:6px 8px;background:rgba(234,179,8,0.12);border:1px solid var(--yellow);border-radius:4px;color:var(--yellow);font-size:var(--text-xs);line-height:1.4;margin-bottom:6px">' +
306
+ '⚠ Hybrid mode: coding work items author in isolated worktrees; only the chosen validation type(s) run in-place on the live checkout (one mutating dispatch at a time; refused on a dirty tree).' +
307
+ '</div>' +
308
+ '<div style="font-size:var(--text-sm);color:var(--muted);margin-bottom:2px">Work-item type(s) to validate live:</div>' +
309
+ '<div style="margin-bottom:4px">' + hybridTypeChecks + '</div>' +
310
+ '<label style="display:inline-flex;align-items:center;gap:6px;font-size:var(--text-sm);color:var(--text);margin-top:2px">' +
311
+ '<input type="checkbox" id="set-liveValidationAutoDispatch-' + escHtml(p.name) + '"' + (hybridAutoPre ? ' checked' : '') + '>' +
312
+ '<span>Automatically validate after each coding work item completes</span>' +
313
+ '</label>' +
314
+ '<div data-checkout-hybrid-error="' + escHtml(p.name) + '" style="display:none;margin-top:6px;color:var(--red);font-size:var(--text-xs)">Select at least one work-item type to validate live.</div>' +
315
+ '</div>';
281
316
  var worktreeModeBlock =
282
317
  '<div data-search="' + escHtml(wtModeSearch) + '" style="margin-bottom:6px">' +
283
318
  '<label style="font-size:var(--text-sm);color:var(--muted);display:block;margin-bottom:2px">Checkout mode</label>' +
284
319
  '<select id="set-checkoutMode-' + escHtml(p.name) + '" data-checkout-mode-select="' + escHtml(p.name) + '" style="width:100%;padding:4px 6px;background:var(--surface);border:1px solid var(--border);border-radius:4px;color:var(--text);font-size:var(--text-md)">' +
285
- '<option value="worktree" title="Each dispatch runs in its own isolated git worktree. Fully parallel, never touches your working directory."' + (currentWtMode === 'worktree' ? ' selected' : '') + '>Worktree (default)</option>' +
286
- '<option value="live" title="Agents run in-place in the project folder. One mutating dispatch at a time; refuses to run on a dirty tree."' + (currentWtMode === 'live' ? ' selected' : '') + '>Live checkout</option>' +
320
+ '<option value="worktree" title="Each dispatch runs in its own isolated git worktree. Fully parallel, never touches your working directory."' + (currentCheckoutMode === 'worktree' ? ' selected' : '') + '>Worktree (default)</option>' +
321
+ '<option value="live" title="Agents run in-place in the project folder. One mutating dispatch at a time; refuses to run on a dirty tree."' + (currentCheckoutMode === 'live' ? ' selected' : '') + '>Live checkout</option>' +
322
+ '<option value="hybrid" title="Coding work authors in isolated worktrees; only a chosen validation step runs on the real checkout."' + (currentCheckoutMode === 'hybrid' ? ' selected' : '') + '>Hybrid dispatch</option>' +
287
323
  '</select>' +
288
- '<div data-checkout-mode-chip="' + escHtml(p.name) + '" style="' + (currentWtMode === 'live' ? '' : 'display:none;') + 'margin-top:6px;padding:6px 8px;background:rgba(234,179,8,0.12);border:1px solid var(--yellow);border-radius:4px;color:var(--yellow);font-size:var(--text-xs);line-height:1.4">' +
324
+ '<div data-checkout-mode-chip="' + escHtml(p.name) + '" style="' + (currentCheckoutMode === 'live' ? '' : 'display:none;') + 'margin-top:6px;padding:6px 8px;background:rgba(234,179,8,0.12);border:1px solid var(--yellow);border-radius:4px;color:var(--yellow);font-size:var(--text-xs);line-height:1.4">' +
289
325
  '⚠ Live mode: dispatches run directly in this repo\'s checkout. Only one mutating dispatch runs at a time. Dirty working trees block dispatch — commit or stash before running.' +
290
326
  '</div>' +
327
+ hybridPanel +
291
328
  '</div>';
292
329
  // W-mqtvnnj1000357fa — per-project live-checkout auto-stash override.
293
330
  // Tri-state: "" (use fleet default → engine.liveCheckoutAutoStash), "on"
@@ -715,12 +752,17 @@ async function openSettings() {
715
752
  // chip is rendered once with display:none for worktree-mode projects and
716
753
  // visible for already-live projects; this handler only flips the
717
754
  // display style — no markup is regenerated.
755
+ // W-mr9mx93d000x1dda — also reveal/hide the Hybrid sub-panel: the live chip
756
+ // shows for 'live', the hybrid panel (type checkboxes + auto-validate) shows
757
+ // for 'hybrid', neither for 'worktree'.
718
758
  document.querySelectorAll('[data-checkout-mode-select]').forEach(function(sel) {
719
759
  sel.addEventListener('change', function() {
720
760
  const projName = sel.getAttribute('data-checkout-mode-select');
721
- const chip = document.querySelector('[data-checkout-mode-chip="' + (window.CSS && CSS.escape ? CSS.escape(projName) : projName) + '"]');
722
- if (!chip) return;
723
- chip.style.display = (sel.value === 'live') ? '' : 'none';
761
+ const esc = (window.CSS && CSS.escape ? CSS.escape(projName) : projName);
762
+ const chip = document.querySelector('[data-checkout-mode-chip="' + esc + '"]');
763
+ if (chip) chip.style.display = (sel.value === 'live') ? '' : 'none';
764
+ const hybridPanel = document.querySelector('[data-checkout-hybrid-panel="' + esc + '"]');
765
+ if (hybridPanel) hybridPanel.style.display = (sel.value === 'hybrid') ? '' : 'none';
724
766
  });
725
767
  });
726
768
  }
@@ -1165,18 +1207,48 @@ async function saveSettings() {
1165
1207
  });
1166
1208
 
1167
1209
  const currentProjects = (_settingsData && Array.isArray(_settingsData.projects)) ? _settingsData.projects : [];
1210
+ // W-mr9mx93d000x1dda — collect per-project hybrid validation errors so a
1211
+ // Hybrid selection with zero checked types blocks the whole save with an
1212
+ // inline error (mirrors the Projects-bar picker's "require >=1 type" guard)
1213
+ // rather than silently POSTing an empty/absent liveValidation.
1214
+ const hybridValidationErrors = [];
1168
1215
  const projectsPayload = currentProjects.map(function(p) {
1169
1216
  // W-mpg3whgp000d09ec / #2732 — mainBranch is now editable from Settings →
1170
1217
  // Projects. Empty string = clear the override; the field stays optional.
1171
1218
  const mainBranchInput = document.getElementById('set-mainBranch-' + p.name);
1172
1219
  const mainBranchValue = mainBranchInput ? mainBranchInput.value.trim() : (p.mainBranch || '');
1173
1220
  // P-a3f9b207 (consolidated W-mqiaw974) — per-project checkoutMode.
1174
- // Normalize to 'worktree' for any value other than 'live' so a stale DOM
1175
- // never POSTs garbage; the server-side validator
1176
- // (shared.validateCheckoutMode) is the authoritative gate for unknown
1177
- // values.
1221
+ // W-mr9mx93d000x1dda the select now carries THREE UI values:
1222
+ // 'worktree' | 'live' | 'hybrid'. Both 'live' and 'hybrid' persist as the
1223
+ // server enum checkoutMode:'live'; hybrid additionally carries a
1224
+ // liveValidation block. Any unknown value normalizes to 'worktree' so a
1225
+ // stale DOM never POSTs garbage (shared.validateCheckoutMode /
1226
+ // shared.validateLiveValidation are the authoritative server gates).
1178
1227
  const wtModeInput = document.getElementById('set-checkoutMode-' + p.name);
1179
- const wtModeValue = (wtModeInput && wtModeInput.value === 'live') ? 'live' : 'worktree';
1228
+ const uiMode = wtModeInput ? wtModeInput.value : 'worktree';
1229
+ const isHybrid = uiMode === 'hybrid';
1230
+ const checkoutMode = (uiMode === 'live' || uiMode === 'hybrid') ? 'live' : 'worktree';
1231
+ // liveValidation: an explicit block for hybrid, else explicit null. Always
1232
+ // sending the key (never omitting it) matches the Projects-bar picker
1233
+ // (_applyCheckoutModeChange) contract EXACTLY and guarantees a Settings
1234
+ // save can never silently leave a stale hybrid block on a project the
1235
+ // operator just moved to Worktrees/Live checkout.
1236
+ let liveValidation = null;
1237
+ if (isHybrid) {
1238
+ const esc = (window.CSS && CSS.escape ? CSS.escape(p.name) : p.name);
1239
+ const typeEls = document.querySelectorAll('[data-live-validation-type="' + esc + '"]');
1240
+ const types = [];
1241
+ typeEls.forEach(function(el) { if (el && el.checked) types.push(el.value); });
1242
+ const errEl = document.querySelector('[data-checkout-hybrid-error="' + esc + '"]');
1243
+ if (types.length === 0) {
1244
+ hybridValidationErrors.push(p.name);
1245
+ if (errEl) errEl.style.display = '';
1246
+ } else {
1247
+ if (errEl) errEl.style.display = 'none';
1248
+ const autoEl = document.getElementById('set-liveValidationAutoDispatch-' + p.name);
1249
+ liveValidation = { type: types, autoDispatch: !!(autoEl && autoEl.checked) };
1250
+ }
1251
+ }
1180
1252
  // W-mqtvnnj1000357fa — per-project live-checkout auto-stash override.
1181
1253
  // '' → null (clear override, fall back to engine fleet setting); 'on' →
1182
1254
  // true; 'off' → false. The server deletes the field on null and validates
@@ -1187,7 +1259,8 @@ async function saveSettings() {
1187
1259
  return {
1188
1260
  name: p.name,
1189
1261
  mainBranch: mainBranchValue || null,
1190
- checkoutMode: wtModeValue,
1262
+ checkoutMode: checkoutMode,
1263
+ liveValidation: liveValidation,
1191
1264
  liveCheckoutAutoStash: autoStashValue,
1192
1265
  workSources: {
1193
1266
  pullRequests: { enabled: document.getElementById('set-ws-prs-' + p.name)?.checked ?? true },
@@ -1196,6 +1269,10 @@ async function saveSettings() {
1196
1269
  };
1197
1270
  });
1198
1271
 
1272
+ if (hybridValidationErrors.length > 0) {
1273
+ throw new Error('Hybrid dispatch needs at least one validation work-item type for: ' + hybridValidationErrors.join(', '));
1274
+ }
1275
+
1199
1276
  // Save config
1200
1277
  const res = await fetch('/api/settings', {
1201
1278
  method: 'POST', headers: { 'Content-Type': 'application/json' },
package/dashboard.js CHANGED
@@ -11037,6 +11037,15 @@ What would you like to discuss or change? When you're happy, say "approve" and I
11037
11037
  // the per-project dropdown. resolveCheckoutMode honors the legacy
11038
11038
  // worktreeMode field; 'worktree' (default) or 'live'.
11039
11039
  checkoutMode: shared.resolveCheckoutMode(p),
11040
+ // W-mr9mx93d000x1dda — surface the hybrid liveValidation config
11041
+ // (type(s) + autoDispatch) so the Settings → Projects checkout-mode
11042
+ // control can pre-fill its Hybrid sub-panel authoritatively (mirrors
11043
+ // the /api/status project mapping the Projects-bar picker reads).
11044
+ // Only meaningful under checkoutMode:'live'; null/false otherwise.
11045
+ liveValidationType: (shared.resolveCheckoutMode(p) === 'live' && p.liveValidation && p.liveValidation.type)
11046
+ ? p.liveValidation.type
11047
+ : null,
11048
+ liveValidationAutoDispatch: !!(shared.resolveCheckoutMode(p) === 'live' && p.liveValidation && p.liveValidation.autoDispatch === true),
11040
11049
  // W-mqtvnnj1000357fa — surface the RAW per-project auto-stash override
11041
11050
  // (true / false / undefined) so the Settings UI can pre-select the
11042
11051
  // tri-state dropdown. Undefined means "use fleet default"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.2340",
3
+ "version": "0.1.2341",
4
4
  "description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
5
5
  "bin": {
6
6
  "minions": "bin/minions.js"