@yangdcm/dsh-expert-team 1.3.2 → 1.3.4
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/CHANGELOG.md +39 -0
- package/client.js +94 -14
- package/lib/command.js +80 -11
- package/lib/host-settings.js +11 -1
- package/lib/settings.js +17 -24
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,45 @@
|
|
|
3
3
|
本包遵循[语义化版本](https://semver.org/lang/zh-CN/)。dsh 宿主版本线的对应关系写在
|
|
4
4
|
`package.json` 的 `engines.dsh` 与 `dsh.compatibility` 里,插件市场按它判断"这个插件跟你的宿主兼不兼容"。
|
|
5
5
|
|
|
6
|
+
## 1.3.4
|
|
7
|
+
|
|
8
|
+
**把 1.3.2 留下的 7 个"有持久化、无消费者"的设置真的接上**(display 4 + roster 3),并清掉当时的假承诺。
|
|
9
|
+
|
|
10
|
+
- **display 四项**(此前只有存储层、零消费者;根因是客户端**全仓只有一处 `/settings` 拉取**、且只在设置表单里):
|
|
11
|
+
- `pollMs`:浮层/面板轮询与画布 `--watch` 轮询都读它(此前分别写死 3000/1200 与 3000);
|
|
12
|
+
- `capsuleMs`:**真正实现**"胶囊 N 毫秒后自动消失"。此前只入队、**永不自动出队** —— 旧代码里的
|
|
13
|
+
`4000` 是**同文本去重窗口**、被误当成停留时长,所以这个功能**压根不存在**;`0 = 不自动消失` 保留;
|
|
14
|
+
- `panelWidth`:设置值 = **默认宽度**;拖动结束**防抖写回设置**(单一真源,localStorage 只作首帧缓存),
|
|
15
|
+
并把三处互不一致的默认/值域对齐(设置 280..900/420、拖动 clamp 640→900、CSS 与画布回退 360→420);
|
|
16
|
+
- `defaultTab`:面板初始页签取设置(用户点过就以点击为准)。
|
|
17
|
+
- 新增**客户端设置桥** `useDisplaySettings()`/`EXPERT_DISPLAY`:挂载时拉一次,**保存成功即当场生效**
|
|
18
|
+
(不必重启/刷新);失败静默回默认值,不影响面板工作。
|
|
19
|
+
- **roster 三项**:命令行**没给**对应参数时用设置兜底,优先级 **flag > 设置 > 常量**:
|
|
20
|
+
- `deliverable`(`artifacts-only` ⇒ `noCode`)、`persist`、`defaultRoles`;
|
|
21
|
+
- **档位收窄的基线也跟着设置走**(否则"设了默认班底"会让 `--tier` 静默失效);
|
|
22
|
+
- 落点是 `createRun` 的**唯一一处**(覆盖 `/team`、浮层决策、`/decide` 等所有建 run 入口),
|
|
23
|
+
判定抽成纯函数 `resolveRosterDefaults()` 便于单测;`--profile` 点过名的仍优先于设置。
|
|
24
|
+
- **清掉假承诺**:`INERT_SETTINGS` 现为**空对象**(7 项全部接线,机制保留作棘轮落点);
|
|
25
|
+
标记文案改为**版本中立**(`(暂未生效:尚未接线 —— …)`),并**移除用户可见文案里的行号**
|
|
26
|
+
(行号会腐烂、且对用户无意义 —— 证据一律写进代码注释)。
|
|
27
|
+
- **新增护栏** `settings-wiring.test.mjs`(进 `npm run test:all`):roster 三项**行为级**(真跑 `createRun`,
|
|
28
|
+
断言 `STATE.json`/`ROSTER.json`/`RUN.log` 三处一致 + flag 优先级 + 档位收窄基线)、画布轮询**行为级**
|
|
29
|
+
(生成的 live 脚本里就是设置值;越界回默认)、`gates.loopGuard` 的 **getter 语义**(同一钩子实例下改设置
|
|
30
|
+
⇒ 行为**双向即时翻转**、**无需重建钩子**),display 四项为**源码级**断言(本包无 DOM、不引依赖)。
|
|
31
|
+
- **更正 1.3.2 的描述**:1.3.2 曾写"待 1.3.3 接线",而 1.3.3 实际发的是宿主 schema 的枚举中文描述,
|
|
32
|
+
接线在本版完成 —— 因此标记文案改为**不承诺版本号**。
|
|
33
|
+
|
|
34
|
+
## 1.3.3
|
|
35
|
+
|
|
36
|
+
**宿主 schema 的枚举成员带上中文描述**(1.3.2 记录为"跳过"的那条,实测后补上)。
|
|
37
|
+
|
|
38
|
+
- 1.3.2 记的是"无法核实 schemastery 的可靠写法 ⇒ 跳过";随后查到其类型定义里
|
|
39
|
+
`description(text)` 的注释就写着 **"for documentation or form UIs"**,且用真 schemastery 实测:
|
|
40
|
+
`z.const('developer').description('技术开发者')` 能把标签写进成员的 `meta.description`,
|
|
41
|
+
schema 照常建成、**值域/类型/默认值完全不变** ⇒ 于是补上(`buildHostSchema` 里按
|
|
42
|
+
"方法确实存在才调用" 的既定纪律挂描述)。
|
|
43
|
+
- 本版**无功能改动之外的任何行为变化**;对设置页无影响(那一侧用 1.3.2 的 `labels`)。
|
|
44
|
+
|
|
6
45
|
## 1.3.2
|
|
7
46
|
|
|
8
47
|
**诚实性收口:把"点了没反应"的设置项要么接线、要么如实标注。**
|
package/client.js
CHANGED
|
@@ -22,7 +22,7 @@ window.__ModuleLoader__.load({
|
|
|
22
22
|
var h = createElement
|
|
23
23
|
|
|
24
24
|
var CSS =
|
|
25
|
-
'.exp-panel{position:fixed;top:0;right:0;bottom:0;width:
|
|
25
|
+
'.exp-panel{position:fixed;top:0;right:0;bottom:0;width:420px;max-width:100vw;display:flex;flex-direction:column;background:var(--dsw-alias-bg-base,var(--bg,#fff));border-left:1px solid var(--dsw-alias-border-l2,var(--border,#d0d7de));box-shadow:-10px 0 32px rgba(0,0,0,.16);z-index:120;font-size:13px;color:var(--dsw-alias-label-primary,var(--text,#1f2328));font-family:-apple-system,"Segoe UI","PingFang SC",sans-serif}' +
|
|
26
26
|
'.exp-head{padding:12px 16px;border-bottom:1px solid var(--dsw-alias-border-l1,var(--border,#d0d7de));background:linear-gradient(180deg,var(--dsw-alias-bg-module-platform,var(--bg-subtle,#f6f8fa)),var(--dsw-alias-bg-base,var(--bg,#fff)));display:flex;align-items:center;justify-content:space-between;flex:none}' +
|
|
27
27
|
'.exp-head.exp-grab{cursor:move;user-select:none}' +
|
|
28
28
|
'.exp-dock-handle{position:absolute;left:-2px;top:0;bottom:0;width:9px;cursor:col-resize;z-index:3;transition:background .12s}' +
|
|
@@ -293,6 +293,74 @@ window.__ModuleLoader__.load({
|
|
|
293
293
|
function setOpen() { open = !open; notify() }
|
|
294
294
|
function subscribe(f) { listeners.add(f); return function () { listeners.delete(f) } }
|
|
295
295
|
function useExternal(getValue) { var s = useState(getValue()); useEffect(function () { return subscribe(function () { s[1](getValue()) }) }, []); return s[0] }
|
|
296
|
+
|
|
297
|
+
// ── 设置桥(1.3.4):`display.*` 的**唯一**运行态来源 ───────────────────────────
|
|
298
|
+
// 为什么必须有它:此前全仓只有一处 `/settings` 拉取,而且只在设置表单里 —— display 四项因此
|
|
299
|
+
// 永远流不到运行态(轮询间隔/胶囊停留/面板宽度/默认页签各自硬编码或读 localStorage)。
|
|
300
|
+
// 这里建**一条**共享的路:面板挂载时拉一次;设置页保存成功后复用同一条路把新值通知出去
|
|
301
|
+
// ⇒ 改设置**当场生效**(同一 client、进程内),不必重启。
|
|
302
|
+
var EXPERT_DISPLAY = { pollMs: 3000, capsuleMs: 4000, panelWidth: 420, defaultTab: 'team' }
|
|
303
|
+
var DISPLAY_TABS = ['team', 'tasks', 'board', 'info']
|
|
304
|
+
var DISPLAY_RANGE = { pollMs: [1000, 60000], capsuleMs: [0, 60000], panelWidth: [280, 900] }
|
|
305
|
+
var displayLoaded = false
|
|
306
|
+
var displayLoading = null
|
|
307
|
+
function clampInt(v, lo, hi, def) { var n = Number(v); if (!isFinite(n)) return def; n = Math.round(n); return Math.max(lo, Math.min(hi, n)) }
|
|
308
|
+
/** 从 `/settings` 响应挑出 display 四项并按值域夹紧;缺项/越界一律回退当前值(不制造 NaN/空白)。 */
|
|
309
|
+
function pickDisplay(settings) {
|
|
310
|
+
var d = (settings && settings.display) || {}
|
|
311
|
+
return {
|
|
312
|
+
pollMs: clampInt(d.pollMs, DISPLAY_RANGE.pollMs[0], DISPLAY_RANGE.pollMs[1], EXPERT_DISPLAY.pollMs),
|
|
313
|
+
capsuleMs: clampInt(d.capsuleMs, DISPLAY_RANGE.capsuleMs[0], DISPLAY_RANGE.capsuleMs[1], EXPERT_DISPLAY.capsuleMs),
|
|
314
|
+
panelWidth: clampInt(d.panelWidth, DISPLAY_RANGE.panelWidth[0], DISPLAY_RANGE.panelWidth[1], EXPERT_DISPLAY.panelWidth),
|
|
315
|
+
defaultTab: DISPLAY_TABS.indexOf(String(d.defaultTab)) >= 0 ? String(d.defaultTab) : EXPERT_DISPLAY.defaultTab
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
/** 应用一份设置:换对象(useExternal 才看得见变化)+ 未拖动过宽度时采纳设置值 + 通知重渲染。 */
|
|
319
|
+
function applyDisplaySettings(settings) {
|
|
320
|
+
var next = pickDisplay(settings)
|
|
321
|
+
var changed = next.pollMs !== EXPERT_DISPLAY.pollMs || next.capsuleMs !== EXPERT_DISPLAY.capsuleMs || next.defaultTab !== EXPERT_DISPLAY.defaultTab
|
|
322
|
+
EXPERT_DISPLAY = next
|
|
323
|
+
// 宽度:设置值是**默认宽度**;用户拖过就以拖动结果为准(拖动结束会把宽度写回设置)。
|
|
324
|
+
if (!panelWidthUserSet && next.panelWidth !== panelWidth) { panelWidth = clampW(next.panelWidth); changed = true }
|
|
325
|
+
if (changed) notify()
|
|
326
|
+
return next
|
|
327
|
+
}
|
|
328
|
+
function fetchSettingsPayload() {
|
|
329
|
+
return fetch('/plugins/dsh-expert-team/settings').then(function (r) {
|
|
330
|
+
return r.json().catch(function () { return null }).then(function (d) { return { ok: r.ok, status: r.status, d: d } })
|
|
331
|
+
})
|
|
332
|
+
}
|
|
333
|
+
/** 拉一次设置(幂等:同一次加载只拉一次;失败静默 —— 面板照旧用默认值工作)。 */
|
|
334
|
+
function loadDisplaySettings() {
|
|
335
|
+
if (displayLoaded) return displayLoading || Promise.resolve(EXPERT_DISPLAY)
|
|
336
|
+
displayLoading = fetchSettingsPayload().then(function (res) {
|
|
337
|
+
displayLoaded = true
|
|
338
|
+
if (res.ok && res.d && res.d.ok && res.d.settings) applyDisplaySettings(res.d.settings)
|
|
339
|
+
return EXPERT_DISPLAY
|
|
340
|
+
}).catch(function () { displayLoaded = true; return EXPERT_DISPLAY })
|
|
341
|
+
return displayLoading
|
|
342
|
+
}
|
|
343
|
+
/** 运行态取 display 配置(挂载时拉一次;保存后由 applyDisplaySettings 通知更新)。 */
|
|
344
|
+
function useDisplaySettings() {
|
|
345
|
+
var v = useExternal(function () { return EXPERT_DISPLAY })
|
|
346
|
+
useEffect(function () { loadDisplaySettings() }, [])
|
|
347
|
+
return v
|
|
348
|
+
}
|
|
349
|
+
/** 拖动结束把宽度**防抖**写回设置(单一真源);localStorage 只作首帧缓存。 */
|
|
350
|
+
var widthSaveTimer = null
|
|
351
|
+
function persistDisplayWidth(w) {
|
|
352
|
+
if (widthSaveTimer) clearTimeout(widthSaveTimer)
|
|
353
|
+
widthSaveTimer = setTimeout(function () {
|
|
354
|
+
widthSaveTimer = null
|
|
355
|
+
try {
|
|
356
|
+
fetch('/plugins/dsh-expert-team/settings', {
|
|
357
|
+
method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ 'display.panelWidth': w })
|
|
358
|
+
}).then(function (r) { return r.json().catch(function () { return null }) }).then(function (d) {
|
|
359
|
+
if (d && d.ok && d.settings) applyDisplaySettings(d.settings)
|
|
360
|
+
}).catch(function () {})
|
|
361
|
+
} catch (e) {}
|
|
362
|
+
}, 400)
|
|
363
|
+
}
|
|
296
364
|
/** 对话流 workflow 卡是否已回退到 dsh 原生(localStorage 开关,刷新生效)。 */
|
|
297
365
|
function nativeWfOn() { try { return window.localStorage.getItem('et-native-workflow') === '1' } catch (e) { return false } }
|
|
298
366
|
function useCurrentSession() { return useExternal(function () { return currentSessionId }) }
|
|
@@ -306,8 +374,12 @@ window.__ModuleLoader__.load({
|
|
|
306
374
|
function saveLS(key, v) { try { window.localStorage.setItem(key, JSON.stringify(v)) } catch (e) {} }
|
|
307
375
|
var dockState = loadLS('et-dock', true)
|
|
308
376
|
var panelPos = loadLS('et-pos', { x: 0, y: 0 })
|
|
309
|
-
|
|
310
|
-
|
|
377
|
+
// 1.3.4:默认宽度来自设置 `display.panelWidth`(首帧用 localStorage 缓存避免闪烁);
|
|
378
|
+
// 值域与设置对齐 280..900(此前 clamp 到 640、CSS 写死 360 —— 三处互不一致)。
|
|
379
|
+
var panelWidthLS = loadLS('et-width', null)
|
|
380
|
+
var panelWidthUserSet = typeof panelWidthLS === 'number'
|
|
381
|
+
var panelWidth = clampW(typeof panelWidthLS === 'number' ? panelWidthLS : EXPERT_DISPLAY.panelWidth)
|
|
382
|
+
function clampW(w) { return Math.max(280, Math.min(900, w)) }
|
|
311
383
|
function setDock(v) { if (dockState === v) return; dockState = v; if (!v) { panelPos = { x: ((window && window.innerWidth) || 1280) - panelWidth, y: 80 }; saveLS('et-pos', panelPos) } saveLS('et-dock', dockState); notify() }
|
|
312
384
|
function setPanelPos(x, y) { panelPos = { x: x, y: y }; saveLS('et-pos', panelPos); notify() }
|
|
313
385
|
/**
|
|
@@ -323,7 +395,7 @@ window.__ModuleLoader__.load({
|
|
|
323
395
|
if (changed) notify()
|
|
324
396
|
} catch (e) {}
|
|
325
397
|
}
|
|
326
|
-
function setPanelWidth(w) { panelWidth = clampW(w); saveLS('et-width', panelWidth); notify() }
|
|
398
|
+
function setPanelWidth(w) { panelWidth = clampW(w); panelWidthUserSet = true; saveLS('et-width', panelWidth); notify(); persistDisplayWidth(panelWidth) }
|
|
327
399
|
|
|
328
400
|
/**
|
|
329
401
|
* ── 对话流卡片 → 右侧面板的「聚焦意图」通道 ──
|
|
@@ -1081,12 +1153,10 @@ window.__ModuleLoader__.load({
|
|
|
1081
1153
|
useEffect(function () {
|
|
1082
1154
|
var alive = true
|
|
1083
1155
|
setLoading(true); setLoadErr('')
|
|
1084
|
-
|
|
1085
|
-
return r.json().catch(function () { return null }).then(function (d) { return { ok: r.ok, status: r.status, d: d } })
|
|
1086
|
-
}).then(function (res) {
|
|
1156
|
+
fetchSettingsPayload().then(function (res) {
|
|
1087
1157
|
if (!alive) return
|
|
1088
1158
|
setLoading(false)
|
|
1089
|
-
if (res.ok && res.d && res.d.ok && res.d.schema) { setData(res.d); return }
|
|
1159
|
+
if (res.ok && res.d && res.d.ok && res.d.schema) { applyDisplaySettings(res.d.settings); setData(res.d); return }
|
|
1090
1160
|
var detail = (res.d && (res.d.error || (res.d.errors || []).join(';'))) || ''
|
|
1091
1161
|
setLoadErr(t('读取设置失败:HTTP ', 'Reading settings failed: HTTP ') + res.status + (detail ? ' ' + t('(', '(') + detail + t(')', ')') : ''))
|
|
1092
1162
|
}).catch(function (e) {
|
|
@@ -1107,6 +1177,7 @@ window.__ModuleLoader__.load({
|
|
|
1107
1177
|
return
|
|
1108
1178
|
}
|
|
1109
1179
|
setData(function (prev) { return { schema: (prev && prev.schema) || [], settings: res.d.settings, ok: true } })
|
|
1180
|
+
applyDisplaySettings(res.d.settings) // 1.3.4:设置页改完 display 四项**当场生效**(不必重启/刷新)
|
|
1110
1181
|
setMsg('已保存') // 1.3.2:经逐项核查,没有任何设置需要重启(见 lib/command.js 的 needsRestart 注释)⇒ 死分支已删
|
|
1111
1182
|
}).catch(function (e) { setErr(String(e && e.message ? e.message : e)); setMsg('') })
|
|
1112
1183
|
}
|
|
@@ -1179,6 +1250,8 @@ window.__ModuleLoader__.load({
|
|
|
1179
1250
|
var pos = usePanelPos()
|
|
1180
1251
|
useLang() // re-render when the shell language changes
|
|
1181
1252
|
var pw = usePanelWidth()
|
|
1253
|
+
// 1.3.4:display 四项在这里取(挂载时拉一次设置;保存后即时通知更新)
|
|
1254
|
+
var dispCfg = useDisplaySettings()
|
|
1182
1255
|
var dataS = useState(null), data = dataS[0], setData = dataS[1]
|
|
1183
1256
|
var runsMetaS = useState([]); var runsMeta = runsMetaS[0], setRunsMeta = runsMetaS[1]
|
|
1184
1257
|
var selTask = useState(null); var selTaskV = selTask[0], setSelTask = selTask[1]
|
|
@@ -1196,7 +1269,7 @@ window.__ModuleLoader__.load({
|
|
|
1196
1269
|
var prevDecRef = useRef(0)
|
|
1197
1270
|
var hoverS = useState(null); var hoverId = hoverS[0], setHover = hoverS[1]
|
|
1198
1271
|
var view = useState('dag'); var viewV = view[0], setView = view[1]
|
|
1199
|
-
var tabS = useState(
|
|
1272
|
+
var tabS = useState(null); var tab = tabS[0] || dispCfg.defaultTab; var setTab = tabS[1] // 1.3.4:初始页签来自设置 display.defaultTab
|
|
1200
1273
|
var listOpenS = useState(false); var listOpen = listOpenS[0], setListOpen = listOpenS[1]
|
|
1201
1274
|
// 流转视图(波次带)自己的 UI 状态:宽视图 / 波折叠 / 未绑定任务展开
|
|
1202
1275
|
var wideS = useState(false); var wideV = wideS[0], setWide = wideS[1]
|
|
@@ -1315,18 +1388,19 @@ window.__ModuleLoader__.load({
|
|
|
1315
1388
|
var m = (d && d.members) || {}
|
|
1316
1389
|
var busy = Object.keys(m).some(function (k) { var v = m[k]; return v && typeof v === 'object' && (v.activity === 'running' || v.shortStatus === 'running') })
|
|
1317
1390
|
clearInterval(pollRef.current)
|
|
1318
|
-
|
|
1391
|
+
// 1.3.4:间隔来自设置 display.pollMs(忙碌时按同一意图加速到 40%,下限 300ms)
|
|
1392
|
+
pollRef.current = setInterval(load, busy ? Math.max(300, Math.round(dispCfg.pollMs * 0.4)) : dispCfg.pollMs)
|
|
1319
1393
|
}
|
|
1320
1394
|
useEffect(function () {
|
|
1321
1395
|
if (!isOpen && !viewMode) return undefined // panel closed & not canvas → no polling
|
|
1322
|
-
load(); pollRef.current = setInterval(load,
|
|
1396
|
+
load(); pollRef.current = setInterval(load, dispCfg.pollMs)
|
|
1323
1397
|
// catch up instantly when the user returns to the tab / window
|
|
1324
1398
|
var onVis = function () { if (document.visibilityState === 'visible') load() }
|
|
1325
1399
|
var onFocus = function () { load() }
|
|
1326
1400
|
document.addEventListener('visibilitychange', onVis)
|
|
1327
1401
|
window.addEventListener('focus', onFocus)
|
|
1328
1402
|
return function () { clearInterval(pollRef.current); document.removeEventListener('visibilitychange', onVis); window.removeEventListener('focus', onFocus) }
|
|
1329
|
-
}, [sessionId, selRunV, isOpen, viewMode])
|
|
1403
|
+
}, [sessionId, selRunV, isOpen, viewMode, dispCfg.pollMs])
|
|
1330
1404
|
|
|
1331
1405
|
// Make the docked panel reserve space (shift the shell frame) instead of
|
|
1332
1406
|
// covering the workspace; clears when closed or floating (md-preview trick).
|
|
@@ -1710,7 +1784,7 @@ window.__ModuleLoader__.load({
|
|
|
1710
1784
|
// 可画宽度三源:实测容器 → 全屏画布按窗口估 → 停靠侧栏宽度偏好。
|
|
1711
1785
|
// 「宽视图」开关终于有作用了:放宽单卡上限(否则 12 波里每波只有 2–3 人时,
|
|
1712
1786
|
// 卡片被 176px 封顶、右侧大片空白)。
|
|
1713
|
-
var baseW = flowW || (viewMode ? Math.max(320, ((typeof window !== 'undefined' && window.innerWidth) || 1200) - 340) : (typeof pw === 'number' ? pw :
|
|
1787
|
+
var baseW = flowW || (viewMode ? Math.max(320, ((typeof window !== 'undefined' && window.innerWidth) || 1200) - 340) : (typeof pw === 'number' ? pw : 420))
|
|
1714
1788
|
var avail = Math.max(240, baseW - 26)
|
|
1715
1789
|
var widest = 1
|
|
1716
1790
|
W.waves.forEach(function (w) { widest = Math.max(widest, w.members.length) })
|
|
@@ -2335,8 +2409,14 @@ window.__ModuleLoader__.load({
|
|
|
2335
2409
|
try {
|
|
2336
2410
|
var last = activityQ[0]
|
|
2337
2411
|
if (last && last.text === text && Date.now() - (last.ts || 0) < 4000) return
|
|
2338
|
-
|
|
2412
|
+
var entry = { icon: icon, text: String(text || '').slice(0, 90), ts: Date.now() }
|
|
2413
|
+
activityQ.unshift(entry)
|
|
2339
2414
|
if (activityQ.length > 8) activityQ.length = 8
|
|
2415
|
+
// 1.3.4:停留时长来自设置 `display.capsuleMs`(0 = 不自动消失)。
|
|
2416
|
+
// ⚠️ 上面那个 4000 是**同文本去重窗口**,不是停留时长 —— 旧实现把它当停留时长,
|
|
2417
|
+
// 于是"胶囊 N 毫秒后消失"这个功能**压根不存在**(只入队、永不自动出队,>8 才截断)。
|
|
2418
|
+
var ttl = EXPERT_DISPLAY.capsuleMs
|
|
2419
|
+
if (ttl > 0) setTimeout(function () { var i = activityQ.indexOf(entry); if (i >= 0) { activityQ.splice(i, 1); notify() } }, ttl)
|
|
2340
2420
|
notify()
|
|
2341
2421
|
} catch (e) {}
|
|
2342
2422
|
}
|
package/lib/command.js
CHANGED
|
@@ -1704,6 +1704,57 @@ function resolveLoopGuard(config, base) {
|
|
|
1704
1704
|
return LOOP_GUARD_ENABLED;
|
|
1705
1705
|
}
|
|
1706
1706
|
|
|
1707
|
+
// ── roster 三项:命令行**未显式给出**时用设置兜底(1.3.4 接线)──────────────────────────
|
|
1708
|
+
//
|
|
1709
|
+
// 为什么放在"建 run 的入口"而不是解析器里:`parseTeamCommand` 是**纯解析**(拿不到设置,也不该拿 ——
|
|
1710
|
+
// 它是被测得很细的纯函数)。把"flag 没给 ⇒ 取设置"放在 `createRun` 的**唯一落点**,才能同时覆盖
|
|
1711
|
+
// `/team`、浮层决策、`/decide` 等所有建 run 的入口,而不是每个入口各接一遍(那就有了第二个真源)。
|
|
1712
|
+
//
|
|
1713
|
+
// ⚠️ 键写成**限定路径**(`roster.persist` 等)不是装饰:`settings-consumers.test.mjs` 对**歧义叶子**
|
|
1714
|
+
// (`persist`/`deliverable`/`defaultRoles` 在本仓另有同名的不同概念,如 `mode.persist`、`--deliverable`)
|
|
1715
|
+
// **只认限定路径**命中的消费者 —— 写成 `r.persist` 会被判成"没有消费者",而那正是本仓最想避免的假绿。
|
|
1716
|
+
/** 读设置的 roster 三项(键 = spec 的限定路径;值已按 spec 归一)。 */
|
|
1717
|
+
export function rosterSettings() {
|
|
1718
|
+
const r = (currentSettings() || {}).roster || {};
|
|
1719
|
+
return {
|
|
1720
|
+
'roster.defaultRoles': Array.isArray(r.defaultRoles) && r.defaultRoles.length ? r.defaultRoles.slice() : null,
|
|
1721
|
+
'roster.deliverable': r.deliverable === 'artifacts-only' ? 'artifacts-only' : 'code+artifacts',
|
|
1722
|
+
'roster.persist': r.persist === true,
|
|
1723
|
+
};
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
/**
|
|
1727
|
+
* 把设置落到一次建 run 的 `mode` 上:**flag > 设置 > 常量**。
|
|
1728
|
+
* 纯函数(设置与常量都由参数传入)⇒ 可单测;`createRun` 只负责调用它。
|
|
1729
|
+
*
|
|
1730
|
+
* @param mode - `parseTeamCommand` 的产物(`persist`/`noCode`/`roles` 都是"flag 给了才为真/非空")
|
|
1731
|
+
* @param rosterSet - `rosterSettings()` 的产物(可为 undefined)
|
|
1732
|
+
* @param fallbackRoles - 常量兜底班底(默认 `DEFAULT_ROLES`)
|
|
1733
|
+
* @returns `{ persist, noCode, roles, rolesBase, rolesFromSettings }`
|
|
1734
|
+
* - `rolesBase`:**档位收窄的基线**(设置了 `defaultRoles` 就用它,否则用常量);
|
|
1735
|
+
* - `rolesFromSettings`:告知调用方"这个班底来自设置、不是用户点名" ⇒ 档位收窄仍应生效。
|
|
1736
|
+
*/
|
|
1737
|
+
export function resolveRosterDefaults(mode, rosterSet, fallbackRoles) {
|
|
1738
|
+
const m = mode && typeof mode === 'object' ? mode : {};
|
|
1739
|
+
const base = Array.isArray(fallbackRoles) && fallbackRoles.length ? fallbackRoles.slice() : DEFAULT_ROLES.slice();
|
|
1740
|
+
const settingsRoles = rosterSet && Array.isArray(rosterSet['roster.defaultRoles']) && rosterSet['roster.defaultRoles'].length
|
|
1741
|
+
? rosterSet['roster.defaultRoles'].slice() : null;
|
|
1742
|
+
const explicitRoles = Array.isArray(m.roles) && m.roles.length ? m.roles.slice() : null;
|
|
1743
|
+
return {
|
|
1744
|
+
persist: m.persist === true ? true : Boolean(rosterSet && rosterSet['roster.persist'] === true),
|
|
1745
|
+
noCode: m.noCode === true ? true : Boolean(rosterSet && rosterSet['roster.deliverable'] === 'artifacts-only'),
|
|
1746
|
+
roles: explicitRoles || settingsRoles,
|
|
1747
|
+
rolesBase: settingsRoles || base,
|
|
1748
|
+
rolesFromSettings: !explicitRoles && Boolean(settingsRoles),
|
|
1749
|
+
};
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
/** 画布 live 模式的轮询间隔(毫秒):来自设置 `display.pollMs`(1.3.4 接线),越界回默认 3000。 */
|
|
1753
|
+
function canvasPollMs() {
|
|
1754
|
+
const n = Number(((currentSettings() || {}).display || {}).pollMs);
|
|
1755
|
+
return Number.isFinite(n) && n >= 1000 && n <= 60000 ? Math.round(n) : 3000;
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1707
1758
|
/**
|
|
1708
1759
|
* 解析档位门是软还是硬。**非法值一律回默认(soft)**,不猜也不报错 ——
|
|
1709
1760
|
* 一个拼错的开关值把流程卡死(hard)比静默放行更糟,而"卡死"正是最贵的失败模式。
|
|
@@ -3092,9 +3143,9 @@ function buildCanvasHtml({ runId, goal, state, roster, tasks, subagents, dir, cw
|
|
|
3092
3143
|
<ol class="path" id="path">${stepper}</ol>
|
|
3093
3144
|
<section><h2>团队编制 <span class="live" id="live-ts"></span></h2><div class="cards" id="roster">${rosterCards || '<div class="empty">(暂无成员)</div>'}</div></section>
|
|
3094
3145
|
<section><h2>任务看板</h2><div class="board" id="board">${boardHtml || '<div class="empty">(暂无任务)</div>'}</div></section>
|
|
3095
|
-
<div class="hint">本画布由 <code>/team canvas</code> 动态生成(数据源:${esc(dir)})。任务/成员状态随各阶段推进更新;想刷新就再跑一次 <code>/team canvas</code>。${watch ?
|
|
3146
|
+
<div class="hint">本画布由 <code>/team canvas</code> 动态生成(数据源:${esc(dir)})。任务/成员状态随各阶段推进更新;想刷新就再跑一次 <code>/team canvas</code>。${watch ? ` <b>── 当前为 live 模式:每 ${Math.round(canvasPollMs() / 1000)} 秒轮询 host 状态接口自动刷新。</b>` : ''}</div>
|
|
3096
3147
|
</div>
|
|
3097
|
-
${watch ? watchScript(runId, cwd) : ''}
|
|
3148
|
+
${watch ? watchScript(runId, cwd, canvasPollMs()) : ''}
|
|
3098
3149
|
</body>
|
|
3099
3150
|
</html>`;
|
|
3100
3151
|
}
|
|
@@ -3103,7 +3154,9 @@ ${watch ? watchScript(runId, cwd) : ''}
|
|
|
3103
3154
|
// (md-preview-style host HTTP endpoint, avoiding typert Remotes) and live-updates
|
|
3104
3155
|
// the phase stepper, roster cards and task board. Falls back silently if the
|
|
3105
3156
|
// route is unavailable (e.g. webServer not mounted).
|
|
3106
|
-
function watchScript(runId, cwd) {
|
|
3157
|
+
function watchScript(runId, cwd, pollMs) {
|
|
3158
|
+
// 1.3.4:轮询间隔来自设置 `display.pollMs`(此前是写死的 3000)。
|
|
3159
|
+
const per = Number.isFinite(Number(pollMs)) && Number(pollMs) >= 1000 ? Math.round(Number(pollMs)) : 3000;
|
|
3107
3160
|
const base = '"/plugins/dsh-expert-team/state"';
|
|
3108
3161
|
const stateUrl = `(${base}+"?cwd="+encodeURIComponent(${JSON.stringify(cwd)})+"&run="+encodeURIComponent(${JSON.stringify(runId)}))`;
|
|
3109
3162
|
return `<script>
|
|
@@ -3134,7 +3187,7 @@ function watchScript(runId, cwd) {
|
|
|
3134
3187
|
}catch(e){}
|
|
3135
3188
|
}
|
|
3136
3189
|
function poll(){ fetch(URL).then(function(r){if(!r.ok)return null;return r.json();}).then(function(d){if(d)render(d);}).catch(function(){}); }
|
|
3137
|
-
poll(); setInterval(poll,
|
|
3190
|
+
poll(); setInterval(poll, ${per});
|
|
3138
3191
|
})();
|
|
3139
3192
|
</script>`;
|
|
3140
3193
|
}
|
|
@@ -4252,6 +4305,16 @@ async function settleRuns(cwd, runArg, ctx) {
|
|
|
4252
4305
|
|
|
4253
4306
|
async function createRun(ctx, invocation, cwd, mode) {
|
|
4254
4307
|
if (mode.profile) mode = resolveProfile(mode.profile, mode); // apply a named profile (roles/deliverable/note)
|
|
4308
|
+
// ── roster 三项兜底(1.3.4 接线):**flag > 设置 > 常量**,唯一落点 ────────────────────
|
|
4309
|
+
// 放在 `--profile` 之后:命名档位(`resolveProfile`)显式点过的 roles/deliverable 仍优先于设置,
|
|
4310
|
+
// 与"用户在命令里点名 > 控制台偏好"同一条纪律。
|
|
4311
|
+
const rosterSet = rosterSettings();
|
|
4312
|
+
const rd = resolveRosterDefaults(mode, rosterSet, DEFAULT_ROLES);
|
|
4313
|
+
mode.persist = rd.persist;
|
|
4314
|
+
mode.noCode = rd.noCode;
|
|
4315
|
+
mode.rolesBase = rd.rolesBase;
|
|
4316
|
+
mode.rolesFromSettings = rd.rolesFromSettings;
|
|
4317
|
+
if (rd.roles && !(mode.roles && mode.roles.length)) mode.roles = rd.roles;
|
|
4255
4318
|
// ── G 线(档位):先定档位,再建 run(档位要写进 TASK/ROSTER/STATE 三份工件)──────────
|
|
4256
4319
|
// ① 显式 `--tier` **认不出来就拒绝**:静默退回默认会让用户以为自己选的那个生效了 ——
|
|
4257
4320
|
// 这正是本仓反复复发的"沉默 ≠ 允许"(SKILL §7.29)。
|
|
@@ -4275,8 +4338,11 @@ async function createRun(ctx, invocation, cwd, mode) {
|
|
|
4275
4338
|
// ③ **档位真正省派工的地方**:显式 `--tier` 且用户没点过角色名 ⇒ 按档定班底。
|
|
4276
4339
|
// (没显式指定时**不**在这里收窄:档位选择发生在浮层,用户选完那一刻才收窄 —— 见 /decide 路由。)
|
|
4277
4340
|
// `--roles` / `--profile` 点过名的,一律不动(`narrowedRoles` 只看"是不是基线班底")。
|
|
4278
|
-
|
|
4279
|
-
|
|
4341
|
+
// 1.3.4:班底来自**设置**(`roster.defaultRoles`)时也算"基线" ⇒ 收窄仍应生效,
|
|
4342
|
+
// 只是基线换成设置里那套(否则"设了默认班底"会让档位收窄静默失效)。
|
|
4343
|
+
if (pickedTier && (!(mode.roles && mode.roles.length) || mode.rolesFromSettings)) {
|
|
4344
|
+
const narrowBase = mode.rolesBase || DEFAULT_ROLES;
|
|
4345
|
+
const next = narrowedRoles(pickedTier, (mode.roles && mode.roles.length ? mode.roles : narrowBase), narrowBase);
|
|
4280
4346
|
if (next) { mode.roles = next; mode.rolesNarrowedBy = pickedTier; }
|
|
4281
4347
|
}
|
|
4282
4348
|
// 批 0-3:planDiscarded **代码强制** —— 用户明确丢弃过的同目标团队,不得由 lead 自动重建。
|
|
@@ -4515,7 +4581,7 @@ function warnLeadToolFaceOnce(status, detail) {
|
|
|
4515
4581
|
LEAD_TOOLFACE_WARNED.add(status);
|
|
4516
4582
|
console.warn(`[expert-team] lead 工具面**未**收窄(${status}):${detail}`);
|
|
4517
4583
|
}
|
|
4518
|
-
export const _live = { pushActivity, phaseAccountingViolations, loggedPhases, authorityViolations, WRITE_TRACER, createWriteTracer, formatConflict, summarizeTool, parseLogLine, roleOfSub, mapRoleToSub, membersFromState, buildRoleSubMap, resolveSubRoles, childSessionTiming, SUB_HEADER_CACHE, workflowEventIndex, workflowChildLabels, workflowChildMeta, workflowRuns, WF_EVENT_CACHE, rememberSessionRun, sessionRunFor, runOwnerSession, SESSION_RUNS, parseTeamCommand, deriveMemberEntries, schemaViolations, runHealth, RUN_STALL_MS, scaffoldFingerprint, SCAFFOLD_REQUIRED, strandedTasks, settleStranded, IN_FLIGHT_STATUSES, normalizeCoverage, SCHEMA_WARN_SEEN, pushActivityEvent, DEFAULT_LIMITS, LIMITS, resolveLimits, capacityViolations, DEFAULT_ROUND_LIMITS, ROUND_LIMITS, ROUND_LIMIT_ENV, resolveRoundLimits, ROUND_LIMIT_OF_KIND, roundOf, isQualityTask, normTitle, roundLimitViolations, reworkLoopWriteGuard, mutateTasks, readStandingRules, appendStandingRule, rulesRun, scopeOverlapWarnings, applyTaskStatus, waitRun, eventFamily, verdictFromToken, normalizeRoleName, truncateCodepoints, filterRunScopedSubs, runCreatedAtMs, runLogTail, liveFiles, LIVE_FILES_CACHE, DEFAULT_ROLES, resolveTierGate, TIER_GATE_ENV, snapshotRun, settingsPath, loadSettingsSync, currentSettings, limitsBaseFromSettings, roundLimitsBaseFromSettings, effectiveTierGate: () => TIER_GATE, ensureSkillInstalled, ensurePresetInstalled, uninstallInstalled, buildSkillRegistration, parseSkillMarkdown, PLUGIN_VERSION, INSTALL_STAMP, runtimeSkillRegistered: () => RUNTIME_SKILL_REGISTERED, agentScopedToolNames, warnLeadToolFaceOnce, resolveLeadToolFace, LEAD_TOOLFACE_ENV, effectiveLeadToolFace: () => LEAD_TOOLFACE, resolveLoopGuard, LOOP_GUARD_ENV, effectiveLoopGuard: () => LOOP_GUARD_ENABLED, installHostSettings, hostValues, hostScope, hostSettingsNote, updateHostSettings, pickFileOnly, pickHostExpressible, buildHostSchema, hostBase, hostSchemaPaths, reapplySettingsDerived, currentSettings, mergeSettings };
|
|
4584
|
+
export const _live = { pushActivity, phaseAccountingViolations, loggedPhases, authorityViolations, WRITE_TRACER, createWriteTracer, formatConflict, summarizeTool, parseLogLine, roleOfSub, mapRoleToSub, membersFromState, buildRoleSubMap, resolveSubRoles, childSessionTiming, SUB_HEADER_CACHE, workflowEventIndex, workflowChildLabels, workflowChildMeta, workflowRuns, WF_EVENT_CACHE, rememberSessionRun, sessionRunFor, runOwnerSession, SESSION_RUNS, parseTeamCommand, deriveMemberEntries, schemaViolations, runHealth, RUN_STALL_MS, scaffoldFingerprint, SCAFFOLD_REQUIRED, strandedTasks, settleStranded, IN_FLIGHT_STATUSES, normalizeCoverage, SCHEMA_WARN_SEEN, pushActivityEvent, DEFAULT_LIMITS, LIMITS, resolveLimits, capacityViolations, DEFAULT_ROUND_LIMITS, ROUND_LIMITS, ROUND_LIMIT_ENV, resolveRoundLimits, ROUND_LIMIT_OF_KIND, roundOf, isQualityTask, normTitle, roundLimitViolations, reworkLoopWriteGuard, mutateTasks, readStandingRules, appendStandingRule, rulesRun, scopeOverlapWarnings, applyTaskStatus, waitRun, eventFamily, verdictFromToken, normalizeRoleName, truncateCodepoints, filterRunScopedSubs, runCreatedAtMs, runLogTail, liveFiles, LIVE_FILES_CACHE, DEFAULT_ROLES, resolveTierGate, TIER_GATE_ENV, snapshotRun, settingsPath, loadSettingsSync, currentSettings, limitsBaseFromSettings, roundLimitsBaseFromSettings, effectiveTierGate: () => TIER_GATE, ensureSkillInstalled, ensurePresetInstalled, uninstallInstalled, buildSkillRegistration, parseSkillMarkdown, PLUGIN_VERSION, INSTALL_STAMP, runtimeSkillRegistered: () => RUNTIME_SKILL_REGISTERED, agentScopedToolNames, warnLeadToolFaceOnce, resolveLeadToolFace, LEAD_TOOLFACE_ENV, effectiveLeadToolFace: () => LEAD_TOOLFACE, resolveLoopGuard, LOOP_GUARD_ENV, effectiveLoopGuard: () => LOOP_GUARD_ENABLED, resolveRosterDefaults, rosterSettings, createRun, watchScript, canvasPollMs, installHostSettings, hostValues, hostScope, hostSettingsNote, updateHostSettings, pickFileOnly, pickHostExpressible, buildHostSchema, hostBase, hostSchemaPaths, reapplySettingsDerived, currentSettings, mergeSettings };
|
|
4519
4585
|
|
|
4520
4586
|
export function apply(ctx, config) {
|
|
4521
4587
|
// 留一份 config:设置在运行时改变(官方面板 / 浮层)时要**用同一份 config** 重算上限与档位门,
|
|
@@ -5469,10 +5535,13 @@ export function apply(ctx, config) {
|
|
|
5469
5535
|
|
|
5470
5536
|
// ── F 线:设置控制台路由(GET 读 / POST 写)──────────────────────────────
|
|
5471
5537
|
// 走 `withRoute`(B 线第 9 项抽出的共享件):方法校验 / 405 空体 / 500 兜底一条不落。
|
|
5472
|
-
// **诚实边界(2026-09-15
|
|
5473
|
-
// 类即时生效"——
|
|
5474
|
-
//
|
|
5475
|
-
//
|
|
5538
|
+
// **诚实边界(2026-09-15 更正 → 1.3.4 接线完成)**:此前这里写"`roster`/`gates` 两类改完要
|
|
5539
|
+
// 重启、`display` 类即时生效"—— 当时两句都不实。现状(都已在测试里钉住):
|
|
5540
|
+
// · `roster`/`gates`:由 `reapplySettingsDerived()` 当场重算,`roster.defaultRoles` 等在建 run
|
|
5541
|
+
// 时读(见 `resolveRosterDefaults()`)⇒ **无需重启**;
|
|
5542
|
+
// · `display.*`:1.3.4 起**真的有消费者** —— 浮层/面板由 `client.js` 的 `useDisplaySettings()`
|
|
5543
|
+
// (单一设置桥 `EXPERT_DISPLAY`)读,画布 live 轮询由本文件的 `canvasPollMs()` 读。
|
|
5544
|
+
// 响应里不声称任何一类需要重启(1.3.2 起 `needsRestart` 恒 false)。
|
|
5476
5545
|
const disposeSettings = registerLocal({
|
|
5477
5546
|
kind: 'exact',
|
|
5478
5547
|
path: '/plugins/dsh-expert-team/settings',
|
package/lib/host-settings.js
CHANGED
|
@@ -61,7 +61,17 @@ export function buildHostSchema(z) {
|
|
|
61
61
|
field = z.number();
|
|
62
62
|
if (item.min != null && typeof field.min === 'function') field = field.min(item.min);
|
|
63
63
|
if (item.max != null && typeof field.max === 'function') field = field.max(item.max);
|
|
64
|
-
} else
|
|
64
|
+
} else {
|
|
65
|
+
// 枚举:值域仍是 `union([const(v)…])`(**类型/值域不动**);若规格层给了中文标签,
|
|
66
|
+
// 就在成员上挂 `.description(label)`。可行性已实测:真 schemastery 下 schema 照常建成,
|
|
67
|
+
// 标签进了成员的 `meta.description`(该方法的类型注释写明"for documentation or form UIs")。
|
|
68
|
+
// 方法不存在时**跳过**(本模块既定纪律:只有确实存在的方法才调用 —— 这里对应宿主/版本差异)。
|
|
69
|
+
field = z.union(item.values.map((v) => {
|
|
70
|
+
const c = z.const(v);
|
|
71
|
+
const label = item.labels && item.labels[v];
|
|
72
|
+
return label && typeof c.description === 'function' ? c.description(label) : c;
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
65
75
|
if (typeof field.default === 'function') field = field.default(item.default);
|
|
66
76
|
inner[key] = field;
|
|
67
77
|
}
|
package/lib/settings.js
CHANGED
|
@@ -62,36 +62,29 @@ export const SETTINGS_SPEC = {
|
|
|
62
62
|
},
|
|
63
63
|
};
|
|
64
64
|
|
|
65
|
-
// ── 「暂未生效」的项:**单一真源**(
|
|
65
|
+
// ── 「暂未生效」的项:**单一真源**(1.3.2 建立 → 1.3.4 接线完成,故现在为空)──────────
|
|
66
66
|
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
//
|
|
67
|
+
// 这张表是**两处消费的同一个真源**:
|
|
68
|
+
// ① `decorateHint()` 把标记追加到 hint(设置页里用户可见);
|
|
69
|
+
// ② `settings-consumers.test.mjs` 用它当白名单,语义是**集合相等** ——
|
|
70
|
+
// 新增一个没有消费者的设置项 ⇒ 红;接线后忘了删这一行 ⇒ 也红(清单只减不增)。
|
|
71
71
|
//
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
//
|
|
72
|
+
// 历史:1.3.2 的审计(两轮独立只读核验)发现 20 个设置项里 **10 项的设置值没有任何消费者**。
|
|
73
|
+
// 当时处理了 3 项(`gates.boundaryTasks`/`gates.boundarySpec` 的行为本就**无条件强制**,直接删 spec;
|
|
74
|
+
// `gates.loopGuard` 是"接错了源",已接线),剩下 7 项(display 4 + roster 3)登记在此、界面标注。
|
|
75
|
+
// **1.3.4 把这 7 项全部接线** ⇒ 表空 = "零装饰性开关"的证明。机制保留:将来若又要"先上表盘、后做行为",
|
|
76
|
+
// 把限定路径写进来即可(界面标注与 ratchet 白名单会自动跟上;接线后删掉那一行,两处同时消失)。
|
|
75
77
|
//
|
|
76
|
-
//
|
|
77
|
-
//
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
-
export const INERT_SETTINGS = {
|
|
82
|
-
'display.pollMs': '现值硬编码在浮层(client.js:1317 的 1200/3000)',
|
|
83
|
-
'display.capsuleMs': '现值硬编码在浮层(client.js:2336 的 4000)',
|
|
84
|
-
'display.panelWidth': '现值来自 localStorage 的 `et-width`(client.js:309),与设置无关',
|
|
85
|
-
'display.defaultTab': '现值硬编码为 team(client.js:1198)',
|
|
86
|
-
'roster.deliverable': '现由 `/team --no-code` 派生(mode.noCode),设置值不参与',
|
|
87
|
-
'roster.persist': '现仅由 `/team --persist` 置位(mode.persist),设置值不参与',
|
|
88
|
-
'roster.defaultRoles': '现用常量 DEFAULT_ROLES 或 `--roles`,设置值不参与',
|
|
89
|
-
};
|
|
78
|
+
// ⚠️ 两条文案纪律(1.3.4 的事故换来的):
|
|
79
|
+
// · **不承诺版本号** —— 1.3.2 写"待 1.3.3 接线",而 1.3.3 发布时并未接线 ⇒ 那句话本身成了假承诺;
|
|
80
|
+
// · **不写行号** —— 行号会腐烂(实测登记后不久就偏移),且对用户毫无意义;
|
|
81
|
+
// 要留证据就写进紧邻的**代码注释**(本仓注释里保留完整出处)。
|
|
82
|
+
export const INERT_SETTINGS = {};
|
|
90
83
|
|
|
91
|
-
/** 「暂未生效」标记文本(只此一份,`decorateHint`
|
|
84
|
+
/** 「暂未生效」标记文本(只此一份,`decorateHint` 是唯一加工点)。**版本中立**。 */
|
|
92
85
|
export function inertMark(path) {
|
|
93
86
|
const why = INERT_SETTINGS[String(path)];
|
|
94
|
-
return why ?
|
|
87
|
+
return why ? `(暂未生效:尚未接线 —— ${why})` : '';
|
|
95
88
|
}
|
|
96
89
|
|
|
97
90
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yangdcm/dsh-expert-team",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.4",
|
|
4
4
|
"description": "dsh「专家团」bundle:一句自然语言自动组建/持久化一支 12 角色多智能体团队,共享工作区协议 + 阶段门控编排 + 结构化交接 + 质量门禁/自动调度,实现者直接改代码并产出持久工件;带 live 团队浮层(质量门禁/覆盖率/工件预览)。 · Role-based multi-agent expert team for DeepSeek Harness: one sentence in, a staged and gated team delivery out.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"check:name": "node scripts/rename-package.mjs --check",
|
|
85
85
|
"test:regression": "node regression.test.mjs",
|
|
86
86
|
"test:e2e": "node e2e.test.mjs",
|
|
87
|
-
"test:all": "node bootstrap.test.mjs && node host-settings.test.mjs && node smoke.test.mjs && node regression.test.mjs && node e2e.test.mjs && node flow.test.mjs && node plan-decide.test.mjs && node plan-discard.test.mjs && node models-honesty.test.mjs && node evidence-gate.test.mjs && node sync-gate.test.mjs && node artifact-writer.test.mjs && node dag.test.mjs && node metrics.test.mjs && node broken-chain.test.mjs && node schema-warn.test.mjs && node template-copy.test.mjs && node task-binding.test.mjs && node role-merge.test.mjs && node wf-recovery.test.mjs && node panorama.test.mjs && node run-ownership.test.mjs && node role-identity.test.mjs && node command-parse.test.mjs && node member-registry.test.mjs && node schema-check.test.mjs && node run-health.test.mjs && node cordis-fs-port.test.mjs && node scaffold-fingerprint.test.mjs && node preset-lint.test.mjs && node stranded-tasks.test.mjs && node agent-scope-guard.test.mjs && node schema-warn-noise.test.mjs && node tool-card-status.test.mjs && node dag-status.test.mjs && node capacity-limits.test.mjs && node task-cas.test.mjs && node write-lock.test.mjs && node card-stall.test.mjs && node multi-session-and-confirm.test.mjs && node standing-rules.test.mjs && node scope-overlap.test.mjs && node task-cancel-protect.test.mjs && node wait-run.test.mjs && node rework-loop.test.mjs && node client-css-integrity.test.mjs && node interception.test.mjs && node mutation-catalog.test.mjs && node write-bypass-ratchet.test.mjs && node asi-hazards.test.mjs && node dispatch-contract.test.mjs && node closure-ledger.test.mjs && node rework-nature.test.mjs && node loop-guard.test.mjs && node vocab-consistency.test.mjs && node state-no-write.test.mjs && node routes-shared.test.mjs && node metrics-render.test.mjs && node metrics-collect.test.mjs && node first-runnable.test.mjs && node closing-budget.test.mjs && node scan-single-source.test.mjs && node tier.test.mjs && node dispatch-ledger.test.mjs && node tier-gate.test.mjs && node tier-badge.test.mjs && node validate-module.test.mjs && node settings.test.mjs && node settings-page.test.mjs && node policy.test.mjs && node phase-accounting.test.mjs && node log-parse-module.test.mjs && node command-parse-module.test.mjs && node authority.test.mjs && node write-tracer.test.mjs && node token-accounting.test.mjs && node lead-toolface.test.mjs && node settings-consumers.test.mjs",
|
|
87
|
+
"test:all": "node bootstrap.test.mjs && node host-settings.test.mjs && node smoke.test.mjs && node regression.test.mjs && node e2e.test.mjs && node flow.test.mjs && node plan-decide.test.mjs && node plan-discard.test.mjs && node models-honesty.test.mjs && node evidence-gate.test.mjs && node sync-gate.test.mjs && node artifact-writer.test.mjs && node dag.test.mjs && node metrics.test.mjs && node broken-chain.test.mjs && node schema-warn.test.mjs && node template-copy.test.mjs && node task-binding.test.mjs && node role-merge.test.mjs && node wf-recovery.test.mjs && node panorama.test.mjs && node run-ownership.test.mjs && node role-identity.test.mjs && node command-parse.test.mjs && node member-registry.test.mjs && node schema-check.test.mjs && node run-health.test.mjs && node cordis-fs-port.test.mjs && node scaffold-fingerprint.test.mjs && node preset-lint.test.mjs && node stranded-tasks.test.mjs && node agent-scope-guard.test.mjs && node schema-warn-noise.test.mjs && node tool-card-status.test.mjs && node dag-status.test.mjs && node capacity-limits.test.mjs && node task-cas.test.mjs && node write-lock.test.mjs && node card-stall.test.mjs && node multi-session-and-confirm.test.mjs && node standing-rules.test.mjs && node scope-overlap.test.mjs && node task-cancel-protect.test.mjs && node wait-run.test.mjs && node rework-loop.test.mjs && node client-css-integrity.test.mjs && node interception.test.mjs && node mutation-catalog.test.mjs && node write-bypass-ratchet.test.mjs && node asi-hazards.test.mjs && node dispatch-contract.test.mjs && node closure-ledger.test.mjs && node rework-nature.test.mjs && node loop-guard.test.mjs && node vocab-consistency.test.mjs && node state-no-write.test.mjs && node routes-shared.test.mjs && node metrics-render.test.mjs && node metrics-collect.test.mjs && node first-runnable.test.mjs && node closing-budget.test.mjs && node scan-single-source.test.mjs && node tier.test.mjs && node dispatch-ledger.test.mjs && node tier-gate.test.mjs && node tier-badge.test.mjs && node validate-module.test.mjs && node settings.test.mjs && node settings-page.test.mjs && node policy.test.mjs && node phase-accounting.test.mjs && node log-parse-module.test.mjs && node command-parse-module.test.mjs && node authority.test.mjs && node write-tracer.test.mjs && node token-accounting.test.mjs && node lead-toolface.test.mjs && node settings-consumers.test.mjs && node settings-wiring.test.mjs",
|
|
88
88
|
"test:toolcard": "node tool-card-status.test.mjs",
|
|
89
89
|
"test:flow": "node flow.test.mjs",
|
|
90
90
|
"test:decide": "node plan-decide.test.mjs",
|