@yemi33/minions 0.1.2332 → 0.1.2334
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/engine/projects.js +34 -1
- package/engine/pull-requests-store.js +12 -1
- package/engine/work-items-store.js +15 -1
- package/package.json +1 -1
package/engine/projects.js
CHANGED
|
@@ -125,7 +125,10 @@ function _requeueProjectlessCentralWorkItems(itemIds) {
|
|
|
125
125
|
* @param {object} [options]
|
|
126
126
|
* @param {'archive'|'keep'|'purge'} [options.dataMode='archive']
|
|
127
127
|
* archive: move projects/<name>/ to projects/.archived/<name>-YYYYMMDD/
|
|
128
|
-
* keep: leave projects/<name>/ in place
|
|
128
|
+
* keep: leave projects/<name>/ in place, but tombstone work-items.json /
|
|
129
|
+
* pull-requests.json into projects/<name>/.dropped/ so dropScope()
|
|
130
|
+
* (step 9) isn't immediately undone by the *-store.js resync-on-
|
|
131
|
+
* read path re-hydrating the still-present JSON (W-mr96oira000g979a)
|
|
129
132
|
* purge: rm -rf projects/<name>/
|
|
130
133
|
* @returns {object} summary { ok, project, cancelledItems, killedAgents,
|
|
131
134
|
* drainedDispatches, cleanedWorktrees, disabledSchedules, archivedTo,
|
|
@@ -145,6 +148,7 @@ function removeProject(target, options = {}) {
|
|
|
145
148
|
pipelineRefs: [],
|
|
146
149
|
archivedTo: null,
|
|
147
150
|
purgedDataDir: false,
|
|
151
|
+
tombstonedDataFiles: false,
|
|
148
152
|
// P-bfa2d-remove-project-cleanup — soft-delete tag counts for entries in
|
|
149
153
|
// the three central state files that survived the hard-cancel passes
|
|
150
154
|
// (steps 1, 2). See step 7.5 for the orphan-tag policy rationale.
|
|
@@ -427,6 +431,35 @@ function removeProject(target, options = {}) {
|
|
|
427
431
|
summary.archiveError = e.code ? `${e.code}: ${e.message}` : e.message;
|
|
428
432
|
summary.warnings.push('archive data dir: ' + e.message);
|
|
429
433
|
}
|
|
434
|
+
} else if (dataMode === 'keep') {
|
|
435
|
+
// W-mr96oira000g979a — 'keep' leaves projects/<name>/ (and therefore
|
|
436
|
+
// work-items.json / pull-requests.json) at its normal, live-resolving
|
|
437
|
+
// path. dropScope() below DELETEs the SQL rows for this scope, but the
|
|
438
|
+
// *-store.js resync-on-read path (_resyncScopeIfJsonDiverged) treats a
|
|
439
|
+
// scope with an empty SQL table + a still-present JSON file as a
|
|
440
|
+
// fresh-install first-touch and re-hydrates straight from that JSON —
|
|
441
|
+
// silently undoing the drop the very next time anything reads this
|
|
442
|
+
// scope. Tombstone the two live-state files into a `.dropped/`
|
|
443
|
+
// subdirectory so `_filePathForScope(scope)` (and the read-side
|
|
444
|
+
// resync/fallback logic keyed off it) no longer resolves to them,
|
|
445
|
+
// while keeping the historical data on disk for manual inspection —
|
|
446
|
+
// exactly what 'keep' promises, minus the auto-resurrection.
|
|
447
|
+
try {
|
|
448
|
+
const droppedDir = path.join(dataDir, '.dropped');
|
|
449
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
450
|
+
for (const fname of ['work-items.json', 'pull-requests.json']) {
|
|
451
|
+
const src = path.join(dataDir, fname);
|
|
452
|
+
if (!fs.existsSync(src)) continue;
|
|
453
|
+
fs.mkdirSync(droppedDir, { recursive: true });
|
|
454
|
+
let dest = path.join(droppedDir, `${fname}.${stamp}`);
|
|
455
|
+
let n = 1;
|
|
456
|
+
while (fs.existsSync(dest)) dest = path.join(droppedDir, `${fname}.${stamp}-${++n}`);
|
|
457
|
+
_renameWithRetry(src, dest);
|
|
458
|
+
summary.tombstonedDataFiles = true;
|
|
459
|
+
}
|
|
460
|
+
} catch (e) {
|
|
461
|
+
summary.warnings.push('tombstone kept data files: ' + e.message);
|
|
462
|
+
}
|
|
430
463
|
}
|
|
431
464
|
}
|
|
432
465
|
|
|
@@ -313,7 +313,18 @@ function dropScope(scope) {
|
|
|
313
313
|
try {
|
|
314
314
|
const { getDb } = require('./db');
|
|
315
315
|
const result = getDb().prepare('DELETE FROM pull_requests WHERE scope = ?').run(scope);
|
|
316
|
-
|
|
316
|
+
// Mirror the work-items-store.js fix (W-mr96m3hm000fd5d7): don't blindly
|
|
317
|
+
// forget the mirror-hash. _resyncScopeIfJsonDiverged treats "no recorded
|
|
318
|
+
// hash" + "zero SQL rows" as a first-install hydrate and rebuilds SQL from
|
|
319
|
+
// whatever JSON is on disk. removeProject with dataMode:'keep' leaves this
|
|
320
|
+
// scope's JSON file untouched, so forgetting the hash here would let the
|
|
321
|
+
// very next read/write resurrect the just-dropped project's stale PR rows.
|
|
322
|
+
// Adopt the file's current hash instead (or forget it only if the file is
|
|
323
|
+
// actually gone, e.g. dataMode: 'purge'/'archive').
|
|
324
|
+
const jsonPath = _filePathForScope(scope);
|
|
325
|
+
const currentHash = _fileContentHash(jsonPath);
|
|
326
|
+
if (currentHash == null) _lastMirrorHashByScope.delete(scope);
|
|
327
|
+
else _lastMirrorHashByScope.set(scope, currentHash);
|
|
317
328
|
if (result && result.changes > 0) {
|
|
318
329
|
try { require('./db-events').emitStateEvent('pull_requests'); } catch { /* optional */ }
|
|
319
330
|
}
|
|
@@ -390,7 +390,21 @@ function dropScope(scope) {
|
|
|
390
390
|
try {
|
|
391
391
|
const { getDb } = require('./db');
|
|
392
392
|
const result = getDb().prepare('DELETE FROM work_items WHERE scope = ?').run(scope);
|
|
393
|
-
|
|
393
|
+
// Do NOT simply forget the mirror-hash here. _resyncScopeIfJsonDiverged
|
|
394
|
+
// treats "no recorded hash" + "zero SQL rows for the scope" as a genuine
|
|
395
|
+
// first-install hydrate and rebuilds SQL from whatever JSON is on disk.
|
|
396
|
+
// When the caller is removeProject with dataMode:'keep', the JSON file for
|
|
397
|
+
// this scope is deliberately left untouched — so the very next read/write
|
|
398
|
+
// against this scope would silently resurrect the just-dropped project's
|
|
399
|
+
// stale rows straight back into SQL. Instead, adopt the JSON file's
|
|
400
|
+
// *current* hash (if it still exists) so the next resync correctly reads
|
|
401
|
+
// that as "unchanged" and skips the rehydrate. If the file is genuinely
|
|
402
|
+
// gone (dataMode: 'purge'/'archive'), forgetting the hash is safe since
|
|
403
|
+
// _resyncScopeIfJsonDiverged no-ops when the file can't be hashed.
|
|
404
|
+
const jsonPath = _filePathForScope(scope);
|
|
405
|
+
const currentHash = _fileContentHash(jsonPath);
|
|
406
|
+
if (currentHash == null) _lastMirrorHashByScope.delete(scope);
|
|
407
|
+
else _lastMirrorHashByScope.set(scope, currentHash);
|
|
394
408
|
if (result && result.changes > 0) {
|
|
395
409
|
try { require('./db-events').emitStateEvent('work_items'); } catch { /* optional */ }
|
|
396
410
|
try { require('./queries').invalidateWorkItemsCache(); } catch { /* queries not loaded */ }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2334",
|
|
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"
|