@yemi33/minions 0.1.2030 → 0.1.2031

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.
@@ -36,13 +36,21 @@ function _detectPageChanges(data) {
36
36
 
37
37
  // Change detection — skip renders for sections that haven't changed since last refresh.
38
38
  //
39
- // RENDER_VERSIONS is the in-process cache-bust knob (R3, W-mpgb0xgc000hf1d3).
40
- // _changed() caches JSON.stringify(value) per key, so when the input data is
41
- // byte-identical between ticks the render is skipped. That's the right call
42
- // 99% of the time, but it has a sharp edge: when a renderer body itself is
43
- // edited (hot-reloaded via dashboard-build.js or a freshly shipped bundle)
44
- // and the input stays the same, the stale render persists forever because
45
- // the cache key still matches. F8 / "projects chip" class of bug.
39
+ // Two-layer cache:
40
+ // 1. `_lastValueByKey` is an O(1) reference-equality precheck: when the server
41
+ // returns 304 Not Modified (refresh.js ~173) we reuse the same `_lastStatusData`
42
+ // reference tick after tick, so every `_changed(key, data.<slice>)` call below
43
+ // would otherwise re-stringify multi-MB slices for nothing. The JSON.stringify
44
+ // path stays as the fallback for the "fresh object, equal contents" case
45
+ // (S3, W-mpgcikfg000g8dc7).
46
+ // 2. RENDER_VERSIONS is the in-process cache-bust knob (R3, W-mpgb0xgc000hf1d3).
47
+ // _changed() caches JSON.stringify(value) per (key, version), so when the
48
+ // input data is byte-identical between ticks the render is skipped. That's
49
+ // the right call 99% of the time, but it has a sharp edge: when a renderer
50
+ // body itself is edited (hot-reloaded via dashboard-build.js or a freshly
51
+ // shipped bundle) and the input stays the same, the stale render persists
52
+ // forever because the cache key still matches. F8 / "projects chip" class
53
+ // of bug.
46
54
  //
47
55
  // Bump the matching entry below whenever a renderer's *output* may change
48
56
  // for the same input. Cross-restart safety lives in the dashboardBuildId
@@ -74,15 +82,24 @@ const RENDER_VERSIONS = {
74
82
  pinned: 1,
75
83
  };
76
84
  const _sectionCache = {};
85
+ const _lastValueByKey = {};
77
86
  const _sectionCacheVersions = {};
78
87
  function _changed(key, value, version) {
79
88
  var v = version == null ? (RENDER_VERSIONS[key] || 0) : version;
80
89
  // Drop the stale-version entry so the cache doesn't grow unbounded across bumps.
90
+ // A version bump must also reset the ref-eq sentinel — otherwise the S3
91
+ // short-circuit below would defeat the R3 cache-bust contract when the
92
+ // caller passes the SAME object reference across a version bump.
81
93
  if (_sectionCacheVersions[key] !== undefined && _sectionCacheVersions[key] !== v) {
82
94
  delete _sectionCache[key + ':v' + _sectionCacheVersions[key]];
95
+ _lastValueByKey[key] = undefined;
83
96
  }
84
97
  _sectionCacheVersions[key] = v;
85
98
  var cacheKey = key + ':v' + v;
99
+ // Reference-equality short-circuit: skip the stringify entirely when the
100
+ // server returned 304 and the same object reference is being re-checked.
101
+ if (_lastValueByKey[key] === value) return false;
102
+ _lastValueByKey[key] = value;
86
103
  var json = JSON.stringify(value);
87
104
  if (_sectionCache[cacheKey] === json) return false;
88
105
  _sectionCache[cacheKey] = json;
@@ -491,7 +491,7 @@ function openPipelineDetail(id) {
491
491
  var fresh = list.find(function(x) { return x.id === id; });
492
492
  if (fresh) {
493
493
  // Only re-render if data changed
494
- var newHash = JSON.stringify({ runs: fresh.runs || [], enabled: fresh.enabled, _stoppedBy: fresh._stoppedBy, _stopReason: fresh._stopReason });
494
+ var newHash = _computePipelineDetailHash(fresh);
495
495
  if (newHash !== _pipelinePollHash) {
496
496
  _pipelinePollHash = newHash;
497
497
  _pipelinesData = _pipelinesData.map(function(x) { return x.id === id ? fresh : x; });
@@ -504,6 +504,24 @@ function openPipelineDetail(id) {
504
504
  }
505
505
  var _pipelinePollHash = '';
506
506
 
507
+ // F10: hash all pipeline fields the detail modal renders so stages/cron/monitoredResources/stopWhen
508
+ // edits in another tab trigger re-render. Previously only {runs, enabled, _stoppedBy, _stopReason}.
509
+ function _computePipelineDetailHash(p) {
510
+ if (!p) return '';
511
+ return JSON.stringify({
512
+ runs: p.runs || [],
513
+ enabled: p.enabled,
514
+ _stoppedBy: p._stoppedBy,
515
+ _stopReason: p._stopReason,
516
+ stages: p.stages,
517
+ monitoredResources: p.monitoredResources,
518
+ stopWhen: p.stopWhen,
519
+ trigger: p.trigger,
520
+ name: p.name,
521
+ description: p.description
522
+ });
523
+ }
524
+
507
525
  /**
508
526
  * Fetch fresh pipeline data and re-render the detail modal immediately.
509
527
  * Used after actions (continue, trigger, abort) to avoid waiting for the 4s poll.
@@ -516,7 +534,7 @@ async function _refreshPipelineDetail(id) {
516
534
  var fresh = list.find(function(x) { return x.id === id; });
517
535
  if (fresh) {
518
536
  _pipelinesData = _pipelinesData.map(function(x) { return x.id === id ? fresh : x; });
519
- _pipelinePollHash = JSON.stringify({ runs: fresh.runs || [], enabled: fresh.enabled, _stoppedBy: fresh._stoppedBy, _stopReason: fresh._stopReason });
537
+ _pipelinePollHash = _computePipelineDetailHash(fresh);
520
538
  renderPipelines(_pipelinesData);
521
539
  openPipelineDetail(id);
522
540
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.2030",
3
+ "version": "0.1.2031",
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"