@yangdcm/dsh-expert-team 1.3.11 → 1.3.13

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 CHANGED
@@ -3,6 +3,52 @@
3
3
  本包遵循[语义化版本](https://semver.org/lang/zh-CN/)。dsh 宿主版本线的对应关系写在
4
4
  `package.json` 的 `engines.dsh` 与 `dsh.compatibility` 里,插件市场按它判断"这个插件跟你的宿主兼不兼容"。
5
5
 
6
+ ## 1.3.13
7
+
8
+ **渐进式状态:`/state` 支持 `?section=`,首屏与会话体量解耦**(性能修复 #3)
9
+
10
+ - 背景(真机实测,`DSH_EXPERT_TEAM_STATE_PROFILE=1` 分步):完整负载里 `subs` **1 327–1 388 ms**、
11
+ `roles` **638–869 ms**、`runs+select` 15–21 ms、`tail` 3 ms;重会话(20+ 子代理、仍在活跃写日志)
12
+ 总耗时 **2.1–6.2 s**,成本随「子代理数 × 日志体量」线性增长。
13
+ - 新增 `?section=summary|people|feed|artifacts`(逗号可多选;**缺省或 **ALL** = 完整负载、字段语义不变**
14
+ ⇒ 老客户端/脚本/既有测试不受影响):
15
+ - `summary`(首屏用):阶段/进度/计数/违规/告警 —— 真机 `snapshotRun` 中位 **0.3 ms**;
16
+ - `people`:子会话清单/角色/成员时间线(最贵的那两块就在这里);
17
+ - `feed`:每 agent 最近事件;`artifacts`:`RUN.log` 尾 + 工作区改动文件。
18
+ - **上限 + 如实降级**:角色解析硬上限 `DSH_EXPERT_TEAM_MAX_ROLE_SUBS`(默认 60;超出部分如实进
19
+ `degraded: "roles:<n>"`);people 软期限 `DSH_EXPERT_TEAM_PEOPLE_DEADLINE_MS`(默认 2500 ms;超时跳过
20
+ workflow 元数据并记 `wf:deadline`)。响应新增 `sections`/`degraded`/`degradedReason`(缺块 ≠ 空数据)。
21
+ - **副作用修正**:`summary` 路径不再触发「派工即登记」写 `STATE.json`(原先每轮询都可能因空 `subById`
22
+ 算出"清空成员"而覆写工件)⇒ **摘要轮询是纯读**。
23
+ - 客户端:**一快一慢双订阅**(`summary` 按 `pollMs`;`people,feed,artifacts` ≥ 6 s)+
24
+ `mergeStatePayload` **合并**(摘要那一拍不再抹掉已知成员);`stateHub` 改为**按 URL 各自计时**
25
+ (`stateHubBaseOf/DueAt`,tick 只发已到期的 URL)—— 旧实现"全局取最小 base、每 tick 拉全部 URL"
26
+ 正是"慢端点被快钟拖着跑"的根源。
27
+ - 口径对齐(同批):`templates` 常量与两个 e2e 的"run 必需文件"清单**语义不同、关系确定**,
28
+ 由 `artifact-ownership.test.mjs` 的 **E 断言**钉死(常量 ≡ 模板目录;e2e 清单 ≡ 常量 ∪ `RUN.log.md`);
29
+ README/README.en/llms 写实 `METRICS.md` 的来源与位置(**`team/` 根的聚合快照**,`/team learn` 产出),
30
+ 并补一句 **R1 工件归属**(工件由产出它的角色自己写;lead 只读门控)。
31
+ - 验证:`npm run test:all` EXIT=0(**82 个测试文件**;新增 `state-sections.test.mjs`);
32
+ 变异目录随源码文本**同提交**更新(`M70` 的 `find` 串)。
33
+
34
+ ## 1.3.12
35
+
36
+ **面向"可被 AI 检索与引用"的文档与元数据补齐**(纯文档/元数据,代码与 1.3.11 完全相同)。
37
+
38
+ - README(中英各自对等)新增:
39
+ - **速览 / At a glance** 事实表:包名 · npm · 仓库 · 宿主版本 · 运行时依赖 · Node · License · 一行安装 · 一行上手 · 过程产物路径 · 本机数据路径;
40
+ - 一句**同义词行**(dsh 插件 · DeepSeek Harness 多智能体 · agent team · orchestration · 角色化 subagent · DAG 并行 · 阶段门控 · 质量门禁 · 工件留痕)—— 自然成句,不堆砌关键词;
41
+ - **常见问题 / FAQ**:用自然语言问句覆盖「是什么 / 与"一个 agent 硬做"的区别 / 是否必须装别的插件 / 支持哪些 dsh 版本 / 数据放在哪 / 怎么卸载 / 会不会联网 / 支持哪些模型 / 不切 preset 能否用 / 打开很慢怎么办」;
42
+ - **术语 / Glossary**:12 角色 ↔ 代码 id、9 阶段 ↔ id(以 `lib/vocab.js` 为唯一真源)、主要工件清单;
43
+ - 主要小节标题改为**双语**(安装 / 依赖与推荐插件 / 快速上手 / 自定义预设 / 常见问题 / 术语)。
44
+ - **图片 alt 文本**全部换成带关键词的描述性文案(此前偏短 —— alt 是可机读文本,不该白丢)。
45
+ - 「快速上手」的命令块改为 **14 行速查表**(命令 + 一句话用途),措辞以 `lib/command-parse.js` 的 `USAGE` 为真源。
46
+ - 新增仓库根 [`llms.txt`](https://github.com/yangdcm/dsh-expert-team/blob/main/llms.txt):按 [llmstxt.org](https://llmstxt.org) 约定给 LLM 用的索引 —— 事实块(可逐条核对)+ 文档链接(只列仓库里真实存在的文件)+ 诚实边界;README 顶部加一行指路。
47
+ - `package.json` `keywords` 补 4 个:`role-based` / `spec-driven` / `dag` / `agentic-workflow`(`description` 未动)。
48
+ - GitHub 仓库 topics 补齐:`ai-agents` / `agent-orchestration` / `multi-agent-systems` / `llm` / `cordis` / `code-generation` / `workflow-automation`。
49
+
50
+ > 两条**没有做**的(有意):不写隐藏关键词/meta 标签(GitHub 会剥掉,且属取巧);不写夸大话术 —— 本节所有事实都能在仓库或实测里核到。
51
+
6
52
  ## 1.3.11
7
53
 
8
54
  **`/state` 的 `subs` / `roles` 两段收口 —— 这次不靠预测,靠 1.3.10 留下的分步计时开关实测定位。**
package/README.en.md CHANGED
@@ -6,7 +6,7 @@ English | [中文](README.md)
6
6
  [![license](https://img.shields.io/npm/l/@yangdcm/dsh-expert-team)](https://github.com/yangdcm/dsh-expert-team/blob/main/LICENSE)
7
7
  [![CI](https://github.com/yangdcm/dsh-expert-team/actions/workflows/ci.yml/badge.svg)](https://github.com/yangdcm/dsh-expert-team/actions/workflows/ci.yml)
8
8
 
9
- ![expert-team: one sentence in, a gated team delivery out](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/hero.svg)
9
+ ![dsh expert-team plugin banner: a 12-role multi-agent team, a 9-phase gated pipeline, zero runtime dependencies](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/hero.svg)
10
10
 
11
11
  > **One sentence in, a gated team delivery out.** `/team build a payments module with login`
12
12
  > assembles a 12-role expert team and runs
@@ -22,6 +22,25 @@ A plugin for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)
22
22
 
23
23
  > Zero runtime dependencies. Recommended: also install **Hindsight** (cross-project memory) — see [Dependencies and recommended plugins](#dependencies-and-recommended-plugins).
24
24
 
25
+ ## At a glance
26
+
27
+ | Item | Value |
28
+ |---|---|
29
+ | Package | `@yangdcm/dsh-expert-team` (public npm package) |
30
+ | Repository | <https://github.com/yangdcm/dsh-expert-team> |
31
+ | Host | [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) ≥ **0.1.5-rc.1** (`web` profile) |
32
+ | Runtime dependencies | **none** (`dependencies: {}`; `lib/` imports only sibling files and Node builtins) |
33
+ | Node.js | ≥ 20 |
34
+ | License | MIT |
35
+ | Install (one line) | `dsh plugin --profile web add @yangdcm/dsh-expert-team` |
36
+ | First run (one line) | switch the session to "Expert team mode" → `/team build a payments module with login` |
37
+ | Process artifacts | `<your workspace>/team/<run-id>/` (`SPEC.md` · `PLAN.md` · `TASKS.json` · `REVIEW.md` · `TEST.md` · `SUMMARY.md` …) |
38
+ | Machine-local data | `$DSH_HOME/expert-team/` (`settings.json` · `LEARNINGS.md` · `session-runs.json`) |
39
+
40
+ > A dsh plugin · a DeepSeek Harness multi-agent (agent team) orchestrator: role-based subagents · DAG parallelism · staged gates · quality gates · artifact trail.
41
+ >
42
+ > Index for LLMs and retrieval: [`llms.txt`](https://github.com/yangdcm/dsh-expert-team/blob/main/llms.txt)
43
+
25
44
  ## Who it is for
26
45
 
27
46
  **A complete engineering department for small teams and solo builders** — no hiring, no assembling a team:
@@ -49,7 +68,7 @@ solo developers shipping a complete project · any long task where "someone inde
49
68
 
50
69
  <sub>One honest boundary: this is **not** a human team — product-level and scope-level decisions remain yours.</sub>
51
70
 
52
- ![The 9-phase gated pipeline](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/pipeline.svg)
71
+ ![The expert-team 9-phase gated pipeline: clarify → research → design → spec-review (hard gate) → plan approval → implement (DAG parallel) → review → test → deliver](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/pipeline.svg)
53
72
 
54
73
  <sub>Figure 1: the 9-phase gated pipeline. `spec-review` is a **hard gate** — if the SPEC.md
55
74
  "boundaries and prohibitions" section is empty, the run does not advance (`lib/interception.js`).
@@ -71,9 +90,10 @@ $ /team build a payments module with login
71
90
  ├─ implement backend … fan out along the DAG → edits your workspace directly
72
91
  ├─ review sec·reviewer independent review (cross-model) → REVIEW.md
73
92
  ├─ test qa repro, coverage, regressions → TEST.md
74
- └─ deliver docs wrap-up and cost → SUMMARY.md · METRICS.md
93
+ └─ deliver docs wrap-up → SUMMARY.md
75
94
 
76
95
  Persisted throughout: TASKS.json · ROSTER.json · STATE.json · AUTHORITY.md · RUN.log.md
96
+ Aggregate snapshot: `METRICS.md` lives at the **`team/` root** (produced by `/team learn`; not a run-dir template)
77
97
  On disk at: <your workspace>/team/<run-id>/
78
98
  ```
79
99
 
@@ -96,32 +116,32 @@ answer them:
96
116
 
97
117
  ## What it looks like in action
98
118
 
99
- ![The full-screen canvas: phase bar, progress and roster](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/canvas.png)
119
+ ![Expert-team full-screen canvas: phase bar, progress, and the role-based subagent roster (who is running, on which model)](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/canvas.png)
100
120
 
101
121
  <sub>Figure 2: **the full-screen canvas**. Look at the phase bar and progress, the roster (who is running, on which model), and the four views (people / tasks / artifacts / board) — one layer above the floating panel.</sub>
102
122
 
103
- ![The full-screen canvas, tasks view: the dependency graph](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/canvas-tasks.png)
123
+ ![Expert-team task dependency graph: tasks running in parallel along the DAG, including the repair/review rework loop](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/canvas-tasks.png)
104
124
 
105
125
  <sub>Figure 3: **the task dependency graph** — 11 tasks advance in parallel along the dependency DAG; 7 completed, 4 failed. A failure triggers `repair` plus **independent re-verification** (`repair-1 → review-2 → repair-2 → review-3`) until it passes or is honestly marked as needing revision — this is what the "rework does not converge" hard gate looks like in a real run.</sub>
106
126
 
107
- ![Quality-gate violations surfaced live](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/panel-gate.png)
127
+ ![Expert-team quality-gate violation banner: a missing spec boundary blocked by plugin code, not by a prompt](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/panel-gate.png)
108
128
 
109
129
  <sub>Figure 4: **gate violations**. Look at the banner at the top — the violation and its refusal reason
110
130
  (e.g. "SPEC.md's boundary section has entered `implement` but still has no 'expected rejection' row")
111
131
  is decided by `lib/interception.js`, hooked onto the host's `tools/post-execute` waterfall, and surfaced
112
132
  immediately. This is code, not a prompt reminder.</sub>
113
133
 
114
- ![Members, models and task detail](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/panel-live.png)
134
+ ![Expert-team overlay: role members, the model each one uses, task detail and artifact preview](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/panel-live.png)
115
135
 
116
136
  <sub>Figure 5: **the roster**. Look at the member list — who is running, on which model, and what it is doing;
117
137
  expand a member for its tasks and artifacts. Models are configurable per role; heterogeneous models are used for cross-checking.</sub>
118
138
 
119
- ![Phase progress and artifact preview](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/panel-flow.png)
139
+ ![Expert-team phase progress: current and completed phases, plus the artifact body written at that phase](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/panel-flow.png)
120
140
 
121
141
  <sub>Figure 6: **phases and artifacts**. Look at the phase bar and the preview pane — the current phase, the phases
122
142
  already passed, and the actual body of the artifact written in that phase (artifacts are the single source of truth; the overlay is just a view of them).</sub>
123
143
 
124
- ![The expert-team section inside the official settings page](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/settings.png)
144
+ ![The expert-team section inside the official DeepSeek Harness settings page: 18 settings, Chinese labels, applied on change](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/settings.png)
125
145
 
126
146
  <sub>Figure 7: **settings**. Look at the official `Settings → Expert team` page — 18 settings, Chinese labels,
127
147
  **saved on change and applied immediately** (caps, rounds, the tier gate and the oscillation detector are recomputed
@@ -271,20 +291,24 @@ dsh plugin --profile web add dshmarket
271
291
 
272
292
  ## Quick start
273
293
 
274
- ```
275
- /team build a payments module with login # one sentence in, a one-shot team delivery out
276
- /team --persist refactor the orders module # persistent live team: members can be re-tasked, survives sessions
277
- /team --one-shot run a small chore # inverse override: run once even if persistence is the default
278
- /team --no-code review the existing API # produce planning/review/test artifacts only, change no code
279
- /team --code implement it # inverse override: touch code even if "artifacts only" is the default
280
- /team --confirm a big redesign # create the run but do not dispatch; click "run" in the overlay
281
- /team uninstall # reclaim what this plugin laid down under $DSH_HOME
282
- /team status # phase, members, model plan and live violations for every run
283
- /team resume <run-id> # resume across sessions
284
- ```
285
-
286
- Full command list (`/team canvas` visual canvas, `/team codeindex` code index, `/team learn` self-learning,
287
- `/team limit` quotas, `/team settle` cold-start settlement, …) — see `/team help`.
294
+ | Command | What it does |
295
+ |---|---|
296
+ | `/team <one-sentence goal>` | one sentence in, a one-shot team delivery out |
297
+ | `/team --persist <task>` | persistent live team: members can be re-tasked, survives sessions |
298
+ | `/team --one-shot <task>` | inverse override: run once even if persistence is the default |
299
+ | `/team --no-code <task>` | produce planning/review/test artifacts only, change no code |
300
+ | `/team --code <task>` | inverse override: touch code even if "artifacts only" is the default |
301
+ | `/team --confirm <task>` | create the run but do not dispatch; click "run" in the overlay |
302
+ | `/team --tier <tier> <task>` | pick the process tier (fast / standard / strict) |
303
+ | `/team status` | phase, members, model plan and live violations for every run |
304
+ | `/team models [<run>]` | cost / model plan per role |
305
+ | `/team canvas [<run>]` | render the team canvas (HTML); `--watch` refreshes live |
306
+ | `/team learn` | aggregate logs `METRICS.md` + distilled lessons `LEARNINGS.md` |
307
+ | `/team wait [<run>]` | in-flight task progress (non-blocking) |
308
+ | `/team resume <run-id>` | resume across sessions |
309
+ | `/team uninstall` | reclaim what this plugin laid down under `$DSH_HOME` |
310
+
311
+ Full command list (`/team codeindex` code index, `/team limit` quotas, `/team settle` cold-start settlement, …) — see `/team help`.
288
312
 
289
313
  **Where artifacts land**
290
314
 
@@ -292,6 +316,54 @@ Full command list (`/team canvas` visual canvas, `/team codeindex` code index, `
292
316
  - `$DSH_HOME/expert-team/` — machine-local preferences and cross-project experience: `settings.json`,
293
317
  `session-runs.json`, `LEARNINGS.md`
294
318
 
319
+ ## FAQ
320
+
321
+ **What exactly is it?** A plugin you install into your local `dsh`: `/team <one-sentence goal>` spins up a 12-role subagent team (product / architect / researcher / UI / backend / frontend / data / security / reviewer / QA / DevOps / docs), delivers through a 9-phase gated pipeline inside your workspace, and writes the whole process down as reviewable artifacts.
322
+
323
+ **How is it different from "one agent doing it all"?** It targets the three classic failure modes: **context drift** (hand-offs carry structured returns *and* artifact files, not chat history), **grading your own homework** (review and test are separate roles; a `qa`/`reviewer` verdict is required), and **rework that never converges** (over-budget rounds and unclosed items are stopped by hard gates and reported, not hidden).
324
+
325
+ **Do I have to install other plugins?** **No.** This plugin has zero runtime dependencies; Hindsight (cross-project memory) is **recommended** and `dsh-cost-meter` (cost view) is **optional** — the full flow runs without either. See [Dependencies and recommended plugins](#dependencies-and-recommended-plugins).
326
+
327
+ **Which dsh versions are supported?** `engines.dsh: >=0.1.5-rc.1` (developed and verified on 0.1.5-rc.1); earlier versions are untested. Node.js ≥ 20.
328
+
329
+ **Where does the data live?** Artifacts in your workspace: `<workspace>/team/<run-id>/`. Machine-local preferences and cross-project lessons: `$DSH_HOME/expert-team/` (`settings.json` / `LEARNINGS.md` / `session-runs.json`).
330
+
331
+ **How do I uninstall?** `/team uninstall` reclaims the copies this plugin laid down under `$DSH_HOME` (the skill is registered at runtime and never copied), then remove the plugin via the CLI or the marketplace. **Note:** `LEARNINGS.md` and friends under `$DSH_HOME/expert-team/` are **your data** and are kept.
332
+
333
+ **Does it go online by itself?** No. It only calls the tools the host gives the session (files, shell, subagents); network access depends on the tool face you grant.
334
+
335
+ **Which models does it use?** Whatever the host provides; this plugin supports **per-role model configuration** (the overlay shows which model each member runs), and heterogeneous models can be used for cross-checking.
336
+
337
+ **Does it work without the "Expert team mode" preset?** Yes. `/team` is a host-plane command and runs under any preset; it then falls back to the generic `subagent` (role personas go into the prompt), losing only the configuration-level boundaries (`toolFilter` / `maxDepth: 1`).
338
+
339
+ **Why is the overlay/canvas slow to open?** See [Troubleshooting](#troubleshooting) — since 1.3.5 `/state` no longer reads every sub-session log in full; upgrade to ≥ 1.3.5 and restart `dsh web`.
340
+
341
+ ## Glossary
342
+
343
+ **Roles (12)**
344
+
345
+ | Term | Code id |
346
+ |---|---|
347
+ | Product | `pm` |
348
+ | Architect | `architect` |
349
+ | Researcher | `researcher` |
350
+ | UI/UX | `ui` |
351
+ | Backend | `backend` |
352
+ | Frontend | `frontend` |
353
+ | DBA | `dba` |
354
+ | Security | `sec` |
355
+ | Reviewer | `reviewer` |
356
+ | QA | `qa` |
357
+ | DevOps | `devops` |
358
+ | Docs | `docs` |
359
+
360
+ **Phases (9)**: `clarify` → `research` → `design` → `spec-review` (**hard gate**) → `方案确认` plan approval (**approval gate**, on by default, can be turned off) → `implement` → `review` → `test` → `deliver`. (Ids and Chinese labels come from the single source `lib/vocab.js`.)
361
+
362
+ **Key artifacts**: `SPEC.md` (spec and boundaries) · `RESEARCH.md` · `PLAN.md` · `TASKS.json` (task ledger) · `ROSTER.json` (staffing) · `STATE.json` · `AUTHORITY.md` (single source for write authority) · `REVIEW.md` · `TEST.md` · `SUMMARY.md` · `METRICS.md` (cost and timing — an **aggregate snapshot at the `team/` root**, produced by `/team learn`) · `RUN.log.md`
363
+
364
+
365
+ **Who writes**: each artifact is written **by the role that produces it** into `<run-dir>/`; the lead **only reads artifacts to gate and decide** (it has no `write` tool). `STATE.json` and `RUN.log.md` are maintained **by the runtime**.
366
+
295
367
  ## Layout
296
368
 
297
369
  ```
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  [![license](https://img.shields.io/npm/l/@yangdcm/dsh-expert-team)](https://github.com/yangdcm/dsh-expert-team/blob/main/LICENSE)
7
7
  [![CI](https://github.com/yangdcm/dsh-expert-team/actions/workflows/ci.yml/badge.svg)](https://github.com/yangdcm/dsh-expert-team/actions/workflows/ci.yml)
8
8
 
9
- ![专家团:一句话组队交付](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/hero.svg)
9
+ ![dsh 专家团插件横幅:12 角色多智能体团队 · 9 阶段门控流水线 · 零运行时依赖](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/hero.svg)
10
10
 
11
11
  > **一句话组队交付**:`/team 做一个带登录的支付模块` —— 自动组建 12 角色专家团,走
12
12
  > 澄清 → 调研 → 设计 → 规格评审 → 方案确认 → 实现 → 审查 → 测试 → 交付 的门控流水线,
@@ -18,7 +18,26 @@
18
18
  |---|---|---|---|---|
19
19
  | 各带人设 / `toolFilter` / `maxDepth: 1` | 含 1 道硬门 + 1 道确认门 | 含 136 条变异目录与多组棘轮 | `dependencies: {}` | 无 bundler、无 `prepare` 钩子 |
20
20
 
21
- > 零运行时依赖。推荐同时装 **Hindsight**(跨项目记忆)—— 见[依赖与推荐插件](#依赖与推荐插件)。
21
+ > 零运行时依赖。推荐同时装 **Hindsight**(跨项目记忆)—— 见[依赖与推荐插件](#依赖与推荐插件--dependencies-and-recommended-plugins)。
22
+
23
+ ## 速览 / At a glance
24
+
25
+ | 项目 | 值 |
26
+ |---|---|
27
+ | 包名 | `@yangdcm/dsh-expert-team`(npm 公开包) |
28
+ | 仓库 | <https://github.com/yangdcm/dsh-expert-team> |
29
+ | 宿主 | [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) ≥ **0.1.5-rc.1**(`web` profile) |
30
+ | 运行时依赖 | **无**(`dependencies: {}`;`lib/` 只 import 同目录文件与 Node 内建) |
31
+ | Node.js | ≥ 20 |
32
+ | License | MIT |
33
+ | 安装(一行) | `dsh plugin --profile web add @yangdcm/dsh-expert-team` |
34
+ | 上手(一行) | 会话切到「专家团模式」→ `/team 做一个带登录的支付模块` |
35
+ | 过程产物 | `<你的工作区>/team/<run-id>/`(`SPEC.md` · `PLAN.md` · `TASKS.json` · `REVIEW.md` · `TEST.md` · `SUMMARY.md` …) |
36
+ | 本机数据 | `$DSH_HOME/expert-team/`(`settings.json` · `LEARNINGS.md` · `session-runs.json`) |
37
+
38
+ > dsh 插件 · DeepSeek Harness 多智能体(multi-agent)编排器:角色化 subagent 团队 · 依赖 DAG 并行 · 阶段门控 · 质量门禁 · 工件留痕。
39
+ >
40
+ > 给 LLM / 检索用的索引:[`llms.txt`](https://github.com/yangdcm/dsh-expert-team/blob/main/llms.txt)
22
41
 
23
42
  ## 为谁而做
24
43
 
@@ -46,7 +65,7 @@ UI/UX、前后端、数据、安全、评审、测试、运维、文档这 12
46
65
 
47
66
  <sub>一句诚实边界:它**不是**人类团队 —— 产品级与范围级决策仍由你拍板。</sub>
48
67
 
49
- ![9 阶段门控流水线](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/pipeline.svg)
68
+ ![专家团 9 阶段门控流水线:澄清→调研→设计→规格评审(硬门)→方案确认→实现(依赖 DAG 并行)→审查→测试→交付](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/pipeline.svg)
50
69
 
51
70
  <sub>图 1:9 阶段门控流水线。「规格评审」是**硬门** —— SPEC.md 的「边界与禁止项」没填就不放行(`lib/interception.js`);「方案确认」是默认开启的**确认门**(`identity.keepPlanGate`,可在设置里关掉);「实现」阶段按依赖 DAG **并行扇出**,多个实现者同时开工、各自只改自己那份文件。</sub>
52
71
 
@@ -65,9 +84,10 @@ $ /team 做一个带登录的支付模块
65
84
  ├─ 实现 backend … 按依赖 DAG 并行扇出 → 直接改你工作区的代码
66
85
  ├─ 审查 sec·reviewer 独立评审(换模型交叉验证) → REVIEW.md
67
86
  ├─ 测试 qa 复现、覆盖、回归 → TEST.md
68
- └─ 交付 docs 收尾结论与成本 → SUMMARY.md · METRICS.md
87
+ └─ 交付 docs 收尾结论 → SUMMARY.md
69
88
 
70
89
  全程留痕:TASKS.json · ROSTER.json · STATE.json · AUTHORITY.md · RUN.log.md
90
+ 聚合快照:`METRICS.md` 在 **`team/` 根**(`/team learn` 产出;不是 run 目录模板)
71
91
  落盘位置:<你的工作区>/team/<run-id>/
72
92
  ```
73
93
 
@@ -87,27 +107,27 @@ $ /team 做一个带登录的支付模块
87
107
 
88
108
  ## 看一眼它在干什么
89
109
 
90
- ![全屏画布:阶段条与团队编制](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/canvas.png)
110
+ ![专家团全屏画布:阶段条与进度、角色化 subagent 团队编制(谁在跑、用哪个模型)](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/canvas.png)
91
111
 
92
112
  <sub>图 2:**全屏画布**。看阶段条与进度、团队编制(谁在跑、用哪个模型)、以及 `人 / 事 / 料 / 盘` 四个视角 —— 比浮层更完整的一层视图。</sub>
93
113
 
94
- ![全屏画布 · 事:任务依赖图](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/canvas-tasks.png)
114
+ ![专家团任务依赖图:任务按依赖 DAG 并行,含 repair 与 review 的返工闭环](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/canvas-tasks.png)
95
115
 
96
116
  <sub>图 3:**任务依赖图** —— 11 个任务按依赖 DAG 并行推进;7 个完成、4 个失败。失败会触发 `repair` 与**独立复验**(`repair-1 → review-2 → repair-2 → review-3`),直到通过或被如实判为需修订 —— 这就是「返工不收敛」的硬门禁在真实运行里的样子。</sub>
97
117
 
98
- ![质量门禁违规实时可见](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/panel-gate.png)
118
+ ![专家团质量门禁违规实时横幅:规格边界未填即被插件代码拦下](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/panel-gate.png)
99
119
 
100
120
  <sub>图 4:**门禁违规**。看顶部那条横幅 —— 违规项与拒绝理由(例如"SPEC.md 的边界章节已进入 `implement` 但仍无任何一行填写")由 `lib/interception.js` 挂在宿主 `tools/post-execute` 上当场判出后推出,不是提示词提醒。</sub>
101
121
 
102
- ![成员模型与任务详情](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/panel-live.png)
122
+ ![专家团浮层:角色成员列表、各自使用的模型、任务详情与工件预览](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/panel-live.png)
103
123
 
104
124
  <sub>图 5:**角色编制**。看成员列表 —— 谁在跑、用哪个模型、当前在做什么;展开任一成员可看它的任务与产物。模型可按角色分别配置,异构模型用于交叉验证。</sub>
105
125
 
106
- ![阶段推进与工件预览](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/panel-flow.png)
126
+ ![专家团阶段推进视图:当前阶段、已过阶段与该阶段的工件正文](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/panel-flow.png)
107
127
 
108
128
  <sub>图 6:**阶段与工件**。看阶段条与预览区 —— 当前阶段、已过阶段、以及该阶段真正写下的工件正文(工件是唯一真源,浮层只是它的视图)。</sub>
109
129
 
110
- ![官方设置页里的专家团分节](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/settings.png)
130
+ ![DeepSeek Harness 官方设置页里的「专家团」分节:18 个设置项、中文标签、改动即时生效](https://raw.githubusercontent.com/yangdcm/dsh-expert-team/main/docs/images/settings.png)
111
131
 
112
132
  <sub>图 7:**设置**。看官方 `设置 →「专家团」` 这一页 —— 18 个设置项、中文标签、**改动即保存并即时生效**(上限/轮次/档位门/振荡检测在进程内重算);值存在宿主命名空间 `expert-team`,随插件市场的备份/恢复一起走。</sub>
113
133
 
@@ -131,7 +151,7 @@ $ /team 做一个带登录的支付模块
131
151
  还会堵住整个 `dsh web` 的事件循环。1.3.5 改成只查表(实测 0.0026 ms/次、零次 `readSession`),
132
152
  **修复后的端到端数字待实机复测**;`state-perf-guard.test.mjs` 守着它不许回退。
133
153
 
134
- ## 安装
154
+ ## 安装 / Installation
135
155
 
136
156
  **要求**
137
157
 
@@ -190,7 +210,7 @@ pnpm install && dsh web
190
210
  **排障**:预设丢了、或被同名预设占住 —— **重启一次 `dsh web` 即自愈**(插件加载会重铺),也可跑一次 `/team <任务>`。
191
211
  细节见下面「排障」一节,其中包含那条最容易踩的坑:**不要**用 `expert-team` 这个 id 去「创建 preset」。
192
212
 
193
- ## 依赖与推荐插件
213
+ ## 依赖与推荐插件 / Dependencies and recommended plugins
194
214
 
195
215
  **必需**:无。本插件**零运行时依赖**(`package.json` 无 `dependencies` 字段;`lib/` 只 import 同目录文件与 Node 内建,
196
216
  `client.js` 只 `require('react')`,由宿主提供),只要求宿主 `DeepSeek Harness ≥ 0.1.5-rc.1`(web profile)。
@@ -229,28 +249,80 @@ dsh plugin --profile web add dshmarket
229
249
  > 装了 `dsh-browser` / `dsh-mcp-connector` / `@xmanrui/dsh-im` 之后,活动流里出现的就是它们真实的工具名与参数:
230
250
  > **纯显示,装了更清楚,不装不影响团队功能。**
231
251
 
232
- ## 快速上手
233
-
234
- ```
235
- /team 做一个带登录的支付模块 # 一句话组队(一次性,自动组队并交付)
236
- /team --persist 重构订单模块 # 持久化活团队:成员可反复指挥、跨会话恢复
237
- /team --one-shot 跑一个小活 # 反向覆盖:即使默认设了持久化,这次也只跑一次
238
- /team --no-code 评审现有 API 设计 # 只产出计划/评审/测试工件,不改代码
239
- /team --code 直接改 # 反向覆盖:即使默认设了"只出工件",这次也动代码
240
- /team --confirm 大改版需求 # 先建 run、不自动派工,浮层点「执行」才开工
241
- /team uninstall # 回收本插件铺到 $DSH_HOME 的副本(skill 默认走运行时注册,本就不落地)
242
- /team status # 所有 run 的阶段、成员、模型计划、实时违规
243
- /team resume <run-id> # 跨会话恢复
244
- ```
252
+ ## 快速上手 / Quick start
245
253
 
246
- 完整命令(`/team canvas` 可视化画布、`/team codeindex` 代码索引、`/team learn` 自学习、
247
- `/team limit` 配额、`/team settle` 冷启动清算……)见 `/team help`。
254
+ | 命令 | 作用 |
255
+ |---|---|
256
+ | `/team <一句话目标>` | 一句话组队(一次性,自动组队并交付) |
257
+ | `/team --persist <任务>` | 持久化活团队:成员可反复指挥、跨会话恢复 |
258
+ | `/team --one-shot <任务>` | 反向覆盖:即使默认设了持久化,这次也只跑一次 |
259
+ | `/team --no-code <任务>` | 只产出计划/评审/测试工件,不改代码 |
260
+ | `/team --code <任务>` | 反向覆盖:即使默认"只出工件",这次也动代码 |
261
+ | `/team --confirm <任务>` | 先建 run、不自动派工,浮层点「执行」才开工 |
262
+ | `/team --tier <档位> <任务>` | 指定流程档位(快速档 / 标准档 / 严格档) |
263
+ | `/team status` | 所有 run 的阶段、成员、模型计划、实时违规 |
264
+ | `/team models [<run>]` | 每个角色的成本 / 模型计划 |
265
+ | `/team canvas [<run>]` | 生成可视化团队画布(HTML);`--watch` 实时刷新 |
266
+ | `/team learn` | 聚合日志 → `METRICS.md` + 蒸馏经验到 `LEARNINGS.md` |
267
+ | `/team wait [<run>]` | 查看在飞任务进展(不阻塞) |
268
+ | `/team resume <run-id>` | 跨会话恢复 |
269
+ | `/team uninstall` | 回收本插件铺到 `$DSH_HOME` 的副本(skill 走运行时注册,本就不落地) |
270
+
271
+ 完整命令(`/team codeindex` 代码索引、`/team limit` 配额、`/team settle` 冷启动清算……)见 `/team help`。
248
272
 
249
273
  **产物落在哪**
250
274
 
251
275
  - `<你的工作区>/team/<run-id>/` —— `SPEC / PLAN / TASKS / ROSTER / STATE / REVIEW / TEST / SUMMARY / RUN.log.md` 等工件
252
276
  - `$DSH_HOME/expert-team/` —— 本机偏好与跨项目经验:`settings.json`、`session-runs.json`、`LEARNINGS.md`
253
277
 
278
+ ## 常见问题 / FAQ
279
+
280
+ **它到底是什么?** 一个装在本机 `dsh` 上的插件:`/team <一句话目标>` 会拉起一支 12 角色的 subagent 团队(产品 / 架构 / 调研 / UI / 前后端 / 数据 / 安全 / 评审 / 测试 / 运维 / 文档),按 9 阶段门控流程在你的工作区里交付,并把过程写成可复核的工件。
281
+
282
+ **和"直接让一个 agent 硬做"有什么区别?** 针对三个固定失败模式:**上下文漂移**(阶段与工件双通道交接)、**自己批自己**(评审/测试是独立角色,`qa`/`reviewer` 裁决才算过)、**返工不收敛**(超轮次与未闭环被硬门禁拦下并如实报错)。详见[为什么不是「一个 agent 硬做」](#为什么不是一个-agent-硬做)。
283
+
284
+ **必须再装别的插件吗?** **不必**。本插件零运行时依赖;Hindsight(跨项目记忆)是**推荐**、`dsh-cost-meter`(费用视图)是**可选**,不装也能跑完整个流程 —— 见[依赖与推荐插件](#依赖与推荐插件--dependencies-and-recommended-plugins)。
285
+
286
+ **支持哪些 dsh 版本?** `engines.dsh: >=0.1.5-rc.1`(开发与验证基线 0.1.5-rc.1);更早版本未经测试。Node.js ≥ 20。
287
+
288
+ **数据放在哪?** 工件在你的工作区 `<workspace>/team/<run-id>/`;本机偏好与跨项目经验在 `$DSH_HOME/expert-team/`(`settings.json` / `LEARNINGS.md` / `session-runs.json`)。
289
+
290
+ **怎么卸载?** `/team uninstall` 回收它铺到 `$DSH_HOME` 的副本(skill 走运行时注册、本就不落地),再从命令行或插件市场移除插件。**注意**:`$DSH_HOME/expert-team/` 下的 `LEARNINGS.md` 等是**你的数据**,卸载不会删。
291
+
292
+ **会自己联网吗?** 不会主动联网:它只调用宿主提供的工具(文件、shell、子代理);能不能联网取决于你给会话的工具面。
293
+
294
+ **支持哪些模型?** 由宿主决定;本插件支持**按角色分别配置模型**(浮层里能看到每个成员用哪个模型),异构模型可用于交叉验证。
295
+
296
+ **不切「专家团模式」preset 也能用吗?** 能。`/team` 是 host 平面命令,任何预设下都能跑;此时退回通用 `subagent`(角色人设写进 prompt),少的是配置层的边界保证(`toolFilter` / `maxDepth: 1`)。
297
+
298
+ **浮层/画布打开很慢?** 见[排障](#排障) —— 1.3.5 起 `/state` 不再逐条全量读子会话日志;升级到 ≥ 1.3.5 后重启 `dsh web` 即可。
299
+
300
+ ## 术语 / Glossary
301
+
302
+ **角色(12)**
303
+
304
+ | 中文 | English | 代码标识 |
305
+ |---|---|---|
306
+ | 产品 | Product | `pm` |
307
+ | 架构 | Architect | `architect` |
308
+ | 调研 | Researcher | `researcher` |
309
+ | 界面设计 | UI/UX | `ui` |
310
+ | 后端 | Backend | `backend` |
311
+ | 前端 | Frontend | `frontend` |
312
+ | 数据 | DBA | `dba` |
313
+ | 安全审计 | Security | `sec` |
314
+ | 评审 | Reviewer | `reviewer` |
315
+ | 测试 | QA | `qa` |
316
+ | 运维 | DevOps | `devops` |
317
+ | 文档 | Docs | `docs` |
318
+
319
+ **阶段(9)**:`clarify` 澄清 → `research` 调研 → `design` 设计 → `spec-review` 规格评审(**硬门**)→ `方案确认` 方案确认(**确认门**,默认开、可关)→ `implement` 实现 → `review` 审查 → `test` 测试 → `deliver` 交付。(id 与中文名的唯一真源是 `lib/vocab.js`。)
320
+
321
+ **主要工件**:`SPEC.md`(规格与边界)· `RESEARCH.md`(调研)· `PLAN.md`(方案)· `TASKS.json`(任务台账)· `ROSTER.json`(编制)· `STATE.json`(状态)· `AUTHORITY.md`(写入权限单源)· `REVIEW.md`(评审)· `TEST.md`(测试)· `SUMMARY.md`(交付总结)· `METRICS.md`(成本与耗时:**`team/` 根的聚合快照**,`/team learn` 产出)· `RUN.log.md`(运行日志)
322
+
323
+
324
+ **谁写**:每个工件由**产出它的角色**自己 `write` 进 `<run-dir>/`;lead **只读工件做门控与裁决**(它没有 `write` 工具)。`STATE.json` 与 `RUN.log.md` 由**运行时**维护。
325
+
254
326
  ## 插件结构
255
327
 
256
328
  ```
@@ -299,7 +371,7 @@ dsh 安装里插件自带的 Config schema(dsh 路径自动探测,可用 `DS
299
371
  **浮层打开很慢 / 整个 `dsh web` 发卡?** 1.3.5 之前 `/state` 会逐条全量读子会话日志(实测热态 7–10 s、
300
372
  冷态 283 s),并堵住事件循环。升级到 ≥ 1.3.5 后重启 `dsh web` 即可。
301
373
 
302
- ## 自定义预设(想改专家团默认行为时)
374
+ ## 自定义预设 / Custom presets(想改专家团默认行为时)
303
375
 
304
376
  - **创建**:`设置 → Agent 预设 → 用「创造模式」创作自定义预设`(其机制是"复制一份既有预设",产出落在 `$DSH_HOME/.agent-presets/<id>/`)。
305
377
  - **要定制专家团,请以「专家团模式」为源、换一个你自己的 id**(例如 `my-team`):复制出来的目录天然带上 12 个角色工具与它的 skill 目录,
package/client.js CHANGED
@@ -1343,10 +1343,13 @@ window.__ModuleLoader__.load({
1343
1343
  var planMsgS = useState(''); var planMsg = planMsgS[0], setPlanMsg = planMsgS[1]
1344
1344
  var err = useState(''); var errV = err[0], setErr = err[1]
1345
1345
 
1346
- function stateUrl() {
1346
+ function stateUrl(section) {
1347
1347
  var url = '/plugins/dsh-expert-team/state', q = []
1348
1348
  if (sessionId) q.push('sessionId=' + encodeURIComponent(sessionId))
1349
1349
  if (selRunV && selRunV.workspace && selRunV.runId) { q.push('workspace=' + encodeURIComponent(selRunV.workspace)); q.push('run=' + encodeURIComponent(selRunV.runId)) }
1350
+ // `section`:渐进式状态(2026-09-15 性能修复 #3)。首屏只发 `summary`(便宜),
1351
+ // 人/料/事件流晚一拍、低频拉(见下面的双订阅)。不传 = 完整负载(服务端向后兼容)。
1352
+ if (section) q.push('section=' + encodeURIComponent(section))
1350
1353
  return url + (q.length ? ('?' + q.join('&')) : '')
1351
1354
  }
1352
1355
  // ── 数据到达后的副作用(**本组件不再自带轮询器**;由 stateHub 统一驱动)─────────
@@ -1354,9 +1357,22 @@ window.__ModuleLoader__.load({
1354
1357
  // 画布另走 liveStore 的 setInterval ⇒ 画布一开就有 2–3 个轮询并发压同一个重端点
1355
1358
  // (实测 10 秒 7 发、6 次重叠、单发被拖到 5.9/8.3 s)。现在全部经 stateHub:
1356
1359
  // 同 URL 同刻只跑一次、全局单一时钟、统一退避(见 stateHub 的注释)。
1360
+ // ── 分节负载的合并(2026-09-15 性能修复 #3)────────────────────────────────
1361
+ // 为什么必须合并而不是替换:首屏只发 `?section=summary`(便宜:阶段/进度/计数),
1362
+ // "人/料/事件流"晚一拍才到。若直接 `setData(d)`,摘要那一拍会把上一拍已知的成员/事件流
1363
+ // **抹掉**(UI 闪空、通知误判"成员消失")。规则:**新负载里有的键覆盖旧值,没有的键保留**
1364
+ // —— 缺块 ≠ 空数据;`sections` 字段如实告诉 UI"这份负载包含哪些块"(两种零可区分)。
1365
+ function mergeStatePayload(prev, d) {
1366
+ if (!d || typeof d !== 'object') return d
1367
+ if (!prev || typeof prev !== 'object' || prev.ok !== true) return d
1368
+ var out = {}, k
1369
+ for (k in prev) { if (Object.prototype.hasOwnProperty.call(prev, k)) out[k] = prev[k] }
1370
+ for (k in d) { if (Object.prototype.hasOwnProperty.call(d, k)) out[k] = d[k] }
1371
+ return out
1372
+ }
1357
1373
  function onState(d) {
1358
1374
  if (d && d.ok) {
1359
- setData(d); setErr(''); reschedule(d)
1375
+ setData(function (prev) { return mergeStatePayload(prev, d) }); setErr(''); reschedule(d)
1360
1376
  // N2: keep the composer takeover in sync with pendingDecision
1361
1377
  // 批 0-2:把 /state 已经返回的 runId/workspace 一并传出,供横幅拍板时回传给 /decide
1362
1378
  try { if (exports._syncPending) exports._syncPending(d.pendingDecision, sessionId, d.runId, d.workspace) } catch (e) {}
@@ -1391,13 +1407,18 @@ window.__ModuleLoader__.load({
1391
1407
  }
1392
1408
  useEffect(function () {
1393
1409
  if (!isOpen && !viewMode) return undefined // panel closed & not canvas → no polling
1394
- var off = stateHubSubscribe(stateUrl(), dispCfg.pollMs, onState)
1395
- // catch up instantly when the user returns to the tab / window
1396
- var onVis = function () { if (document.visibilityState === 'visible') stateHubFetch(stateUrl(), onState) }
1397
- var onFocus = function () { stateHubFetch(stateUrl(), onState) }
1410
+ // **双订阅**(性能修复 #3):`summary` 便宜(阶段/进度/计数)⇒ 按 `pollMs` 快拉,
1411
+ // 首屏立刻有内容;`people,feed,artifacts` 贵(成员解析/事件流/日志尾+git)⇒ 至少 6 秒一次,
1412
+ // 到达后由 `mergeStatePayload` 合并进同一份 data(**不会**把摘要拍掉的字段抹掉)。
1413
+ // stateHub URL 各自计时(per-URL due),所以"一快一慢"不会互相拖。
1414
+ var off = stateHubSubscribe(stateUrl('summary'), dispCfg.pollMs, onState)
1415
+ var offDetail = stateHubSubscribe(stateUrl('people,feed,artifacts'), Math.max(dispCfg.pollMs, 6000), onState)
1416
+ // catch up instantly when the user returns to the tab / window(只补便宜那一份)
1417
+ var onVis = function () { if (document.visibilityState === 'visible') stateHubFetch(stateUrl('summary'), onState) }
1418
+ var onFocus = function () { stateHubFetch(stateUrl('summary'), onState) }
1398
1419
  document.addEventListener('visibilitychange', onVis)
1399
1420
  window.addEventListener('focus', onFocus)
1400
- return function () { off(); document.removeEventListener('visibilitychange', onVis); window.removeEventListener('focus', onFocus) }
1421
+ return function () { off(); offDetail(); document.removeEventListener('visibilitychange', onVis); window.removeEventListener('focus', onFocus) }
1401
1422
  }, [sessionId, selRunV, isOpen, viewMode, dispCfg.pollMs])
1402
1423
 
1403
1424
  // Make the docked panel reserve space (shift the shell frame) instead of
@@ -2350,23 +2371,32 @@ window.__ModuleLoader__.load({
2350
2371
  // ③ 间隔 = clamp(max(基础间隔, 上次耗时×2), 基础间隔, 30000);
2351
2372
  // 团队有人在跑时基础间隔按 40% 加速(面板原有意图,现在对徽章/画布同样生效)。
2352
2373
  // 纪律不变:**未回绝不发下一个**(晚一点看到状态,好过把 web 拖死)。
2353
- var stateHub = { inflight: {}, lastMs: {}, subs: [], timer: 0, busy: false }
2374
+ var stateHub = { inflight: {}, lastMs: {}, lastAt: {}, subs: [], timer: 0, busy: false }
2354
2375
  function stateHubNow() { return (window.performance && performance.now) ? performance.now() : Date.now() }
2355
2376
  function stateHubUrls() { var out = []; stateHub.subs.forEach(function (x) { if (out.indexOf(x.url) < 0) out.push(x.url) }) ; return out }
2356
- function stateHubBase() {
2377
+ // 每个 URL 的**自己的**基础间隔(同一 URL 有多个订阅者时取最小 ⇒ 谁最急听谁的)。
2378
+ // 性能修复 #3 的"一快一慢"就靠它:`summary` 3 s 一发、`people,feed,artifacts` ≥ 6 s 一发,
2379
+ // 两者互不拖拽(旧实现是"全局取最小 base、每个 tick 把所有 URL 都拉一遍" ⇒ 慢的那份被快钟拖着跑)。
2380
+ function stateHubBaseOf(u) {
2357
2381
  var base = 0
2358
- stateHub.subs.forEach(function (x) { if (!base || x.base < base) base = x.base })
2382
+ stateHub.subs.forEach(function (x) { if (x.url === u && (!base || x.base < base)) base = x.base })
2359
2383
  if (!base) base = 3000
2360
2384
  return stateHub.busy ? Math.max(300, Math.round(base * 0.4)) : base
2361
2385
  }
2386
+ function stateHubDueAt(u) { return (stateHub.lastAt[u] || 0) + stateHubBaseOf(u) }
2362
2387
  function stateHubSchedule() {
2363
2388
  clearInterval(stateHub.timer)
2364
2389
  stateHub.timer = 0
2365
2390
  if (!stateHub.subs.length) return
2366
2391
  var slowest = 0
2367
2392
  stateHubUrls().forEach(function (u) { if ((stateHub.lastMs[u] || 0) > slowest) slowest = stateHub.lastMs[u] })
2368
- var base = stateHubBase()
2369
- var next = Math.max(base, Math.min(30000, Math.round(slowest * 2)))
2393
+ var now = stateHubNow(), earliest = Infinity
2394
+ stateHubUrls().forEach(function (u) { var at = stateHubDueAt(u); if (at < earliest) earliest = at })
2395
+ if (!isFinite(earliest)) earliest = now + 3000
2396
+ // 基础等待 = 最早到期的那一刻;再按"最慢一次请求 ×2"放大(未回绝不发下一个的纪律仍由
2397
+ // in-flight 守卫保证,这里的放大只是为了别把慢端点压成队列)。
2398
+ var wait = Math.max(200, Math.round(earliest - now))
2399
+ var next = Math.max(wait, Math.min(30000, Math.round(slowest * 2)))
2370
2400
  stateHub.timer = setInterval(stateHubTick, next)
2371
2401
  }
2372
2402
  function stateHubDeliver(url) {
@@ -2380,6 +2410,8 @@ window.__ModuleLoader__.load({
2380
2410
  var p = stateHub.inflight[url]
2381
2411
  if (p) { p.then(function (d) { try { onData(d) } catch (e) {} }); return } // 同刻同 URL:复用在飞结果
2382
2412
  var t0 = stateHubNow()
2413
+ // 记"上次发出时刻":即使**失败**也记(否则失败会变成热循环重试)。
2414
+ stateHub.lastAt[url] = t0
2383
2415
  p = fetch(url).then(function (r) { return r.ok ? r.json() : null }).then(function (d) {
2384
2416
  delete stateHub.inflight[url]
2385
2417
  stateHub.lastMs[url] = Math.max(0, stateHubNow() - t0)
@@ -2390,7 +2422,9 @@ window.__ModuleLoader__.load({
2390
2422
  }
2391
2423
  function stateHubTick() {
2392
2424
  if (!stateHub.subs.length) { clearInterval(stateHub.timer); stateHub.timer = 0; return }
2393
- stateHubUrls().forEach(function (u) { stateHubFetch(u, stateHubDeliver(u)) })
2425
+ var now = stateHubNow()
2426
+ // **只发改到期的那些 URL**(不是每个 tick 把所有 URL 拉一遍)
2427
+ stateHubUrls().forEach(function (u) { if (stateHubDueAt(u) <= now && !stateHub.inflight[u]) stateHubFetch(u, stateHubDeliver(u)) })
2394
2428
  stateHubSchedule()
2395
2429
  }
2396
2430
  function stateHubSubscribe(url, base, onData) {
@@ -2409,7 +2443,7 @@ window.__ModuleLoader__.load({
2409
2443
  // ── 共享 state 轮询(HeaderButton / LiveCapsule 复用,单例;无订阅者即停)──
2410
2444
  var liveStore = { data: null, sid: '', off: null, subs: new Set() }
2411
2445
  var LIVE_BASE_MS = 2500 // 徽章/画布的基础节奏(与面板的 dispCfg.pollMs 取更小者由 hub 统一决定)
2412
- function liveUrl(sid) { return '/plugins/dsh-expert-team/state?sessionId=' + encodeURIComponent(sid) }
2446
+ function liveUrl(sid) { return '/plugins/dsh-expert-team/state?sessionId=' + encodeURIComponent(sid) + '&section=people,feed' }
2413
2447
  // 手动催一次(子代理刚出现时立刻刷新一次徽章,不必等下一个 tick)
2414
2448
  function liveTick() {
2415
2449
  var sid = liveStore.sid