@yemi33/minions 0.1.2449 → 0.1.2450
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/render-work-items.js +8 -4
- package/dashboard.js +18 -7
- package/engine/core/queries.js +83 -34
- package/package.json +1 -1
|
@@ -1238,10 +1238,14 @@ function openWorkItemDetail(id, source) {
|
|
|
1238
1238
|
// — see W-mphejzmj000718bf / W-mq5xg5e9000nec0e. We then hydrate the
|
|
1239
1239
|
// missing/truncated fields from GET /api/work-items/<id> and re-render in
|
|
1240
1240
|
// place.
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1241
|
+
// The bulk /api/work-items list is now a LEAN payload: it carries badges,
|
|
1242
|
+
// status, and the PR link, but NOT the detail-only enrichment fields
|
|
1243
|
+
// (_artifacts / _notes / _model) — those are computed on demand by
|
|
1244
|
+
// GET /api/work-items/<id> to keep the polled list event-loop-safe
|
|
1245
|
+
// (W-mscir7fv000u6321). The detail modal therefore ALWAYS hydrates from the
|
|
1246
|
+
// full record; we still render the cached slim record first so the click
|
|
1247
|
+
// feels instant, then re-render in place once the full record arrives.
|
|
1248
|
+
const needsHydration = true;
|
|
1245
1249
|
|
|
1246
1250
|
const initial = needsHydration ? Object.assign({}, cached, { _descriptionLoading: true }) : cached;
|
|
1247
1251
|
document.getElementById('modal-title').textContent = initial.title || initial.id;
|
package/dashboard.js
CHANGED
|
@@ -15245,18 +15245,29 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
15245
15245
|
builder: () => getAgents(),
|
|
15246
15246
|
});
|
|
15247
15247
|
}},
|
|
15248
|
-
{ method: 'GET', path: '/api/work-items', desc: '
|
|
15248
|
+
{ method: 'GET', path: '/api/work-items', desc: 'Lean SQL-backed work items joined with dispatch state + PR links (_pr/_prUrl) — fresh on every request; description hard-capped + acceptanceCriteria/references replaced with *Count integers (W-mq5xg5e9000nec0e). The expensive detail-only enrichment (_artifacts/_notes/_model) is NOT computed for the list — the detail modal lazy-loads the full record via GET /api/work-items/<id> (W-mscir7fv000u6321). Optional ?limit=&offset= returns a { items, total, hasMore, offset, limit } page (progressive load, W-ms3bbzry000i6612); with no paging params the full array is returned. Pagination is applied to the lean list BEFORE PR link-up so a large history cannot monopolize the event loop.', handler: (req, res) => {
|
|
15249
15249
|
const page = _parsePageParams(req);
|
|
15250
15250
|
return serveFreshJson(req, res, {
|
|
15251
15251
|
tag: 'work-items',
|
|
15252
15252
|
inputs: [CONFIG_PATH],
|
|
15253
15253
|
variant: page ? ('p' + page.offset + '.' + page.limit) : '',
|
|
15254
|
-
// W-
|
|
15255
|
-
//
|
|
15256
|
-
//
|
|
15257
|
-
//
|
|
15258
|
-
|
|
15259
|
-
|
|
15254
|
+
// W-mscir7fv000u6321 — event-loop-safe list build. getWorkItems(enrich:
|
|
15255
|
+
// false) returns the deterministically-ordered lean list (rows + dispatch
|
|
15256
|
+
// cross-ref + sort) with NO O(items × files) _artifacts/_notes scan.
|
|
15257
|
+
// Paginate FIRST, then slim + attach _pr/_prUrl to just the returned
|
|
15258
|
+
// page (or full list when unpaged) via the cheap map-based helper.
|
|
15259
|
+
// W-mq5xg5e9000nec0e — slimWorkItemForList shallow-copies so the cached
|
|
15260
|
+
// lean list from queries.getWorkItems() is never mutated; attach the PR
|
|
15261
|
+
// links to those copies, never the shared cache.
|
|
15262
|
+
builder: () => {
|
|
15263
|
+
const lean = getWorkItems(null, { enrich: false });
|
|
15264
|
+
if (page) {
|
|
15265
|
+
const pageResult = _paginateList(lean, page);
|
|
15266
|
+
pageResult.items = queries.attachWorkItemPrLinks(pageResult.items.map(slimWorkItemForList));
|
|
15267
|
+
return pageResult;
|
|
15268
|
+
}
|
|
15269
|
+
return queries.attachWorkItemPrLinks(lean.map(slimWorkItemForList));
|
|
15270
|
+
},
|
|
15260
15271
|
});
|
|
15261
15272
|
}},
|
|
15262
15273
|
{ method: 'GET', path: '/api/pull-requests', desc: 'Fully-enriched SQL-backed pull requests with URL backfill and project stamps. Optional ?limit=&offset= returns a { items, total, hasMore, offset, limit } page (progressive load, W-ms3bbzry000i6612); with no paging params the full array is byte-identical to legacy.', handler: (req, res) => {
|
package/engine/core/queries.js
CHANGED
|
@@ -2106,6 +2106,82 @@ function invalidateWorkItemsCache() {
|
|
|
2106
2106
|
_workItemsLeanCacheAt = 0;
|
|
2107
2107
|
}
|
|
2108
2108
|
|
|
2109
|
+
// attachWorkItemPrLinks(items, config) — backfill the _pr / _prUrl fields the
|
|
2110
|
+
// /api/work-items LIST rows render (dashboard/js/render-work-items.js `wiRow`).
|
|
2111
|
+
// This is the ONLY cross-reference the list rows need; the far more expensive
|
|
2112
|
+
// _artifacts/_notes/_model enrichment stays detail-only (GET /api/work-items/<id>).
|
|
2113
|
+
//
|
|
2114
|
+
// Root-cause perf note (W-mscir7fv000u6321): the list endpoint used to run the
|
|
2115
|
+
// FULL enrich over the entire work-item history on every 4 s poll, whose
|
|
2116
|
+
// _artifacts/_notes step is an O(items × files) synchronous KB/inbox/archive/
|
|
2117
|
+
// agent-dir scan that hard-froze the single dashboard event loop (~26-30 s
|
|
2118
|
+
// requests at ~2,281 completed items), starving /api/health and Command Center
|
|
2119
|
+
// SSE. Extracting the cheap PR link-up lets the endpoint paginate FIRST and then
|
|
2120
|
+
// attach links to just the page slice. This helper is deliberately map-based —
|
|
2121
|
+
// O(items + prs), never O(items × prs) — and performs NO filesystem access, so
|
|
2122
|
+
// it is safe to call on the full list or a single page. Mutates and returns
|
|
2123
|
+
// `items` (same semantics the enrich block relied on).
|
|
2124
|
+
function attachWorkItemPrLinks(items, config) {
|
|
2125
|
+
if (!Array.isArray(items) || items.length === 0) return items || [];
|
|
2126
|
+
config = config || getConfig();
|
|
2127
|
+
const projects = getProjects(config);
|
|
2128
|
+
const allPrs = getPullRequests(config);
|
|
2129
|
+
// Prebuilt lookups so each item is O(1) instead of scanning allPrs.
|
|
2130
|
+
const prById = new Map();
|
|
2131
|
+
const prsByDisplayId = new Map();
|
|
2132
|
+
const prByProjectPrdItem = new Map();
|
|
2133
|
+
const prByProjectBranch = new Map();
|
|
2134
|
+
for (const p of allPrs) {
|
|
2135
|
+
if (p.id != null && !prById.has(p.id)) prById.set(p.id, p);
|
|
2136
|
+
const disp = shared.getPrDisplayId(p);
|
|
2137
|
+
if (disp != null) {
|
|
2138
|
+
const bucket = prsByDisplayId.get(disp);
|
|
2139
|
+
if (bucket) bucket.push(p); else prsByDisplayId.set(disp, [p]);
|
|
2140
|
+
}
|
|
2141
|
+
if (p._project) {
|
|
2142
|
+
for (const wid of (p.prdItems || [])) {
|
|
2143
|
+
const key = p._project + '\0' + wid;
|
|
2144
|
+
if (!prByProjectPrdItem.has(key)) prByProjectPrdItem.set(key, p);
|
|
2145
|
+
}
|
|
2146
|
+
if (p.branch) {
|
|
2147
|
+
const bkey = p._project + '\0' + p.branch;
|
|
2148
|
+
if (!prByProjectBranch.has(bkey)) prByProjectBranch.set(bkey, p);
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
for (const item of items) {
|
|
2153
|
+
if (item._pr && !item._prUrl) {
|
|
2154
|
+
const project = shared.resolveProjectSource(item.project || item._source, projects, { allowCentral: false }).project || null;
|
|
2155
|
+
const canonicalPrId = shared.getCanonicalPrId(project, item._pr);
|
|
2156
|
+
const displayPrId = shared.getPrDisplayId(item._pr);
|
|
2157
|
+
const exactPr = prById.get(canonicalPrId);
|
|
2158
|
+
const displayMatches = exactPr ? [] : (prsByDisplayId.get(displayPrId) || []);
|
|
2159
|
+
const pr = exactPr || (displayMatches.length === 1 ? displayMatches[0] : null);
|
|
2160
|
+
if (pr) {
|
|
2161
|
+
item._pr = pr.id;
|
|
2162
|
+
item._prUrl = pr.url;
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
if (!item._pr) {
|
|
2166
|
+
// Derive from PR.prdItems (single source of truth), then fall back to
|
|
2167
|
+
// branch identity. The branch fallback heals decomposed-child WIs whose
|
|
2168
|
+
// PR-opening agent only stamped the parent id into prdItems — branch
|
|
2169
|
+
// names (`work/<wi-id>`) are unique per WI within a project, so a
|
|
2170
|
+
// branch hit is as authoritative as a prdItems hit. Scoped to the same
|
|
2171
|
+
// project so cross-repo branch collisions don't false-link.
|
|
2172
|
+
// (W-mpn0b76200044eed)
|
|
2173
|
+
const itemProject = item.project || item._source;
|
|
2174
|
+
const linkedPr = prByProjectPrdItem.get(itemProject + '\0' + item.id)
|
|
2175
|
+
|| (item.branch ? prByProjectBranch.get(itemProject + '\0' + item.branch) : null);
|
|
2176
|
+
if (linkedPr) {
|
|
2177
|
+
item._pr = linkedPr.id;
|
|
2178
|
+
item._prUrl = linkedPr.url;
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
return items;
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2109
2185
|
// getWorkItems(config, { enrich, onlyId })
|
|
2110
2186
|
// enrich (default true): also cross-reference PRs and populate the
|
|
2111
2187
|
// detail-modal _artifacts/_notes fields. The /api/work-items list payload
|
|
@@ -2203,39 +2279,12 @@ function getWorkItems(config, opts) {
|
|
|
2203
2279
|
// Detail-modal enrichment (PR cross-ref + _artifacts/_notes). Skipped for
|
|
2204
2280
|
// enrich:false callers — see the getWorkItems header.
|
|
2205
2281
|
if (enrich && !skipEnrichment) {
|
|
2206
|
-
|
|
2207
|
-
//
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
const canonicalPrId = shared.getCanonicalPrId(project, item._pr);
|
|
2213
|
-
const displayPrId = shared.getPrDisplayId(item._pr);
|
|
2214
|
-
const exactPr = allPrs.find(p => p.id === canonicalPrId);
|
|
2215
|
-
const displayMatches = exactPr ? [] : allPrs.filter(p => shared.getPrDisplayId(p) === displayPrId);
|
|
2216
|
-
const pr = exactPr || (displayMatches.length === 1 ? displayMatches[0] : null);
|
|
2217
|
-
if (pr) {
|
|
2218
|
-
item._pr = pr.id;
|
|
2219
|
-
item._prUrl = pr.url;
|
|
2220
|
-
}
|
|
2221
|
-
}
|
|
2222
|
-
if (!item._pr) {
|
|
2223
|
-
// Derive from PR.prdItems (single source of truth), then fall back to
|
|
2224
|
-
// branch identity. The branch fallback heals decomposed-child WIs whose
|
|
2225
|
-
// PR-opening agent only stamped the parent id into prdItems — branch
|
|
2226
|
-
// names (`work/<wi-id>`) are unique per WI within a project, so a
|
|
2227
|
-
// branch hit is as authoritative as a prdItems hit. Scoped to the same
|
|
2228
|
-
// project so cross-repo branch collisions don't false-link.
|
|
2229
|
-
// (W-mpn0b76200044eed)
|
|
2230
|
-
const itemProject = item.project || item._source;
|
|
2231
|
-
const linkedPr = allPrs.find(p => p._project === itemProject && (p.prdItems || []).includes(item.id))
|
|
2232
|
-
|| (item.branch && allPrs.find(p => p.branch === item.branch && p._project === itemProject));
|
|
2233
|
-
if (linkedPr) {
|
|
2234
|
-
item._pr = linkedPr.id;
|
|
2235
|
-
item._prUrl = linkedPr.url;
|
|
2236
|
-
}
|
|
2237
|
-
}
|
|
2238
|
-
}
|
|
2282
|
+
// Cross-reference with PRs (the _pr/_prUrl LIST-row fields). Extracted to a
|
|
2283
|
+
// shared, map-based helper so the /api/work-items list endpoint can attach
|
|
2284
|
+
// the same links to a PAGINATED slice without the expensive _artifacts/_notes
|
|
2285
|
+
// filesystem scan below (W-mscir7fv000u6321). Narrowed to enrichTargets so an
|
|
2286
|
+
// onlyId detail lookup only links its own row (W-mscmqk6b03ha5673).
|
|
2287
|
+
attachWorkItemPrLinks(enrichTargets, config);
|
|
2239
2288
|
|
|
2240
2289
|
// Populate _artifacts for the work item detail modal
|
|
2241
2290
|
// Build dispatch ID → work item ID lookup from completed dispatches
|
|
@@ -3609,7 +3658,7 @@ module.exports = {
|
|
|
3609
3658
|
getKnowledgeBaseEntries, getKnowledgeBaseEntriesSnapshot, getKnowledgeBaseIndex,
|
|
3610
3659
|
|
|
3611
3660
|
// Work items & PRD
|
|
3612
|
-
getWorkItems, invalidateWorkItemsCache, getPrdInfo,
|
|
3661
|
+
getWorkItems, attachWorkItemPrLinks, invalidateWorkItemsCache, getPrdInfo,
|
|
3613
3662
|
|
|
3614
3663
|
// W-mq5uzmc6001d708f — test hooks for the defensive PR enrichment cache.
|
|
3615
3664
|
_resetPrEnrichmentCacheForTest,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2450",
|
|
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"
|