dsh-agent-board 1.7.0 → 1.7.2
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/README.md +21 -10
- package/index.mjs +10 -3
- package/lib/client/board-list.js +47 -5
- package/lib/client/dashboard.js +151 -8
- package/lib/client/kernel.js +61 -15
- package/lib/client/task-detail.js +161 -9
- package/lib/client.js +420 -37
- package/lib/core.mjs +402 -21
- package/lib/dispatch.mjs +164 -12
- package/lib/health.mjs +43 -11
- package/lib/notify.mjs +42 -12
- package/lib/policy.mjs +17 -1
- package/lib/rpc.mjs +71 -13
- package/lib/session.mjs +24 -3
- package/lib/store.mjs +2 -1
- package/lib/usage.mjs +84 -3
- package/package.json +1 -1
- package/test/core.test.mjs +1906 -38
- package/test/health.test.mjs +86 -4
package/test/health.test.mjs
CHANGED
|
@@ -58,8 +58,44 @@ test('信号 a 口径:路径归一化(./ 前缀与反斜杠合并计数)',
|
|
|
58
58
|
assert.ok(hints[0].text.indexOf('src/big.js 在近 8 张卡中被 8 次声明 touches(占比 100%)') >= 0)
|
|
59
59
|
})
|
|
60
60
|
|
|
61
|
+
// task-muupnnq5 ③:机械随卡路径(组装产物 / 守门员目录 / 根 README)不计入热度,也不占占比分母
|
|
62
|
+
test('信号 a 防灌水:test/ 与 lib/client.js 只出现在机械随卡时不再触发 warn', function () {
|
|
63
|
+
var ts = []
|
|
64
|
+
// 10 张卡都挂守门员/产物路径——旧口径下 lib/client.js 会以 10 次 + 100% 占比误报「架构收敛点」
|
|
65
|
+
for (var i = 0; i < 10; i++) ts.push(mk(i, { touches: ['lib/client.js', 'test/health.test.mjs', 'test'] }))
|
|
66
|
+
assert.deepEqual(computeHealthHints(ts), [])
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('信号 a 防灌水:噪声路径不进占比分母', function () {
|
|
70
|
+
var ts = []
|
|
71
|
+
// 10 张卡只挂守门员/产物路径:过滤后一律不算「有 touches 任务」,不该抬高分母、也不该进热度计数
|
|
72
|
+
for (var i = 0; i < 10; i++) ts.push(mk(i, { touches: ['README.md', 'test/health.test.mjs', 'lib/client.js'] }))
|
|
73
|
+
// 7 张真实热点路径卡:真实分母 7(旧口径会被 10 张噪声卡撑成 17)
|
|
74
|
+
for (var j = 0; j < 7; j++) ts.push(mk(10 + j, { touches: ['src/real.js'] }))
|
|
75
|
+
// 判定式:次数 7 < 8 → 不命中;若分母含噪声卡则是 7/17=41% ≥ 40%,会误报(本断言锁死该回归)
|
|
76
|
+
assert.deepEqual(computeHealthHints(ts), [])
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
test('信号 a 防灌水:真实热点路径照常计入(噪声过滤不误伤相邻路径)', function () {
|
|
80
|
+
var ts = []
|
|
81
|
+
for (var i = 0; i < 8; i++) ts.push(mk(i, { touches: ['lib/core.mjs', 'test/core.test.mjs'] })) // 同卡里的真实路径仍计数(只丢 test/ 那条)
|
|
82
|
+
var hints = computeHealthHints(ts)
|
|
83
|
+
assert.equal(hints.length, 1)
|
|
84
|
+
assert.ok(hints[0].text.indexOf('lib/core.mjs 在近 8 张卡中被 8 次声明 touches') >= 0)
|
|
85
|
+
assert.equal(hints[0].text.indexOf('test/core.test.mjs'), -1)
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
test('信号 a 防灌水:过滤只在健康度侧,touches 锁语义不受影响(core 判定原样)', async function () {
|
|
89
|
+
// 黑名单属健康度展示口径:core 的锁冲突判定必须仍认这些路径(否则会静默放行并行 Worker 互踩)
|
|
90
|
+
var core = await import('../lib/core.mjs')
|
|
91
|
+
assert.equal(core.normTouch('./lib/client.js'), 'lib/client.js')
|
|
92
|
+
assert.equal(core.patOverlap('lib/client.js', './lib/client.js'), true)
|
|
93
|
+
assert.equal(core.patOverlap('test/**', 'test/health.test.mjs'), true)
|
|
94
|
+
assert.equal(core.patOverlap('README.md', 'README.md'), true)
|
|
95
|
+
})
|
|
96
|
+
|
|
61
97
|
// ===== 信号 b:串行代价代理 =====
|
|
62
|
-
test('信号 b 命中:有 touches 任务滞留中位数 > 无 touches 的 2 倍且样本 ≥5', function () {
|
|
98
|
+
test('信号 b 命中:有 touches 任务滞留中位数 > 无 touches 的 2 倍且样本 ≥5(绝对阈值也过)', function () {
|
|
63
99
|
var ts = []
|
|
64
100
|
for (var i = 0; i < 6; i++) ts.push(mk(i, { touches: ['src/x' + i + '.js'], claimedAt: iso(BASE + i * MIN + 30 * MIN), resolvedAt: iso(BASE + i * MIN + 31 * MIN) }))
|
|
65
101
|
for (var j = 0; j < 6; j++) ts.push(mk(100 + j, { claimedAt: iso(BASE + (100 + j) * MIN + 3 * MIN), resolvedAt: iso(BASE + (100 + j) * MIN + 4 * MIN) }))
|
|
@@ -72,11 +108,57 @@ test('信号 b 命中:有 touches 任务滞留中位数 > 无 touches 的 2
|
|
|
72
108
|
|
|
73
109
|
test('信号 b 不命中:有 touches 任务样本 <5', function () {
|
|
74
110
|
var ts = []
|
|
75
|
-
for (var i = 0; i < 4; i++) ts.push(mk(i, { touches: ['src/x' + i + '.js'], claimedAt: iso(BASE + i * MIN + 60 * MIN) }))
|
|
76
|
-
for (var j = 0; j < 4; j++) ts.push(mk(100 + j, { claimedAt: iso(BASE + (100 + j) * MIN + MIN) }))
|
|
111
|
+
for (var i = 0; i < 4; i++) ts.push(mk(i, { touches: ['src/x' + i + '.js'], claimedAt: iso(BASE + i * MIN + 60 * MIN), resolvedAt: iso(BASE + i * MIN + 61 * MIN) }))
|
|
112
|
+
for (var j = 0; j < 4; j++) ts.push(mk(100 + j, { claimedAt: iso(BASE + (100 + j) * MIN + MIN), resolvedAt: iso(BASE + (100 + j) * MIN + 2 * MIN) }))
|
|
77
113
|
assert.deepEqual(computeHealthHints(ts), [])
|
|
78
114
|
})
|
|
79
115
|
|
|
116
|
+
// task-muupnnq5 ①:亚分钟差异(0.4min vs 0.1min 满足 2 倍)是纯噪声,绝对阈值 5min 拦掉
|
|
117
|
+
test('信号 b 不触发:相对倍数够但不足 5min 绝对阈值(4.9min vs 0.4min)', function () {
|
|
118
|
+
var ts = []
|
|
119
|
+
for (var i = 0; i < 6; i++) ts.push(mk(i, { touches: ['src/n' + i + '.js'], claimedAt: iso(BASE + i * MIN + 294000), resolvedAt: iso(BASE + i * MIN + 300000) })) // 等待 4.9min
|
|
120
|
+
for (var j = 0; j < 6; j++) ts.push(mk(100 + j, { claimedAt: iso(BASE + (100 + j) * MIN + 24000), resolvedAt: iso(BASE + (100 + j) * MIN + 30000) })) // 等待 0.4min
|
|
121
|
+
assert.deepEqual(computeHealthHints(ts), [])
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
test('信号 b 触发:刚过 5min 绝对阈值(5.1min vs 0.4min)', function () {
|
|
125
|
+
var ts = []
|
|
126
|
+
for (var i = 0; i < 6; i++) ts.push(mk(i, { touches: ['src/y' + i + '.js'], claimedAt: iso(BASE + i * MIN + 306000), resolvedAt: iso(BASE + i * MIN + 312000) })) // 等待 5.1min
|
|
127
|
+
for (var j = 0; j < 6; j++) ts.push(mk(100 + j, { claimedAt: iso(BASE + (100 + j) * MIN + 24000), resolvedAt: iso(BASE + (100 + j) * MIN + 30000) })) // 等待 0.4min
|
|
128
|
+
var hints = computeHealthHints(ts)
|
|
129
|
+
assert.equal(hints.length, 1)
|
|
130
|
+
assert.ok(hints[0].text.indexOf('中位数 5min vs <1min') >= 0) // 5.1 → 5min;0.4min 不再撞脸成「0min」而是 <1min
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
// task-muupnnq5 ④:排队中(无 claimedAt/resolvedAt)的 pending 卡按 now-createdAt 计入,治右删失低估
|
|
134
|
+
test('信号 b 口径:仍在排队的 pending 卡滞留计入(now-createdAt)', function () {
|
|
135
|
+
var now = Date.now()
|
|
136
|
+
var ts = []
|
|
137
|
+
// 6 张仍排队的有 touches 卡,创建于 40min 前 → 滞留 40min
|
|
138
|
+
for (var i = 0; i < 6; i++) ts.push(mk(i, { status: 'pending', touches: ['src/w' + i + '.js'], createdAt: iso(now - 40 * MIN) }))
|
|
139
|
+
// 6 张立刻完成的无 touches 卡 → 滞留 ~0
|
|
140
|
+
for (var j = 0; j < 6; j++) ts.push(mk(100 + j, { createdAt: iso(now - 60 * MIN), claimedAt: iso(now - 59 * MIN), resolvedAt: iso(now - 58 * MIN) }))
|
|
141
|
+
var hints = computeHealthHints(ts)
|
|
142
|
+
assert.equal(hints.length, 1)
|
|
143
|
+
assert.ok(hints[0].text.indexOf('并行度受锁限制') >= 0)
|
|
144
|
+
// 反证:同一批卡若都是「刚刚创建还在排队」(滞留 ~0)则不判定——差别全部来自 now-createdAt 口径
|
|
145
|
+
var fresh = ts.map(function (t) { return Object.assign({}, t, { createdAt: iso(now - 30000) }) })
|
|
146
|
+
assert.deepEqual(computeHealthHints(fresh), [])
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
// task-muupnnq5 ①:亚分钟滞留的展示修正——不再四舍五入成「0min」撞脸(治幽灵告警的观感来源)
|
|
150
|
+
test('文案口径:不足 1 分钟渲染 <1min(无 0min 撞脸)', function () {
|
|
151
|
+
var now = Date.now()
|
|
152
|
+
var ts = []
|
|
153
|
+
// 6 张排队中的有 touches 卡:滞留 40min(过绝对阈值)+ 6 张 0.2min 对照组 → 对照组必须显示 <1min
|
|
154
|
+
for (var i = 0; i < 6; i++) ts.push(mk(i, { status: 'pending', touches: ['src/z' + i + '.js'], createdAt: iso(now - 40 * MIN) }))
|
|
155
|
+
for (var j = 0; j < 6; j++) ts.push(mk(100 + j, { createdAt: iso(now - 12000), claimedAt: iso(now), resolvedAt: iso(now) }))
|
|
156
|
+
var hints = computeHealthHints(ts)
|
|
157
|
+
assert.equal(hints.length, 1)
|
|
158
|
+
assert.ok(hints[0].text.indexOf('40min vs <1min') >= 0)
|
|
159
|
+
assert.equal(hints[0].text.indexOf('vs 0min'), -1) // 亚分钟不再渲染成「0min」
|
|
160
|
+
})
|
|
161
|
+
|
|
80
162
|
// ===== 信号 c:执行时长 p90(口径 = claimedAt→resolvedAt,task-mutdnitw 修正后不含排队)=====
|
|
81
163
|
test('信号 c 命中:resolved 任务执行时长 p90 > 45min', function () {
|
|
82
164
|
var ts = []
|
|
@@ -144,7 +226,7 @@ test('四信号同时命中时截断为 3 条(warn 优先)', function () {
|
|
|
144
226
|
for (var i = 0; i < 8; i++) ts.push(mk(i, { touches: ['src/hot.js'], rejectCount: 1, claimedAt: iso(BASE + i * MIN + 30 * MIN), resolvedAt: iso(BASE + i * MIN + 100 * MIN) }))
|
|
145
227
|
// 6 张其他 touches 卡:滞留 30min、执行 70min(凑信号 b 样本与信号 c)
|
|
146
228
|
for (var j = 0; j < 6; j++) ts.push(mk(100 + j, { touches: ['src/other.js'], claimedAt: iso(BASE + (100 + j) * MIN + 30 * MIN), resolvedAt: iso(BASE + (100 + j) * MIN + 100 * MIN) }))
|
|
147
|
-
// 6 张无 touches 卡:滞留 3min、执行 97min(信号 b
|
|
229
|
+
// 6 张无 touches 卡:滞留 3min、执行 97min(信号 b 对照组;resolvedAt 必须有,否则滞留会退化成 now-createdAt)
|
|
148
230
|
for (var k = 0; k < 6; k++) ts.push(mk(200 + k, { claimedAt: iso(BASE + (200 + k) * MIN + 3 * MIN), resolvedAt: iso(BASE + (200 + k) * MIN + 100 * MIN) }))
|
|
149
231
|
var hints = computeHealthHints(ts)
|
|
150
232
|
assert.equal(hints.length, 3)
|