@yemi33/minions 0.1.1987 → 0.1.1988

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.
@@ -1,41 +1,19 @@
1
1
  // dashboard/js/qa.js — QA tab wiring (W-mpd5ewhj000oc5c5).
2
2
  //
3
- // The QA tab is the canonical home for long-running test/validation surfaces.
4
- // Phase 1 (this WI) mounts the managed-spawn + keep-processes panels into the
5
- // QA page using the shared mount API on render-managed.js + render-other.js.
6
- // The Engine page keeps its own mount registered eagerly inside those modules
7
- // so dual-render works (engine.html and qa.html show the same data, fed by
8
- // the same poll loop in refresh.js — no extra fetches, no extra SSE streams).
3
+ // The QA tab hosts validation runbooks (human-driven and agent-driven) against
4
+ // running managed instances. It does NOT mirror the live-process inventory
5
+ // that lives on /engine (Managed Processes + Keep-Processes panels). Runbook
6
+ // rows in the next WI will link to their targets by name rather than duplicate
7
+ // the inventory tables (W-mpdad3mq000m53bb).
9
8
  //
10
- // SSE log streaming is lazy: openManagedLog() opens a single EventSource on
11
- // user click and closeManagedLog() aborts it. The modal is a singleton, so
12
- // opening from either tab doesn't multiply connections (cf. render-managed.js
13
- // "single-stream invariant").
14
- //
15
- // Out of scope: actual runbook dispatch wiring. The placeholder card with the
16
- // disabled "+ New runbook" button is the only UX hook for the next phase.
9
+ // The only wiring this file owns is the switchPage SSE-close hook: if the
10
+ // user has the managed-log modal (defined in render-managed.js) open on the
11
+ // engine page and navigates away, we close the EventSource so it doesn't
12
+ // keep streaming behind the new page. The hook is harmless when the QA page
13
+ // never opens the modal itself, and matches the broader page-navigation
14
+ // contract for SSE cleanup.
17
15
 
18
16
  (function () {
19
- function _registerQaMounts() {
20
- if (typeof mountManagedProcessesPanel === 'function') {
21
- mountManagedProcessesPanel({
22
- contentId: 'qa-managed-processes-content',
23
- countId: 'qa-managed-processes-count',
24
- });
25
- }
26
- if (typeof mountKeepProcessesPanel === 'function') {
27
- mountKeepProcessesPanel({
28
- contentId: 'qa-keep-processes-content',
29
- countId: 'qa-keep-processes-count',
30
- });
31
- }
32
- }
33
-
34
- // The page fragment is in the DOM at script-load time (all .page divs are
35
- // assembled into layout.html at build), so registering immediately is safe.
36
- // The mount API is no-op when the QA fragment is missing (defensive).
37
- _registerQaMounts();
38
-
39
17
  // Close any open managed-log SSE stream when the user navigates away from a
40
18
  // page that triggered it — the modal otherwise floats over the new page and
41
19
  // the EventSource keeps streaming. Hooks into the existing switchPage()
@@ -48,6 +26,4 @@
48
26
  };
49
27
  window.switchPage.__qaWrapped = true;
50
28
  }
51
-
52
- window.MinionsQA = { _registerQaMounts };
53
29
  })();
@@ -103,16 +103,23 @@ function _processStatusUpdate(data) {
103
103
  prunePrdRequeueState(window._lastWorkItems);
104
104
  if (_changed('engineLog', data.engineLog)) renderEngineLog(data.engineLog || []);
105
105
  if (_changed('metrics', data.metrics)) renderMetrics(data.metrics || {});
106
- // keep_processes panel renders on every page where its mount is in the DOM
107
- // (Engine page + QA page W-mpd5ewhj000oc5c5). Cheap call (one fetch); the
108
- // renderer iterates all registered mounts and skips when none are present.
109
- if (typeof renderKeepProcesses === 'function') {
110
- try { renderKeepProcesses(); } catch {}
111
- }
112
- // managed-processes panel — same mount-point pattern, ETag-gated so
113
- // unchanged ticks return 304 with no body (P-6e2a8b13).
106
+ // managed-processes panel ETag-gated so unchanged ticks return 304 with
107
+ // no body (P-6e2a8b13). Sequenced BEFORE the keep-processes call below via
108
+ // .then() so the keep renderer reads a populated managed-PID cache for
109
+ // dedup (W-mpdad3mq000m53bb). _processStatusUpdate isn't async, so we
110
+ // chain on the returned Promise instead of awaiting.
111
+ let _managedRender = Promise.resolve();
114
112
  if (typeof renderManagedProcesses === 'function') {
115
- try { renderManagedProcesses(); } catch {}
113
+ try { _managedRender = Promise.resolve(renderManagedProcesses()); } catch {}
114
+ }
115
+ // keep_processes panel renders on every page where its mount is in the DOM.
116
+ // Cheap call (one fetch); the renderer iterates all registered mounts, skips
117
+ // when none are present, and suppresses any PID already tracked as a managed
118
+ // process via MinionsManagedProcesses.getLastItems().
119
+ if (typeof renderKeepProcesses === 'function') {
120
+ _managedRender
121
+ .catch(function () { /* keep render even if managed fetch failed — getLastItems() returns the last good cache (or []) */ })
122
+ .then(function () { try { renderKeepProcesses(); } catch {} });
116
123
  }
117
124
  if (_changed('workItems', data.workItems)) renderWorkItems(data.workItems || []);
118
125
  if (_changed('skills', data.skills)) renderSkills(data.skills || []);
@@ -45,6 +45,12 @@ function unmountManagedProcessesPanel(contentId) {
45
45
  }
46
46
  }
47
47
 
48
+ // Read-only accessor for the last successfully-fetched managed-process items.
49
+ // Used by render-other.js renderKeepProcesses() to suppress PIDs that are
50
+ // already tracked as managed processes (W-mpdad3mq000m53bb dedup). Returns
51
+ // the live cache reference — callers must not mutate it.
52
+ function getLastItems() { return _managedProcessesLastItems || []; }
53
+
48
54
  function _fmtAgo(ms) {
49
55
  if (!ms || ms < 0) return '0s';
50
56
  const s = Math.floor(ms / 1000);
@@ -302,4 +308,5 @@ window.MinionsManagedProcesses = {
302
308
  closeManagedLog,
303
309
  mountManagedProcessesPanel,
304
310
  unmountManagedProcessesPanel,
311
+ getLastItems,
305
312
  };
@@ -529,15 +529,45 @@ async function renderKeepProcesses() {
529
529
  } catch (e) {
530
530
  fetchErr = e;
531
531
  }
532
+ // Dedup against managed-processes: agents that declare a process via
533
+ // managed-spawn.json AND leave it running via keep_processes show up in
534
+ // both panels. Managed is canonical (engine owns the lifecycle), so we
535
+ // suppress any PID already tracked there from the keep-processes table
536
+ // (W-mpdad3mq000m53bb).
537
+ const managed = (window.MinionsManagedProcesses && typeof window.MinionsManagedProcesses.getLastItems === 'function')
538
+ ? window.MinionsManagedProcesses.getLastItems()
539
+ : [];
540
+ const managedPidSet = new Set(
541
+ (Array.isArray(managed) ? managed : [])
542
+ .map(m => Number(m && m.pid))
543
+ .filter(n => Number.isFinite(n))
544
+ );
545
+ let rawCount = 0;
546
+ let filtered = items;
547
+ if (!fetchErr && items) {
548
+ rawCount = items.length;
549
+ filtered = [];
550
+ for (const it of items) {
551
+ if (!it.valid) { filtered.push(it); continue; }
552
+ // Shallow clone so we don't mutate the fetched array — caller may
553
+ // re-render the same payload on a 304 tick.
554
+ const pids = Array.isArray(it.pids) ? it.pids.filter(p => !managedPidSet.has(Number(p && p.pid))) : [];
555
+ if (!pids.length) continue;
556
+ filtered.push({ ...it, pids });
557
+ }
558
+ }
532
559
  if (fetchErr) {
533
560
  countText = '?';
534
561
  html = '<span style="color:var(--red)">Failed to load: ' + escHtml(fetchErr.message) + '</span>';
535
- } else if (!items.length) {
562
+ } else if (!filtered.length) {
536
563
  countText = '0';
537
- html = '<p class="empty">No agents have left processes running. Set <code>meta.keep_processes: true</code> on a work item to enable.</p>';
564
+ const baseEmpty = '<p class="empty">No agents have left processes running. Set <code>meta.keep_processes: true</code> on a work item to enable.</p>';
565
+ html = (rawCount > 0)
566
+ ? baseEmpty.replace('</p>', ' <span style="color:var(--muted)">(all PIDs are tracked as managed processes above)</span></p>')
567
+ : baseEmpty;
538
568
  } else {
539
- countText = String(items.length);
540
- html = items.map(function (it) {
569
+ countText = String(filtered.length);
570
+ html = filtered.map(function (it) {
541
571
  if (!it.valid) {
542
572
  return '<div style="border:1px solid var(--border);border-radius:4px;padding:8px;margin-bottom:8px;background:var(--surface2)">' +
543
573
  '<div style="color:var(--red);font-weight:600">' + escHtml(it.agentId) + ' INVALID</div>' +
@@ -21,13 +21,13 @@
21
21
  </section>
22
22
  <section id="keep-processes-section">
23
23
  <h2>Keep-Processes <span class="count" id="keep-processes-count">0</span>
24
- <span style="font-size:10px;color:var(--muted);font-weight:400;text-transform:none;letter-spacing:0">processes left running by agents (W-mp68q6ke0010de68 — opt-in keep_processes flag)</span>
24
+ <span style="font-size:10px;color:var(--muted);font-weight:400;text-transform:none;letter-spacing:0">processes left running by agents</span>
25
25
  </h2>
26
26
  <div id="keep-processes-content"><p class="empty">No agents have left processes running. Set <code>meta.keep_processes: true</code> on a work item to enable.</p></div>
27
27
  </section>
28
28
  <section id="managed-processes-section">
29
29
  <h2>Managed Processes <span class="count" id="managed-processes-count">0</span>
30
- <span style="font-size:10px;color:var(--muted);font-weight:400;text-transform:none;letter-spacing:0">engine-managed long-running services (P-6e2a8b13 — managed-spawn primitive)</span>
30
+ <span style="font-size:10px;color:var(--muted);font-weight:400;text-transform:none;letter-spacing:0">engine-managed long-running services</span>
31
31
  </h2>
32
32
  <div id="managed-processes-content"><p class="empty">No managed processes. Agents declare them via <code>agents/&lt;id&gt;/managed-spawn.json</code>.</p></div>
33
33
  </section>
@@ -1,18 +1,6 @@
1
1
  <section>
2
- <h2>Live Processes <span style="font-size:10px;color:var(--muted);font-weight:400;text-transform:none;letter-spacing:0">canonical home for managed instances + agent-left processes (W-mpd5ewhj000oc5c5)</span></h2>
3
- <p class="empty" style="margin:4px 0 12px 0">The QA tab is the foundation for human-driven and agent-driven validation against running managed instances. Phase 1 surfaces the live process inventory; runbook dispatch lands in a follow-up WI.</p>
4
- </section>
5
- <section id="qa-managed-processes-section">
6
- <h2>Managed Processes <span class="count" id="qa-managed-processes-count">0</span>
7
- <span style="font-size:10px;color:var(--muted);font-weight:400;text-transform:none;letter-spacing:0">engine-managed long-running services (P-6e2a8b13 — managed-spawn primitive)</span>
8
- </h2>
9
- <div id="qa-managed-processes-content"><p class="empty">No managed processes. Agents declare them via <code>agents/&lt;id&gt;/managed-spawn.json</code>.</p></div>
10
- </section>
11
- <section id="qa-keep-processes-section">
12
- <h2>Keep-Processes <span class="count" id="qa-keep-processes-count">0</span>
13
- <span style="font-size:10px;color:var(--muted);font-weight:400;text-transform:none;letter-spacing:0">processes left running by agents (W-mp68q6ke0010de68 — opt-in keep_processes flag)</span>
14
- </h2>
15
- <div id="qa-keep-processes-content"><p class="empty">No agents have left processes running. Set <code>meta.keep_processes: true</code> on a work item to enable.</p></div>
2
+ <h2>QA</h2>
3
+ <p class="empty" style="margin:4px 0 12px 0">Canonical home for human-driven and agent-driven validation against running managed instances. Runbook dispatch lands in a follow-up WI.</p>
16
4
  </section>
17
5
  <section id="qa-runbooks-section">
18
6
  <h2>Validation Runbooks <span style="font-size:10px;color:var(--muted);font-weight:400;text-transform:none;letter-spacing:0">human or agent-driven smoke / E2E flows against the live instances above</span></h2>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.1987",
3
+ "version": "0.1.1988",
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"