@yemi33/minions 0.1.2325 → 0.1.2327

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
@@ -9105,6 +9105,29 @@ What would you like to discuss or change? When you're happy, say "approve" and I
9105
9105
  }
9106
9106
  }
9107
9107
 
9108
+ // W-mr5m9inn002d27e1 — safeWrite/safeUnlink can both throw or silently no-op
9109
+ // (disk full, permission error, locked file on Windows). Previously the
9110
+ // archive step's try/catch only logged to console.error while the handler
9111
+ // still returned ok:true, so a failed archive left the note in
9112
+ // notes/inbox/ (risking double-processing) with no operator-visible
9113
+ // signal. Centralize the archive attempt so both call sites report
9114
+ // {archived, archiveError} instead of swallowing the failure.
9115
+ function _archiveInboxNote(inboxPath, archiveDestPath) {
9116
+ try {
9117
+ const _c = safeRead(inboxPath);
9118
+ safeWrite(archiveDestPath, _c);
9119
+ safeUnlink(inboxPath);
9120
+ if (fs.existsSync(inboxPath)) {
9121
+ // safeUnlink swallows its own errors, so a failed removal doesn't
9122
+ // throw — detect it by checking the source file is actually gone.
9123
+ return { archived: false, archiveError: 'inbox note could not be removed after archiving (unlink failed)' };
9124
+ }
9125
+ return { archived: true, archiveError: null };
9126
+ } catch (e) {
9127
+ return { archived: false, archiveError: e.message };
9128
+ }
9129
+ }
9130
+
9108
9131
  async function handleInboxPersist(req, res) {
9109
9132
  try {
9110
9133
  const body = await readBody(req);
@@ -9150,9 +9173,12 @@ What would you like to discuss or change? When you're happy, say "approve" and I
9150
9173
  // Move to archive
9151
9174
  const archiveDir = path.join(MINIONS_DIR, 'notes', 'archive');
9152
9175
  if (!fs.existsSync(archiveDir)) fs.mkdirSync(archiveDir, { recursive: true });
9153
- try { const _c = safeRead(inboxPath); safeWrite(path.join(archiveDir, `persisted-${name}`), _c); safeUnlink(inboxPath); } catch (e) { console.error('inbox archive:', e.message); }
9176
+ const { archived, archiveError } = _archiveInboxNote(inboxPath, path.join(archiveDir, `persisted-${name}`));
9177
+ if (!archived) console.error('inbox archive:', archiveError);
9154
9178
 
9155
- return jsonReply(res, 200, { ok: true, title });
9179
+ return jsonReply(res, 200, archived
9180
+ ? { ok: true, title, archived: true }
9181
+ : { ok: true, title, archived: false, warning: `Persisted to notes.md, but the inbox note could not be archived: ${archiveError}. It remains in notes/inbox and may be reprocessed.` });
9156
9182
  } catch (e) { return jsonReply(res, 400, { error: e.message }); }
9157
9183
  }
9158
9184
 
@@ -9215,9 +9241,12 @@ What would you like to discuss or change? When you're happy, say "approve" and I
9215
9241
  // Move inbox item to archive
9216
9242
  const archiveDir = path.join(MINIONS_DIR, 'notes', 'archive');
9217
9243
  if (!fs.existsSync(archiveDir)) fs.mkdirSync(archiveDir, { recursive: true });
9218
- try { const _c = safeRead(inboxPath); safeWrite(path.join(archiveDir, `kb-${category}-${name}`), _c); safeUnlink(inboxPath); } catch (e) { console.error('inbox archive:', e.message); }
9244
+ const { archived, archiveError } = _archiveInboxNote(inboxPath, path.join(archiveDir, `kb-${category}-${name}`));
9245
+ if (!archived) console.error('inbox archive:', archiveError);
9219
9246
 
9220
- return jsonReply(res, 200, { ok: true, category, file: name });
9247
+ return jsonReply(res, 200, archived
9248
+ ? { ok: true, category, file: name, archived: true }
9249
+ : { ok: true, category, file: name, archived: false, warning: `Promoted to knowledge/${category}/${name}, but the inbox note could not be archived: ${archiveError}. It remains in notes/inbox and may be reprocessed.` });
9221
9250
  } catch (e) { return jsonReply(res, 400, { error: e.message }); }
9222
9251
  }
9223
9252
 
@@ -299,11 +299,24 @@ function _mirrorJsonFromSql(scope, filePath) {
299
299
  }
300
300
  }
301
301
 
302
+ // Drop every SQL row for `scope` and forget its mirror-hash. Called by
303
+ // removeProject after the JSON file is archived so safeJson shim reads no
304
+ // longer surface the removed project's records.
305
+ //
306
+ // Per CLAUDE.md's mutator contract, every mutator wrapper must call
307
+ // emitStateEvent() so the dashboard's MAX(events.id) cache-version
308
+ // advances — otherwise the dashboard keeps serving stale cached
309
+ // pull-requests data for other still-active projects. Emit only when rows
310
+ // were actually deleted, matching the wrote-gated emit convention used by
311
+ // applyPullRequestsMutation's callers (see shared.js mutatePullRequests).
302
312
  function dropScope(scope) {
303
313
  try {
304
314
  const { getDb } = require('./db');
305
- getDb().prepare('DELETE FROM pull_requests WHERE scope = ?').run(scope);
315
+ const result = getDb().prepare('DELETE FROM pull_requests WHERE scope = ?').run(scope);
306
316
  _lastMirrorHashByScope.delete(scope);
317
+ if (result && result.changes > 0) {
318
+ try { require('./db-events').emitStateEvent('pull_requests'); } catch { /* optional */ }
319
+ }
307
320
  } catch { /* db unavailable */ }
308
321
  }
309
322
 
@@ -379,11 +379,22 @@ function _mirrorJsonFromSql(scope, filePath) {
379
379
  // Drop every SQL row for `scope` and forget its mirror-hash. Called by
380
380
  // removeProject after the JSON file is archived so safeJson shim reads no
381
381
  // longer surface the removed project's records.
382
+ //
383
+ // Per CLAUDE.md's mutator contract, every mutator wrapper must call
384
+ // emitStateEvent() so the dashboard's MAX(events.id) cache-version
385
+ // advances — otherwise the dashboard keeps serving stale cached
386
+ // work-items data for other still-active projects. Emit only when rows
387
+ // were actually deleted, matching the wrote-gated emit convention used by
388
+ // applyWorkItemsMutation's callers (see shared.js mutateWorkItems).
382
389
  function dropScope(scope) {
383
390
  try {
384
391
  const { getDb } = require('./db');
385
- getDb().prepare('DELETE FROM work_items WHERE scope = ?').run(scope);
392
+ const result = getDb().prepare('DELETE FROM work_items WHERE scope = ?').run(scope);
386
393
  _lastMirrorHashByScope.delete(scope);
394
+ if (result && result.changes > 0) {
395
+ try { require('./db-events').emitStateEvent('work_items'); } catch { /* optional */ }
396
+ try { require('./queries').invalidateWorkItemsCache(); } catch { /* queries not loaded */ }
397
+ }
387
398
  } catch { /* db unavailable */ }
388
399
  }
389
400
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.2325",
3
+ "version": "0.1.2327",
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"