@yemi33/minions 0.1.2351 → 0.1.2352
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/queries.js +117 -59
- package/package.json +1 -1
package/engine/queries.js
CHANGED
|
@@ -2878,63 +2878,72 @@ function _resolveCommonGitDir(gitDir) {
|
|
|
2878
2878
|
return path.isAbsolute(raw) ? raw : path.resolve(gitDir, raw);
|
|
2879
2879
|
}
|
|
2880
2880
|
|
|
2881
|
-
// Enumerate the per-project git ref files we watch for cache-busting
|
|
2882
|
-
//
|
|
2883
|
-
//
|
|
2884
|
-
//
|
|
2881
|
+
// Enumerate the per-project git ref files we watch for cache-busting, split
|
|
2882
|
+
// into two groups (W-mrb6qnuc000785b2):
|
|
2883
|
+
// identityFiles — HEAD + logs/HEAD, resolved against the PER-WORKTREE
|
|
2884
|
+
// gitdir (`_resolveGitDir`). These are never shared across linked
|
|
2885
|
+
// worktrees of the same repo, so a change here means THIS worktree's
|
|
2886
|
+
// own branch/commit identity moved (opg-microsoft/minions#295, #381) —
|
|
2887
|
+
// the only signal allowed to gate `gitStale: true` / hide the branch
|
|
2888
|
+
// name in the dashboard.
|
|
2889
|
+
// sharedFiles — FETCH_HEAD + refs/remotes/origin/<comparator>, resolved
|
|
2890
|
+
// against the COMMON gitdir (`_resolveCommonGitDir`). These are written
|
|
2891
|
+
// by ANY linked worktree of the same repo doing a `git fetch` — a
|
|
2892
|
+
// legitimate signal that ahead/behind against origin may be stale and
|
|
2893
|
+
// worth recomputing, but NOT a signal that this worktree's own branch
|
|
2894
|
+
// identity changed. Lumping these into the same boolean as identityFiles
|
|
2895
|
+
// produced a permanent "(refreshing...)" chip on repos with several
|
|
2896
|
+
// active worktrees sharing one commondir, since some other worktree's
|
|
2897
|
+
// fetch touches these files constantly.
|
|
2898
|
+
// Same paths as the fast-state mtime tracker so callers see a coherent view
|
|
2899
|
+
// across surfaces.
|
|
2885
2900
|
function _projectGitRefFiles(localPath, configuredMainBranch) {
|
|
2886
2901
|
const gitDir = _resolveGitDir(localPath);
|
|
2887
2902
|
if (!gitDir) return null;
|
|
2888
2903
|
const commonGitDir = _resolveCommonGitDir(gitDir);
|
|
2889
|
-
const
|
|
2904
|
+
const identityFiles = [
|
|
2890
2905
|
// opg-microsoft/minions#381 — track HEAD directly so a bare `git checkout`
|
|
2891
2906
|
// (which rewrites .git/HEAD but may not advance logs/HEAD when reflog is
|
|
2892
2907
|
// disabled or when the snapshot already reflects the post-checkout mtime)
|
|
2893
|
-
// still triggers
|
|
2908
|
+
// still triggers identityAdvanced=true and the gitStale marker on the
|
|
2909
|
+
// next call.
|
|
2894
2910
|
path.join(gitDir, 'HEAD'),
|
|
2895
2911
|
path.join(gitDir, 'logs', 'HEAD'),
|
|
2912
|
+
];
|
|
2913
|
+
const sharedFiles = [
|
|
2896
2914
|
path.join(commonGitDir, 'FETCH_HEAD'),
|
|
2897
2915
|
];
|
|
2898
2916
|
const comparator = configuredMainBranch && String(configuredMainBranch).trim();
|
|
2899
2917
|
if (comparator) {
|
|
2900
|
-
|
|
2918
|
+
sharedFiles.push(path.join(commonGitDir, 'refs', 'remotes', 'origin', comparator));
|
|
2901
2919
|
}
|
|
2902
|
-
return
|
|
2920
|
+
return { identityFiles, sharedFiles };
|
|
2903
2921
|
}
|
|
2904
2922
|
|
|
2905
|
-
// Snapshot mtimeMs for each ref file
|
|
2906
|
-
//
|
|
2907
|
-
//
|
|
2908
|
-
//
|
|
2923
|
+
// Snapshot mtimeMs for each ref file, grouped by identity vs shared (see
|
|
2924
|
+
// `_projectGitRefFiles`). Missing files record `null`. Used as the baseline
|
|
2925
|
+
// that the next `getProjectGitStatus` call compares against — inequality,
|
|
2926
|
+
// not threshold-vs-timestamp, so the result is precision-independent
|
|
2927
|
+
// (Windows `Date.now()` can be 15ms coarse while NTFS mtime is
|
|
2909
2928
|
// sub-millisecond, which used to make threshold checks fire spuriously on
|
|
2910
2929
|
// freshly-written files).
|
|
2911
2930
|
function _snapshotProjectGitRefMtimes(localPath, configuredMainBranch) {
|
|
2912
|
-
const
|
|
2913
|
-
if (!
|
|
2914
|
-
const
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2931
|
+
const groups = _projectGitRefFiles(localPath, configuredMainBranch);
|
|
2932
|
+
if (!groups) return null;
|
|
2933
|
+
const statMtime = f => {
|
|
2934
|
+
try { return fs.statSync(f).mtimeMs; }
|
|
2935
|
+
catch { return null; /* ENOENT recorded as null — flipping to present must bust */ }
|
|
2936
|
+
};
|
|
2937
|
+
const identity = Object.create(null);
|
|
2938
|
+
for (const f of groups.identityFiles) identity[f] = statMtime(f);
|
|
2939
|
+
const shared = Object.create(null);
|
|
2940
|
+
for (const f of groups.sharedFiles) shared[f] = statMtime(f);
|
|
2941
|
+
return { identity, shared };
|
|
2920
2942
|
}
|
|
2921
2943
|
|
|
2922
|
-
//
|
|
2923
|
-
//
|
|
2924
|
-
|
|
2925
|
-
// races on Windows. Lets `getProjectGitStatus` bypass its 15s TTL after
|
|
2926
|
-
// `git pull`, `git fetch`, `git checkout`, etc. so the next /api/status
|
|
2927
|
-
// reflects the new HEAD / ahead-behind within one SPA poll instead of
|
|
2928
|
-
// waiting out the TTL (W-mphdmr8c00030124). Cost: 2-3 statSync per call —
|
|
2929
|
-
// well under the 'cheap' budget.
|
|
2930
|
-
function _projectGitRefsAdvancedSince(localPath, configuredMainBranch, snapshot) {
|
|
2931
|
-
// No snapshot yet (legacy entry shape OR first call) — preserve the
|
|
2932
|
-
// current cached value so the TTL-only fast-path still works. A real
|
|
2933
|
-
// change still surfaces on the next /api/status because the fast-state
|
|
2934
|
-
// mtime tracker watches the same files and will bust the upstream cache.
|
|
2935
|
-
if (!snapshot) return false;
|
|
2936
|
-
const current = _snapshotProjectGitRefMtimes(localPath, configuredMainBranch);
|
|
2937
|
-
if (!current) return false;
|
|
2944
|
+
// Compare two flat {file: mtimeMs} maps and return true if ANY tracked
|
|
2945
|
+
// file's mtime (or existence) differs between them.
|
|
2946
|
+
function _refGroupChanged(current, snapshot) {
|
|
2938
2947
|
for (const file of Object.keys(snapshot)) {
|
|
2939
2948
|
if (current[file] !== snapshot[file]) return true;
|
|
2940
2949
|
}
|
|
@@ -2946,16 +2955,54 @@ function _projectGitRefsAdvancedSince(localPath, configuredMainBranch, snapshot)
|
|
|
2946
2955
|
return false;
|
|
2947
2956
|
}
|
|
2948
2957
|
|
|
2958
|
+
// Return `{ identityAdvanced, aheadBehindAdvanced }` describing which
|
|
2959
|
+
// tracked ref groups changed since `snapshot` (W-mrb6qnuc000785b2):
|
|
2960
|
+
// identityAdvanced — THIS worktree's own HEAD/logs/HEAD moved. The only
|
|
2961
|
+
// signal that should gate `gitStale: true`.
|
|
2962
|
+
// aheadBehindAdvanced — identityAdvanced OR the shared commondir files
|
|
2963
|
+
// (FETCH_HEAD / refs/remotes/origin/<comparator>)
|
|
2964
|
+
// changed — from a fetch in ANY linked worktree of
|
|
2965
|
+
// the same repo. Gates nulling ahead/behind and
|
|
2966
|
+
// bypassing the TTL to schedule a refresh, but never
|
|
2967
|
+
// gates gitStale on its own.
|
|
2968
|
+
// Replaces the older single-boolean threshold-vs-cachedTs check that
|
|
2969
|
+
// suffered from `Date.now()`/`mtimeMs` resolution races on Windows. Lets
|
|
2970
|
+
// `getProjectGitStatus` bypass its 15s TTL after `git pull`, `git fetch`,
|
|
2971
|
+
// `git checkout`, etc. so the next /api/status reflects the new HEAD /
|
|
2972
|
+
// ahead-behind within one SPA poll instead of waiting out the TTL
|
|
2973
|
+
// (W-mphdmr8c00030124). Cost: 2-4 statSync per call — well under the
|
|
2974
|
+
// 'cheap' budget.
|
|
2975
|
+
function _projectGitRefsAdvancedSince(localPath, configuredMainBranch, snapshot) {
|
|
2976
|
+
// No snapshot yet (legacy entry shape OR first call) — preserve the
|
|
2977
|
+
// current cached value so the TTL-only fast-path still works. A real
|
|
2978
|
+
// change still surfaces on the next /api/status because the fast-state
|
|
2979
|
+
// mtime tracker watches the same files and will bust the upstream cache.
|
|
2980
|
+
if (!snapshot) return { identityAdvanced: false, aheadBehindAdvanced: false };
|
|
2981
|
+
const current = _snapshotProjectGitRefMtimes(localPath, configuredMainBranch);
|
|
2982
|
+
if (!current) return { identityAdvanced: false, aheadBehindAdvanced: false };
|
|
2983
|
+
const identityAdvanced = _refGroupChanged(current.identity, snapshot.identity);
|
|
2984
|
+
const sharedAdvanced = _refGroupChanged(current.shared, snapshot.shared);
|
|
2985
|
+
return { identityAdvanced, aheadBehindAdvanced: identityAdvanced || sharedAdvanced };
|
|
2986
|
+
}
|
|
2987
|
+
|
|
2949
2988
|
function getProjectGitStatus(localPath, configuredMainBranch = null) {
|
|
2950
2989
|
const norm = String(localPath || '').replace(/\\/g, '/');
|
|
2951
2990
|
if (!norm) return PROJECT_GIT_STATUS_MISSING;
|
|
2952
2991
|
const key = _projectGitStatusCacheKey(localPath, configuredMainBranch);
|
|
2953
2992
|
const now = Date.now();
|
|
2954
2993
|
const cached = _projectGitStatusCache.get(key);
|
|
2955
|
-
// Compute ref-mtime freshness once. Used
|
|
2956
|
-
//
|
|
2957
|
-
//
|
|
2958
|
-
|
|
2994
|
+
// Compute ref-mtime freshness once. Used below to gate the TTL fast-path
|
|
2995
|
+
// AND to decide whether the stale-while-revalidate return should null out
|
|
2996
|
+
// the known-stale ahead/behind counters (#2848) and/or flag gitStale
|
|
2997
|
+
// (opg-microsoft/minions#295, #381) — split so a fetch from an unrelated
|
|
2998
|
+
// worktree sharing this repo's commondir (FETCH_HEAD /
|
|
2999
|
+
// refs/remotes/origin/<comparator>) only refreshes ahead/behind quietly
|
|
3000
|
+
// and never flags THIS project's branch display as stale
|
|
3001
|
+
// (W-mrb6qnuc000785b2).
|
|
3002
|
+
const advancement = cached
|
|
3003
|
+
? _projectGitRefsAdvancedSince(localPath, configuredMainBranch, cached.refMtimes)
|
|
3004
|
+
: { identityAdvanced: false, aheadBehindAdvanced: false };
|
|
3005
|
+
const refsAdvanced = advancement.aheadBehindAdvanced;
|
|
2959
3006
|
// Within TTL: short-circuit ONLY when no tracked git ref has advanced
|
|
2960
3007
|
// past cached.ts. Without the mtime gate, a freshly-pulled repo serves
|
|
2961
3008
|
// the pre-pull ahead/behind counts for up to 15s + one SPA poll (~19s
|
|
@@ -2989,28 +3036,39 @@ function getProjectGitStatus(localPath, configuredMainBranch = null) {
|
|
|
2989
3036
|
if (!cached) return PROJECT_GIT_STATUS_PENDING;
|
|
2990
3037
|
// When tracked git refs have advanced past the cached snapshot, the
|
|
2991
3038
|
// cached ahead/behind counts are known to be stale (e.g. user just ran
|
|
2992
|
-
// `git pull` / `git fetch`
|
|
2993
|
-
//
|
|
2994
|
-
//
|
|
2995
|
-
//
|
|
3039
|
+
// `git pull` / `git fetch` — from THIS worktree or any other worktree
|
|
3040
|
+
// sharing the same repo commondir — and the local checkout is now
|
|
3041
|
+
// current). Returning the cached value as-is would briefly display a
|
|
3042
|
+
// misleading "behind: 8" after the user has already pulled to current —
|
|
3043
|
+
// see yemi33/minions#2848. Null those specific counters so the dashboard
|
|
2996
3044
|
// shows a pending state for ahead/behind until the background probe
|
|
2997
|
-
// settles. TTL-expiry fall-throughs (
|
|
2998
|
-
// prior counters because nothing
|
|
3045
|
+
// settles. TTL-expiry fall-throughs (aheadBehindAdvanced=false) still
|
|
3046
|
+
// return the prior counters because nothing has actually changed there.
|
|
2999
3047
|
//
|
|
3000
|
-
// opg-microsoft/minions#295: gitBranch/gitDirty on the cached
|
|
3001
|
-
// ALSO known-stale
|
|
3002
|
-
//
|
|
3003
|
-
//
|
|
3004
|
-
//
|
|
3005
|
-
//
|
|
3006
|
-
//
|
|
3007
|
-
//
|
|
3008
|
-
//
|
|
3009
|
-
//
|
|
3010
|
-
//
|
|
3011
|
-
//
|
|
3048
|
+
// opg-microsoft/minions#295 / #381: gitBranch/gitDirty on the cached
|
|
3049
|
+
// value are ALSO known-stale, but ONLY when THIS project's OWN
|
|
3050
|
+
// per-worktree HEAD/logs/HEAD moved (identityAdvanced) — e.g. the
|
|
3051
|
+
// operator ran `git switch` / `git pull` directly in a live checkout
|
|
3052
|
+
// outside the dashboard, or a bare `git checkout` rewrote HEAD. A shared
|
|
3053
|
+
// commondir file (FETCH_HEAD / refs/remotes/origin/<comparator>) changing
|
|
3054
|
+
// because some OTHER linked worktree of the same repo fetched is NOT
|
|
3055
|
+
// evidence this project's branch identity changed — gating gitStale on
|
|
3056
|
+
// that shared signal produced a permanently-stuck "(refreshing...)" chip
|
|
3057
|
+
// on repos with several active worktrees (W-mrb6qnuc000785b2). The cached
|
|
3058
|
+
// value still carries the OLD branch name, so a consumer rendering
|
|
3059
|
+
// `gitBranch` as authoritative briefly shows the pre-switch branch as if
|
|
3060
|
+
// current when identityAdvanced fires. We keep the cached
|
|
3061
|
+
// gitBranch/gitDirty on the object (so the next poll can self-correct
|
|
3062
|
+
// without a flash of "missing"), but add a `gitStale: true` marker so
|
|
3063
|
+
// render paths can avoid presenting the known-stale branch/dirty as
|
|
3064
|
+
// current and fall back to a neutral "refreshing" presentation. The async
|
|
3065
|
+
// refresh scheduled above self-corrects on the next poll. We do NOT add a
|
|
3066
|
+
// synchronous git probe here — status rebuild must never block the event
|
|
3067
|
+
// loop.
|
|
3012
3068
|
if (refsAdvanced) {
|
|
3013
|
-
|
|
3069
|
+
const next = { ...cached.value, ahead: null, behind: null };
|
|
3070
|
+
if (advancement.identityAdvanced) next.gitStale = true;
|
|
3071
|
+
return next;
|
|
3014
3072
|
}
|
|
3015
3073
|
return cached.value;
|
|
3016
3074
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2352",
|
|
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"
|