@yemi33/minions 0.1.2301 → 0.1.2302

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/dashboard.js CHANGED
@@ -4263,12 +4263,25 @@ function _resetPreambleCache() {
4263
4263
  _preambleCacheTs = 0;
4264
4264
  }
4265
4265
 
4266
+ // Compact one-word-ish label for a project's effective dispatch checkout mode,
4267
+ // for CC state surfaces (preamble + refresh). 'worktree' | 'live' |
4268
+ // 'hybrid (live-validation: <type>)' so CC can see at a glance whether a project
4269
+ // authors in isolated worktrees, runs in-place on the operator tree, or splits
4270
+ // the two (coding → worktree, validation type → live). See resolveCheckoutMode.
4271
+ function _projectCheckoutModeLabel(p) {
4272
+ const mode = shared.resolveCheckoutMode(p);
4273
+ if (mode === 'live' && p && p.liveValidation && p.liveValidation.type) {
4274
+ return `hybrid (live-validation: ${p.liveValidation.type})`;
4275
+ }
4276
+ return mode;
4277
+ }
4278
+
4266
4279
  function buildCCStatePreamble() {
4267
4280
  const now = Date.now();
4268
4281
  if (_preambleCache && now - _preambleCacheTs < PREAMBLE_TTL) return _preambleCache;
4269
4282
  // Lightweight snapshot — just enough to orient. Use tools for details.
4270
4283
  const agents = getAgents().map(a => `- ${a.name} (${a.id}): ${a.status}${a.currentTask ? ' — ' + a.currentTask.slice(0, 60) : ''}`).join('\n');
4271
- const projects = PROJECTS.map(p => `- ${p.name}: ${p.localPath}`).join('\n');
4284
+ const projects = PROJECTS.map(p => `- ${p.name}: ${p.localPath} [${_projectCheckoutModeLabel(p)}]`).join('\n');
4272
4285
 
4273
4286
  const dq = getDispatchQueue();
4274
4287
  const active = (dq.active || []).map(d => `- ${d.agentName || d.agent}: ${(d.task || '').slice(0, 50)}`).join('\n') || '(none)';
@@ -4340,7 +4353,7 @@ function buildCCStateRefresh() {
4340
4353
  // Agent roster carries the current task too — without it a resumed session
4341
4354
  // sees only that an agent is "busy", never *what* it is busy with.
4342
4355
  const agents = getAgents().map(a => `- ${a.name}: ${a.status}${a.currentTask ? ' — ' + a.currentTask.slice(0, 50) : ''}`).join('\n');
4343
- const projects = PROJECTS.map(p => `- ${p.name} (${p.repo || p.localPath})`).join('\n');
4356
+ const projects = PROJECTS.map(p => `- ${p.name} (${p.repo || p.localPath}) [${_projectCheckoutModeLabel(p)}]`).join('\n');
4344
4357
 
4345
4358
  // MCP servers — just names + source for orientation
4346
4359
  let mcpLine = '(none discovered)';
@@ -11337,6 +11350,31 @@ What would you like to discuss or change? When you're happy, say "approve" and I
11337
11350
  }
11338
11351
  // Drop the legacy field so a migrated project never carries both.
11339
11352
  delete proj.worktreeMode;
11353
+ // Defensive cleanup: liveValidation is only honored under
11354
+ // checkoutMode:'live'. If this update moves the project OFF live,
11355
+ // drop any stale hybrid block so resolveCheckoutMode doesn't have to
11356
+ // warn-and-ignore an orphaned liveValidation on every resolve.
11357
+ if (shared.resolveCheckoutMode(proj) !== 'live') {
11358
+ delete proj.liveValidation;
11359
+ }
11360
+ }
11361
+ // Hybrid live-validation block (per-project). Only meaningful when the
11362
+ // resolved checkout mode is 'live' — runs AFTER the checkoutMode block
11363
+ // above so resolveCheckoutMode(proj) reflects the intended final mode.
11364
+ // Empty string / null clears the override; any other value flows
11365
+ // through shared.validateLiveValidation which throws HTTP 400 on
11366
+ // malformed input or when checkoutMode !== 'live'. Absent key leaves
11367
+ // any existing block untouched (so a Settings save that omits the
11368
+ // field never silently drops a configured hybrid project).
11369
+ if (Object.prototype.hasOwnProperty.call(update, 'liveValidation')) {
11370
+ const rawLv = update.liveValidation;
11371
+ if (rawLv === '' || rawLv === null || rawLv === undefined) {
11372
+ delete proj.liveValidation;
11373
+ } else {
11374
+ const validatedLv = shared.validateLiveValidation(rawLv, { checkoutMode: shared.resolveCheckoutMode(proj) });
11375
+ if (validatedLv === undefined) delete proj.liveValidation;
11376
+ else proj.liveValidation = validatedLv;
11377
+ }
11340
11378
  }
11341
11379
  }
11342
11380
  }
@@ -14301,7 +14339,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
14301
14339
 
14302
14340
  // Settings
14303
14341
  { method: 'GET', path: '/api/settings', desc: 'Return current engine + claude + routing config', handler: handleSettingsRead },
14304
- { method: 'POST', path: '/api/settings', desc: 'Update engine + claude + agent + projects config', params: 'engine?, claude?, agents?, projects?', handler: handleSettingsUpdate },
14342
+ { method: 'POST', path: '/api/settings', desc: 'Update engine + claude + agent + projects config', params: 'engine?, claude?, agents?, projects? (per-project: checkoutMode, liveValidation, mainBranch, workSources)', handler: handleSettingsUpdate },
14305
14343
  { method: 'POST', path: '/api/settings/routing', desc: 'Update routing.md', params: 'content', handler: handleSettingsRouting },
14306
14344
  { method: 'POST', path: '/api/settings/reset', desc: 'Reset engine + claude + agent settings to defaults', handler: handleSettingsReset },
14307
14345
 
@@ -14512,6 +14550,7 @@ module.exports = {
14512
14550
  _resolveScheduleProjectValue: resolveScheduleProjectValue,
14513
14551
  _collectArchivedWorkItems: collectArchivedWorkItems,
14514
14552
  buildCCStatePreamble,
14553
+ _projectCheckoutModeLabel,
14515
14554
  buildCCStateRefresh,
14516
14555
  _resetRefreshCache,
14517
14556
  _routesAsMeta,
package/engine/shared.js CHANGED
@@ -2757,6 +2757,47 @@ function validateCheckoutMode(value) {
2757
2757
  return value;
2758
2758
  }
2759
2759
 
2760
+ // Validate + normalize a per-project `liveValidation` block (hybrid mode).
2761
+ // Hybrid = checkoutMode:'live' + liveValidation:{ type, autoDispatch }: coding
2762
+ // work items author in isolated worktrees (escaping the live cap) while only
2763
+ // work items whose type === liveValidation.type run in-place on the live
2764
+ // checkout. See resolveCheckoutMode (which routes per work-item type) and
2765
+ // docs/live-checkout-mode.md.
2766
+ //
2767
+ // Returns:
2768
+ // - undefined → caller should clear the field (empty / null / '' input)
2769
+ // - { type, autoDispatch? } normalized object on success
2770
+ // Throws HTTP 400 (via _httpError) on malformed input, or when the effective
2771
+ // checkout mode is not 'live' (liveValidation is meaningless without it — it is
2772
+ // silently ignored at resolve time, so we refuse to persist a misconfiguration).
2773
+ //
2774
+ // `opts.checkoutMode` is the project's effective (resolved) checkout mode; pass
2775
+ // shared.resolveCheckoutMode(project) AFTER any checkoutMode update has been
2776
+ // applied so the gate sees the intended final mode.
2777
+ function validateLiveValidation(value, opts) {
2778
+ if (value === undefined || value === null || value === '') return undefined;
2779
+ if (typeof value !== 'object' || Array.isArray(value)) {
2780
+ throw _httpError(400, 'Invalid liveValidation: must be an object { type, autoDispatch }.');
2781
+ }
2782
+ const checkoutMode = opts && opts.checkoutMode;
2783
+ if (checkoutMode !== CHECKOUT_MODES.LIVE) {
2784
+ throw _httpError(400, 'liveValidation requires checkoutMode "live" (hybrid mode). Set checkoutMode to "live" first, or clear liveValidation.');
2785
+ }
2786
+ const type = typeof value.type === 'string' ? value.type.trim() : '';
2787
+ if (!type) {
2788
+ throw _httpError(400, 'Invalid liveValidation: "type" is required and must be a non-empty string — the work-item type that runs on the live checkout (e.g. "build-and-test").');
2789
+ }
2790
+ const out = { type };
2791
+ // autoDispatch (optional): when true, lifecycle auto-creates a validation WI
2792
+ // of `type` after each coding WI completes with a PR (engine/lifecycle.js
2793
+ // autoDispatchLiveValidationWi). Coerce to a strict boolean; absent leaves it
2794
+ // off so the operator opts in explicitly.
2795
+ if (Object.prototype.hasOwnProperty.call(value, 'autoDispatch')) {
2796
+ out.autoDispatch = !!value.autoDispatch;
2797
+ }
2798
+ return out;
2799
+ }
2800
+
2760
2801
  // ── Engine Defaults ─────────────────────────────────────────────────────────
2761
2802
  // Single source of truth for engine configuration defaults.
2762
2803
  // Used by: engine.js, minions.js (init). config.template.json only has the project schema.
@@ -9192,6 +9233,7 @@ module.exports = {
9192
9233
  validateProjectPath,
9193
9234
  CHECKOUT_MODES,
9194
9235
  validateCheckoutMode,
9236
+ validateLiveValidation,
9195
9237
  resolveCheckoutMode,
9196
9238
  isLiveCheckoutProject,
9197
9239
  resolveLiveCheckoutAutoReset,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.2301",
3
+ "version": "0.1.2302",
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"
@@ -319,6 +319,27 @@ The `X-CC-Turn-Id` header is the audit trail — the handler stamps it onto the
319
319
 
320
320
  **Errors:** if a `curl` returns 4xx/5xx, surface the error text in your reply so the user sees what went wrong. Don't retry blindly — usually the body explains the missing field.
321
321
 
322
+ ## Project checkout modes (worktree / live / hybrid)
323
+
324
+ Every configured project has an effective **checkout mode** — surfaced in your state snapshot next to the project as `[worktree]`, `[live]`, or `[hybrid (live-validation: <type>)]`. It controls where dispatched agents run:
325
+
326
+ - **`worktree`** (default) — each dispatch gets its own isolated `git worktree`. Agents run fully in parallel; nothing touches the operator's working tree. Use for normal repos.
327
+ - **`live`** — agents run **in-place** inside the project's `localPath` (no worktree). The engine caps this to **one mutating dispatch at a time** per project and **refuses on a dirty tree**. Use only when worktrees are unworkable (e.g. Android `repo`, submodules, deep Windows paths, emulators that bind the real checkout).
328
+ - **`hybrid`** — `live` **plus** a `liveValidation: { type, autoDispatch }` block. Coding work items (`implement`/`fix`/`docs`/`decompose`) author in **isolated worktrees** (full parallelism), while **only** work items whose type matches `liveValidation.type` (e.g. `build-and-test`) run **in-place on the live checkout**. This is the best of both: parallel code authoring + a real on-disk build/validation that can't run in a worktree.
329
+
330
+ **When to recommend hybrid:** the project's build/test/validation genuinely cannot run in an isolated worktree (it needs the real checkout — submodules, a `repo`-managed tree, an emulator/dev-server bound to `localPath`, deep-path tooling), **but** you still want coding agents to work in parallel rather than serialize through the single live checkout. If the *whole* workflow must run on the real tree, use plain `live`. If nothing needs the real tree, stay on `worktree`.
331
+
332
+ **Configuring it** (via the projects array on `POST /api/settings` — never hand-edit `config.json`):
333
+ ```bash
334
+ # Switch a project to hybrid mode (live checkout + deferred build-and-test validation)
335
+ curl -s -X POST http://localhost:{{dashboard_port}}/api/settings \
336
+ -H 'Content-Type: application/json' -H 'X-CC-Turn-Id: {{cc_turn_id}}' \
337
+ -d '{"projects":[{"name":"<project>","checkoutMode":"live","liveValidation":{"type":"build-and-test","autoDispatch":true}}]}'
338
+ # Clear hybrid (back to plain live): pass "liveValidation": null
339
+ # Clear live entirely (back to worktree): pass "checkoutMode": "worktree" (also clear liveValidation)
340
+ ```
341
+ The server validates: `liveValidation` requires `checkoutMode:"live"` (400 otherwise) and a non-empty `type`. With `autoDispatch:true`, the engine auto-creates a `<type>` validation WI on the live checkout after each coding WI completes with a PR. **Always confirm the mode switch with the user before applying it** — it changes how every future dispatch on that project runs.
342
+
322
343
  ## GitHub auth
323
344
 
324
345
  We have multiple authed `gh` accounts (`yemi33`, `yemishin_microsoft`) covering three repo scopes (`yemi33/minions`, `yemishin_microsoft/minions`, `opg-microsoft/minions`).