@mstar-harness/dsh 3.8.0 → 3.8.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.
Files changed (51) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +142 -194
  3. package/README.zh.md +29 -17
  4. package/bundle/README.md +83 -171
  5. package/dist/client/index.d.ts +17 -8
  6. package/dist/client/panel/MstarPanelTitle.d.ts +13 -0
  7. package/dist/client/panel/PanelView.d.ts +56 -52
  8. package/dist/client/panel/TabNav.d.ts +17 -14
  9. package/dist/client/panel/definition.d.ts +23 -0
  10. package/dist/client/panel/engine-status-client.d.ts +84 -6
  11. package/dist/client/panel/graph/project-graph.d.ts +35 -64
  12. package/dist/client/panel/graph/schema.d.ts +1 -2
  13. package/dist/client/panel/guards.d.ts +41 -1
  14. package/dist/client/panel/locale.d.ts +1 -1
  15. package/dist/client/panel/mstar-glyph.d.ts +22 -0
  16. package/dist/client/panel/pages/AgentListPage.d.ts +71 -0
  17. package/dist/client/panel/pages/EventLogPage.d.ts +7 -4
  18. package/dist/client/panel/pages/IterationInfoSection.d.ts +16 -13
  19. package/dist/client/panel/pages/IterationTaskPage.d.ts +13 -14
  20. package/dist/client/panel/panel-store.d.ts +28 -0
  21. package/dist/client/panel/sidebar.d.ts +13 -7
  22. package/dist/client/panel/state-section.d.ts +25 -3
  23. package/dist/client/panel/use-mstar-engine-status.d.ts +39 -14
  24. package/dist/client/panel/zones/Legend.d.ts +5 -3
  25. package/dist/client/panel/zones/TaskBoard.d.ts +13 -9
  26. package/dist/client.js +1038 -1242
  27. package/dist/engine-status-endpoint.d.ts +85 -8
  28. package/dist/engine-status-store.d.ts +91 -1
  29. package/dist/engine-status-wire.d.ts +9 -0
  30. package/dist/gates/_shared.d.ts +61 -9
  31. package/dist/gates/adapter.d.ts +32 -2
  32. package/dist/gates/agent-flow.d.ts +312 -60
  33. package/dist/gates/catalog.d.ts +59 -38
  34. package/dist/gates/dispatch.d.ts +11 -2
  35. package/dist/gates/goal-bridge.d.ts +10 -130
  36. package/dist/gates/plan-mode-bridge.d.ts +20 -11
  37. package/dist/gates/role-persona.d.ts +16 -0
  38. package/dist/gates/steering.d.ts +41 -0
  39. package/dist/gates/workflow-ledger.d.ts +31 -4
  40. package/dist/gates/workflow-selection.d.ts +41 -20
  41. package/dist/index.js +1208 -394
  42. package/dist/types.d.ts +50 -18
  43. package/harness-commands/amazing-pr-review.md +2 -0
  44. package/harness-commands/codebase-audit.md +2 -0
  45. package/harness-skills/mstar-host/SKILL.md +3 -1
  46. package/harness-skills/mstar-host/references/dsh-workflow-scripts.md +424 -0
  47. package/harness-skills/mstar-host/references/dsh.md +259 -266
  48. package/harness-skills/mstar-roles/references/project-manager.md +2 -0
  49. package/harness-skills/mstar-sdd/SKILL.md +2 -0
  50. package/package.json +66 -64
  51. package/dist/client/panel/pages/AgentCanvasPage.d.ts +0 -345
@@ -34,226 +34,94 @@ or a custom profile).
34
34
  - Web client plugin (workflow panel): the same `mstar` bundle row carries a
35
35
  browser client half (`dsh.client` + `exports["./client"]`) discovered
36
36
  automatically by `ClientModuleHostService` — no separate profile layer or
37
- install step. It registers a **`conversation.view`** view-ring tab
38
- (`id: 'mstar-workflow'`, `order: 20`) labeled **"MStar 工作流" / "MStar
39
- Workflow"** rendering the latest `mstar-engine-status` catalog **anchor** row
37
+ install step. It registers a **right-Sidebar page tab type** (`id:
38
+ '@mstar-harness/dsh'`, `kind: 'mstar-workflow'`, one guide-page capsule at
39
+ `order: 20`) labeled **"启明星工作流" / "Morning
40
+ Star Workflow"** rendering the latest `mstar-engine` catalog **anchor** row
40
41
  — the persisted source is the bare first-party `plugin` arm
41
- (`{ kind: 'plugin', plugin: 'mstar-engine-status', form: 'catalog' }`, no
42
- payload members), and the payload is fetched from the host's
42
+ (`{ kind: 'plugin', plugin: 'mstar-engine', form: 'catalog' }`, no
43
+ payload members; the anchor reader also accepts the legacy
44
+ `mstar-engine-status` identity from persisted logs), and the payload is fetched from the host's
43
45
  `/api/mstar/engineStatus` endpoint (the gateway owns the route; the browser
44
46
  half calls `connection.rpc.call('/api', 'mstar/engineStatus', { args: { sessionId, cwd } })`
45
- and renders the session's stored snapshot, or an explicit unavailable reason) —
46
- as the
47
- **MStar Workflow layout** — a right sidebar (plans ≤5 in time-desc order +
48
- `+N more`, open residual findings ≤10 with severity chips + overflow hint,
49
- policy with **enforcement first** then push / worktree / control worktree,
50
- leases, knowledge, direction) over a bottom **fixed meta dock** (version +
51
- harness dir; small muted, hairline-separated, does NOT scroll with the
52
- sidebar digest — the former header row was removed), and an **HTML/CSS zone
53
- dashboard** (the react-flow cyclic graph was removed in plan
54
- `20260810-panel-canvas-zones`): the canvas fills the Tab (the page never
55
- scrolls; the zone container is the only scroll body) with an **iteration
56
- zone** (Step 1–5 stepper + `Step N/5` badge + active-highlight / inactive
57
- dimmed state; the steps carry a FOUR-STATE machine — `current` / `next` /
58
- `done` / `idle` (plan `20260812-panel-f5-iteration-zone-fix` Task 1): every
59
- step BEFORE the current one projects `done`「已完成」(completed — a finished
60
- Step 1 must not read as idle while Step 2 is current), `next` is the single
61
- forward target, `idle` is schema-only + the branch panel — iteration base /
62
- target / spec integration, rendered only while active; the expanded head is
63
- a LEFT-RIGHT SPLIT — branches (small left half, WIDTH-CAPPED — `flex: 0 1
64
- 260px` + `max-width: 280px`, never stretches with the container; the <860px
65
- column stack resets to content height) + steps (large right half, `flex: 1 1
66
- 0` absorbing the remaining width) via `data-iteration-head-split`, stacking
67
- on narrow widths, and NO branch panel
68
- when there is no active iteration; the current step follows the steering
69
- compass: `compassStatus: 'active'` (Phase 1 in flight) → Step 1
70
- (iteration-start) is CURRENT with verdict `unknown` — no PASS/FAIL badge,
71
- plan `20260811-panel-f4-iteration-zone`; **the iteration info section is
72
- SHARED by the tasks AND agents tabs** (plan
73
- `20260812-panel-f5-design-system` Task 8, user round-4 decision #4 — one
74
- `IterationInfoSection` component, both tabs render the same `view.iteration`
75
- block: summary + steps + branches), a **tasks zone** (5-column
76
- kanban: Todo / InProgress / InReview / Done / `blocked-unknown` — the
77
- Blocked state and the former `unknown` catch-all fold into ONE merged
78
- column titled「受阻/未知」/「Blocked / Unknown」, plan
79
- `20260813-panel-quick-fixes` Task 1 — with count badges; every column
80
- caps its rendered rows at `PLAN_CAP` and shows a clickable 「更多」/「收起」
81
- expand button (`data-kanban-more` anchor) unfolding the full column — the
82
- projection keeps ALL plan rows, the cap is a render concern never a
83
- discard), an **agent-execution zone** (the FOUR EXPECTED_ROLE_FLOW stage/phase
84
- columns — review-edit-chain → sdd-implement → qc-tri → qa-gate, the
85
- terminal stage; the former `sdd-task-review` stage is removed and its SDD
86
- L2 reviewer is now the PIPELINE role `code-reviewer` (v2.1.1, the former
87
- `generalPurpose` seat) — a strict FOUR-column layout with NO standalone
88
- unknown column (plan `20260812-panel-f5-design-system` Task 5, user
89
- 2026-08-12 round-2 decision — the former rightmost UNKNOWN column of plan
90
- `20260812-panel-f5-agent-layout` is superseded): the `general` bucket
91
- sinks into an **unknown SUB-PARTITION at the bottom of the `qa-gate`
92
- column** (a `data-sub-bucket="unknown"` caption row 「unknown / 未匹配角色」
93
- after the last qa-gate card, then the general cards; the standalone
94
- on-demand column was already removed in the agent-layout plan); `explore`
95
- is removed — no card, no column. The columns are laid out in **TWO
96
- side-by-side Phase groups** (plan `20260812-panel-f5-design-system` Task 8,
97
- user round-4 decision #2; side-by-side layout per plan
98
- `20260813-panel-agent-canvas-legend-layout` Task 2): the **Phase 1 group
99
- on the LEFT** (review-edit-chain — the sequential Review & Edit chain:
100
- product-manager → architect → writing-specialist) and the **Phase 2 group
101
- on the RIGHT** (sdd-implement → qc-tri → qa-gate — the iterative plan
102
- loop), top-aligned (all group label rows share the same `y = PAD_Y`), each
103
- with its group label row; the **Phase-2 label annotates the CURRENT PLAN**
104
- (projected `agents.activePlanId` = the first InProgress `state.plans[]`
105
- row, `data-canvas-group-plan`; `+N more` when several plans run in
106
- parallel, muted「无进行中 plan」when none). The `sdd-implement` column is split into SUB-BUCKETS by
107
- the PROJECTED `entity.bucket` (never a render guess): the **implementor**
108
- partition ABOVE — the flow roles in the stage's original order
109
- (fullstack-dev / fullstack-dev-2 / frontend-dev), then the on-demand
110
- roles (ops-engineer / prompt-engineer, carrying the **on-demand badge** —
111
- the standalone on-demand column is gone) — and the **sdd-reviewer**
112
- partition BELOW (code-reviewer, idle included), with the implementor /
113
- sdd-reviewer caption labels; `zone: 'on-demand'` entities live in the
114
- implementor partition, `zone: 'general'` entities render in the qa-gate
115
- column's bottom unknown sub-partition. The subagent ENTITY cards aggregate **by role** from actual
116
- dispatch evidence: the same role across sessions folds into one card ×N,
117
- and every off-roster dispatch (the former `generalPurpose` SDD reviewer,
118
- `scout`, anonymous `role === ''`) folds into the single `general` bucket
119
- entity — the card is ROLE-TITLED (the role id, e.g. `fullstack-dev`); the
120
- agent session id / task tag (`planId#taskId`) ride the RECORD line, never
121
- the title. Cards show the role chip / status point / ×N count; running
122
- entities carry the business glow-pulse
123
- highlight, un-evidenced stages render the dashed "待执行" pending
124
- placeholder with their expected role chips, un-evidenced KNOWN_AGENTS
125
- members render dashed idle cards (the full 14-role roster is never
126
- hidden), and the header shows the `N executing · M pending` summary.
127
- Cards carry the projected **emphasis tier** (plan
128
- `20260812-panel-f5-design-system` Task 4, design doc §3): `emphasis:
129
- 'current' | 'next' | 'off' | null` — the iteration's current-phase roles
130
- render at **100%** chrome intensity, later-phase expected roles at **75%**,
131
- already-passed / stage-less (on-demand, general) roles at **45%**, and
132
- `null` (no iteration / unresolved transition) applies NO override — always
133
- a chrome **alpha mix** (`--mstar-canvas-emphasis-*` tokens; never a
134
- whole-card `opacity`, so the status point + running glow stay opaque).
135
- Settled entities get a **standalone GREEN done frame + green ✓** (plan
136
- `20260812-panel-f5-design-system` Task 8, user round-4 decisions #1/#3:
137
- `data-agent-done="true"` — a full-strength success border + 1px ring on
138
- the rounded card body + the ✓ in the status point) **ONLY when
139
- `emphasis ≠ 'off'`** — an off-tier role (already-passed / stage-less
140
- on-demand + general) renders the muted dot instead and NEVER shows the
141
- completion marker (the completed state never appears on a stage-less
142
- role). The canvas filters to the CURRENT iteration only (plan
143
- `20260813-panel-quick-fixes` Task 2): dispatch evidence projects for the
144
- current iteration's plans — the steering compass `iterationId` when
145
- active, else the nearest iteration derived from the catalog
146
- `plans[].iterationRefs` (the most-recent plan's refs by 8-digit id date
147
- prefix + doneAt); provably cross-iteration events produce no entity/edge
148
- (the roster keeps its idle cards); plan-less / unknown-plan / standalone
149
- dispatches are never hidden. Status honesty (Task 2): `advisory` is NO
150
- LONGER terminal — a soft-enforcement dispatch falls through to its paired
151
- settle (green ✓ when a settle exists, `running` when none) while `denied`
152
- stays terminal; the advisory verdict still renders in the event log. The
153
- canvas legend sits BELOW the viewport (Task 3 — moved from above, user
154
- 2026-08-13 feedback).
155
- Edges (plan `20260812-panel-f5-design-system` Task 5, design doc §2):
156
- the `expected` stage skeleton arrows AND the ANIMATED **next** edge (the
157
- former `@keyframes agent-dash-flow` dash-flow arrow of plan
158
- `20260810-panel-agent-flow-zone`) are **REMOVED** — flow order is implied
159
- by the fixed column order + column labels, the current position by the
160
- running card glow + status point — leaving TWO semantic kinds: the
161
- evidence-driven **`actual` handoff** edges (same-plan ts-adjacent dispatch
162
- entity-key pairs, `general` endpoints filtered, ≤1 per entity pair) drawn
163
- as **bezier `C` curves** anchored to card **PORTS** — 4 fixed
164
- edge-midpoint ports (north / south / east / west; static-invisible,
165
- hover-revealed as small dots) with the arrow tip pulled back to a **10px
166
- standoff** off the port — the arrow follows the line's local tangent at
167
- the anchor (**H1**), and no line's stroke or arrow crosses any text
168
- (**H2**: standoff + side-gap routing, design doc §2.0/§2.5/§2.6;
169
- tightened in plan `20260813-panel-quick-fixes` Task 3 — same-column
170
- vertical flows whose center-x line would cross an in-between card body
171
- (e.g. fullstack-dev → frontend-dev skipping an idle fullstack-dev-2)
172
- reroute into the column's LEFT side gap, forward AND reverse, and reverse
173
- horizontal beziers keep direction-aware control points BETWEEN the
174
- endpoints so they never bulge into the adjacent column) — plus
175
- the **bidirectional supervise line** (plan `20260812-panel-f5-agent-layout`
176
- Task 1/2) — ONE static design-knowledge sub-bucket edge inside the
177
- `sdd-implement` column (implementor ↔ sdd-reviewer — the mstar-sdd
178
- mutual-supervision contract), now anchored at the **side-gap vertical
179
- anchor** (`x = card right edge + 18px`, vertical bezier flow, arrows along
180
- the vertical tangent — design doc §2.5/§2.7); dim dashed by default, lit
181
- business SOLID when the projected `evidenced` flag is true —
182
- evidence-driven lighting, never a fabricated activation); the 事件记录 tab
183
- (`EventLogPage`, spec panel-tabs §5, plan `20260811-panel-event-log`) is a
184
- NON-canvas log page with two partitions — **Agent 流转事件** (`view.events`
185
- ≤50 latest-first; off-pipeline unexpected dispatches fold in once via
186
- `expected: false` and carry a dispatch-only 「未匹配角色」 badge — settle
187
- rows are completion records and never flag as unexpected) and **违规记录**
188
- (`view.violations`, gate violations with severity/code/message); every row
189
- is an expandable native `<details>` (no-JS, keyboard-accessible) whose body
190
- shows the full catalog fields — missing fields render「—」, never a guessed
191
- value. Layout (plan `20260811-panel-f3-agent-general`): the two partitions
192
- render SIDE BY SIDE in a locked-height two-column grid
193
- (`repeat(2, minmax(0, 1fr))` — the page never scrolls as a whole; each
194
- partition pins its title and owns an internal `overflow-y` scroll on its
195
- row list; plan `20260813-panel-quick-fixes` Task 4 root-caused the
196
- whole-page scroll — the panel root opts into the host
197
- `data-conversation-composer-overlay` (the host's documented full-height
198
- opt-in), so the host `.viewArea` becomes a definite-height container and
199
- `height:100%` resolves: `.rowList`'s `overflow-y: auto` now scrolls
200
- INSIDE the partition and the host page no longer scrolls, with bottom
201
- clearance reserving the floating composer via the host-published
202
- `--dsh-composer-height`), falling back to two stacked 50/50 locked rows
203
- below 1200px —
204
- the `data-event-log-*` anchor family is unchanged. The canvas-corner **`AgentEventDock`** is REMOVED with the page
205
- (无双份日志 — its row layout + status chips migrated into `EventLogPage`);
206
- the fixed footer bar (zone legend + gate summary + violations) died with
207
- the WorkflowCanvas in plan `20260811-panel-tabs-shell` — the footer that
208
- remains is the freshness marker. Empty branches (spec §2 — plan
209
- `20260812-panel-f5-agent-layout` Task 3): waiting keeps the muted hint,
210
- and NO harness renders a **CENTERED inactive-state card** (folder icon +
211
- 「No Morning Star harness detected」 title + the hint copy — the detail
212
- panel stays inactive, no tabs / no sidebar, activating automatically once
213
- a harness is detected; the `data-mstar-empty="no-harness"` anchor stays on
214
- the title, `data-mstar-graph` on the main container). Below 1200px
215
- the zones stack vertically. Pure `projectGraph` projection (never throws,
216
- explicit degraded states — muted empty states, never orange warn boxes).
217
- The branches block left the sidebar in plan `20260810-panel-sidebar-info`
218
- (its anchor fields ride the row's per-session snapshot payload, NOT the
219
- persisted source — the source is the bare first-party `plugin` arm; the
220
- iteration zone renders them via plan `20260810-panel-canvas-zones`); refresh
221
- follows the session snapshot, no polling — while the main agent is ACTIVELY
222
- orchestrating, a
223
- ledger record (dispatch/settle) invalidates the workspace's TTL-cached
224
- catalog row so the next pre-step rebuilds and (digest text change)
225
- re-injects it, and the panel refreshes per step (seconds, not the 60 s TTL);
226
- while the main agent IDLES (waiting, no tool calls) the panel keeps the
227
- LAST snapshot — no live push channel (documented limit, plan
228
- `20260811-panel-f4-timeliness`). Bundle served at
229
- `/plugins/@mstar-harness/dsh/client.js` (closure-factory CJS with NO graph
230
- library inlined — react-flow removed; the build asserts the bundle contains
231
- no `xyflow`/`reactflow` markers, no `@deepseek-ai/*` value imports, and no
232
- `import.meta` / ESM statements — the loader runs plugin bundles as classic
233
- scripts). **Known limitations**: the stepper's Step 1 (iteration-start) IS
234
- the current step while the steering compass is `status: active` (Phase 1 in
235
- flight — catalog `compassStatus` field), carrying NO PASS/FAIL badge (Phase
236
- 1 has no gate verdict); Step 5 (merge-ready) can never be the CURRENT step —
237
- the engine phase gate only evaluates Phase 2→3→4 (merge-ready is never a gate
238
- transition); it renders `next` only while Step 4 (pr-delivery) is current,
239
- idle otherwise;
240
- the current step follows the TTL-refreshed `compassStatus` — up to one
241
- catalog interval (60 s) behind a mid-session `active`→`locked` flip (bounded,
242
- documented staleness, never a wrong verdict); the agent-entity
243
- status derivation pairs a PAIRED settle exactly by its dispatch identity
244
- (`agent`, `role`, `planId`, `taskId` — under QC-tri N=3 concurrency each
245
- settle lands on ITS dispatch), and an unpaired dispatch stays `running`
246
- (no paired settle — never guessed, never faked); the current-iteration
247
- filter with NO steering compass infers the iteration from plan ids
248
- (8-digit date prefix) + doneAt — deterministic, documented heuristic, and
249
- only provably cross-iteration events are dropped; no historical back-scan of
250
- resumed long logs; no custom
251
- top-level slot (the `conversation.view` tab is the only session-level panel
252
- seat without dsh-private layout changes); no-session → shell hero
253
- (strict-session view ring). Panel acceptance is dual-track: in-loop browser
254
- harness verification (agent-browser/CDP against the rebuilt bundle,
255
- iteration guides record the verified runs) plus user-restart final GUI
256
- acceptance.
47
+ and renders the session's stored snapshot, or an explicit unavailable
48
+ reason). **Morning Star Workflow layout**: a narrow-column shell bound to the
49
+ sidebar pane's definite height — exactly three zones: the **section nav**
50
+ (任务迭代 / 代理执行 / 事件记录; `data-mstar-tab-nav`), the panel-owned
51
+ **single scroll body** (`[data-mstar-scroll]` — the ONLY `overflow-y`
52
+ element in the panel; nothing scrolls horizontally; `data-mstar-graph`
53
+ rides it), and the pinned **meta dock** (version + harness dir; never
54
+ scrolls). The workspace-state digest (plans ≤5 time-desc + `+N more`,
55
+ open residual findings ≤10 with severity chips + overflow hint, policy
56
+ with **enforcement first** then push / worktree / control worktree,
57
+ leases, knowledge, direction) renders IN FLOW at the end of the scroll
58
+ body, closed by the freshness footer (`snapshot {time} · turn {turn}` —
59
+ the served snapshot's own timestamp + turn, never "live"). The three
60
+ sections stack in the scroll body: the tasks page (iteration head +
61
+ **vertical** 5-step stepper with the FOUR-STATE `current` / `next` /
62
+ `done` / `idle` machine + the branch panel + five stacked status groups
63
+ — the merged「受阻/未知」/「Blocked / Unknown」column kept, `PLAN_CAP`
64
+ render caps + the clickable 「更多」/「收起」 expand button
65
+ (`data-kanban-more`); the projection keeps ALL plan rows — then the
66
+ project rollup), the events page (two partitions — Agent 流转事件 /
67
+ 违规记录 — as flow rows, every row an expandable native `<details>`
68
+ carrying the full catalog fields, missing fields render 「—」 never a
69
+ guessed value), and the agents page — a **vertical grouped list** (the
70
+ react-flow canvas, its SVG edge layer, card ports and pointer pan are
71
+ REMOVED): two Phase groups in constant order (Phase 1 review-edit-chain
72
+ above; Phase 2 sdd-implement → qc-tri → qa-gate below, its label
73
+ annotating the CURRENT plan — `data-agent-group-plan` + `+N more`),
74
+ `sdd-implement` split into implementor / reviewer sub-partitions, the
75
+ `general` bucket sunk into an `unknown` sub-bucket, the full 14-role
76
+ roster as full-width flow rows (idle rows dashed muted — the roster is
77
+ never hidden) with role chip / status point / `×N` count / record line,
78
+ the `N executing · M pending` summary, and the three-entry legend in
79
+ flow below the list; rows carry the projected **emphasis tier**
80
+ (`--mstar-canvas-emphasis-*` chrome alpha mix — never a whole-row
81
+ `opacity`, so the status point + running glow stay opaque), settled
82
+ entities get the standalone GREEN done frame + ✓
83
+ (`data-agent-done="true"`) ONLY when `emphasis ≠ 'off'`; the agents page
84
+ contains zero `<svg>`, zero `data-agent-port` / `data-canvas-*` anchors
85
+ and no pan transform. The **iteration info section is shared by the
86
+ tasks AND agents pages** (one `IterationInfoSection`, both render the
87
+ same `view.iteration` block). Empty branches are explicit states —
88
+ `waiting` / `loading` / `unavailable` (with its reason) / no-harness
89
+ each carry their OWN anchor and copy and render no tabs, no digest and
90
+ no meta dock; no harness renders a CENTERED inactive-state card that
91
+ activates automatically once a harness is detected (inside the same
92
+ single scroll zone). Projection is the pure `projectGraph(source)`
93
+ function (schema constants vs catalog evidence strictly separated; never
94
+ throws; explicit degraded states — muted empty states, never orange warn
95
+ boxes). Refresh follows the session snapshot, no polling — while the
96
+ main agent is ACTIVELY orchestrating, a ledger record (dispatch/settle)
97
+ invalidates the workspace's TTL-cached catalog row so the next pre-step
98
+ rebuilds and (digest text change) re-injects it, and the panel refreshes
99
+ per step (seconds, not the 60 s TTL); while the main agent IDLES the
100
+ panel keeps the LAST snapshot — no live push channel. Bundle served at
101
+ `/plugins/@mstar-harness/dsh/client.js` (closure-factory CJS with NO
102
+ graph library inlined — react-flow removed; the build asserts the bundle
103
+ contains no `xyflow`/`reactflow` markers, no `@deepseek-ai/*` value
104
+ imports, and no `import.meta` / ESM statements — the loader runs plugin
105
+ bundles as classic scripts). Full realized layout detail:
106
+ `packages/dsh/README.md` (§ Web client plugin). **Known limitations**:
107
+ the stepper's Step 1 (iteration-start) IS the current step while the
108
+ steering compass is `status: active` (Phase 1 in flight — catalog
109
+ `compassStatus` field), carrying NO PASS/FAIL badge (Phase 1 has no gate
110
+ verdict); Step 5 (merge-ready) can never be the CURRENT step — the
111
+ engine phase gate only evaluates Phase 2→3→4; the current step follows
112
+ the TTL-refreshed `compassStatus` — up to one catalog interval (60 s)
113
+ behind a mid-session `active`→`locked` flip (bounded, documented
114
+ staleness, never a wrong verdict); the agent-entity status derivation
115
+ pairs a PAIRED settle exactly by its dispatch identity (`agent`, `role`,
116
+ `planId`, `taskId`), and an unpaired dispatch stays `running` (never
117
+ guessed, never faked); with NO steering compass the current-iteration
118
+ filter infers the iteration from plan ids (8-digit date prefix) +
119
+ doneAt — deterministic, documented heuristic, only provably
120
+ cross-iteration events are dropped, no historical back-scan of resumed
121
+ long logs; the sidebar chip title is captured at open time; a docked
122
+ body renders nothing while `tab.visible === false`. Panel acceptance is
123
+ dual-track: in-loop browser harness verification against the rebuilt
124
+ bundle plus user-restart final GUI acceptance.
257
125
 
258
126
  ## Skill loading
259
127
 
@@ -270,6 +138,7 @@ or a custom profile).
270
138
  | dsh tool | Harness use |
271
139
  |----------|-------------|
272
140
  | **`subagent`** | Primary dispatch — the model-facing delegation tool the dispatch gate matches (default `toolName`; a renamed instance must be declared via Config `dispatchTools`) |
141
+ | **`workflow`** | Read-only N≥3 fan-out — one run, one conversation `workflow-run` node (§ Read-only fan-out via the `workflow` tool; scripts → `references/dsh-workflow-scripts.md`) |
273
142
  | **`mstar_iteration_gate`** | Evaluate the iteration phase gate in-app (`evaluatePhaseGate` — `mstar iteration gate` parity) |
274
143
  | **`mstar_sdd_workspace`** / **`mstar_sdd_task_brief`** | SDD workspace resolve + task brief extraction (`mstar sdd …` parity) |
275
144
  | **`mstar_*_validate`** | On-demand seam validators (design-md / audit / compound / roles) |
@@ -324,10 +193,8 @@ build (`catalogTtlMs`, default 60 s).
324
193
 
325
194
  The plugin records ACTUAL subagent dispatch and real-completion settle events —
326
195
  the evidence of what really happened, distinct from the client-side expected
327
- role flow. The workflow panel's agent-execution zone (the stage/entity
328
- projection — plan `20260810-panel-agent-flow-zone`) and the 事件记录 tab's
329
- `EventLogPage` log page (plan `20260811-panel-event-log`) are pure consumers
330
- of this evidence.
196
+ role flow. The workflow panel's 代理执行 (agents) page and the 事件记录
197
+ tab's `EventLogPage` log page are pure consumers of this evidence.
331
198
 
332
199
  - **Recording point (one core)**: `DshHostAdapter.dispatchGate` is the SINGLE
333
200
  record path behind both dispatch surfaces — the `tools/pre-execute` listener
@@ -342,59 +209,107 @@ of this evidence.
342
209
  `beforeDispatch` followed by the identical text as an in-loop subagent tool
343
210
  call) records two dispatch events — the surfaces are mutually exclusive by
344
211
  design; the double record is documented, not deduplicated.
345
- - **File / bounds**: events append to `{HARNESS_DIR}/agent-flow.jsonl` (JSON
346
- Lines, one event per line; harness dirs are gitignored by convention). The
347
- ledger assumes ONE dsh process writes each harness dir (single-writer):
348
- concurrent dsh sessions on the same repo can lose events (the append itself
349
- is near-atomic O_APPEND, but truncation is a read-modify-write) — the loss
350
- only under-reports actual flow in the panel, never a gate impact. After each
351
- append the file truncates to the most recent **500** events; truncation is
352
- size-gated (≈500 lines' typical size — small files stay append-only) and
353
- performed as an atomic temp-file rename. The catalog read returns the
354
- latest-first view with a default window of **50** and a role × outcome
355
- summary. A MISSING file reads as the empty view ("no actual dispatches yet"
356
- — recording starts at plan merge); an unreadable file is absent evidence;
357
- malformed lines are skipped, never fatal.
358
- - **Settle = real completion pairing, never faked** (plan
359
- `20260811-panel-f4-timeliness`): `tools/post-execute` IS part of the
212
+ - **File / bounds**: events append to the ACTIVE workflow dir —
213
+ `{HARNESS_DIR}/workflows/<id>/agent-flow.jsonl` (JSON Lines, one event per
214
+ line; harness dirs are gitignored by convention) — never the harness root:
215
+ with no active lifecycle the record is SKIPPED with a one-time warn. The
216
+ append and the size-gated truncating read-modify-write form ONE critical
217
+ section behind a per-workflow lockdir, so a second dsh session sharing the
218
+ active lifecycle cannot silently drop the other writer's lines (steady state
219
+ stays one writer per workflow dir); any loss only under-reports actual flow
220
+ in the panel, never a gate impact. After each append the file truncates to
221
+ the most recent **500** events; truncation is size-gated (≈500 lines'
222
+ typical size — small files stay append-only) and performed as an atomic
223
+ temp-file rename. The catalog read returns the latest-first view with a
224
+ default window of **50** and a role × outcome summary. A MISSING file reads
225
+ as the empty view ("no actual dispatches yet" — recording starts at plan
226
+ merge); an unreadable file is absent evidence; malformed lines are skipped,
227
+ never fatal.
228
+ - **Settle = real completion pairing, never faked**: `tools/post-execute`
229
+ IS part of the
360
230
  verified dsh-tools registry surface (`runPostExecute` dispatches the
361
231
  waterfall for every tool call — verified against the upstream source and
362
232
  pinned by a real-call probe). The pairing listener matches dispatch TOOLS
363
- (Config `dispatchTools`, default `['subagent']`), looks up the exec's
364
- `callId` in the apply-scoped pairing store, and branches on the verified
365
- result shapes:
366
- - `{ kind: 'background', taskId }` → store `taskId → dispatchRef`; the REAL
367
- settle arrives via `ctx.tasks.onTaskDone` (terminal mapping
368
- completed → ok / killed → denied / failed → error, `durationMs` when
369
- available), wired through `ctx.inject(['tasks'])`.
233
+ (Config `dispatchTools`, default `['subagent', 'subagent_fork']`), looks up
234
+ the exec's agent-namespaced call key in the apply-scoped pairing store, and
235
+ branches on the verified result shapes:
236
+ - `{ kind: 'background', jobId }` (the registry job id, `<kind>-N`) → store
237
+ `jobId → dispatchRef` and the bounded job id as the ref's `taskRef`; the
238
+ REAL settle arrives via `ctx.inject(['jobs'])` → `jobs.onJobDone`
239
+ (terminal mapping completed → ok / killed → denied / failed → error,
240
+ `durationMs` when available). A background value without a valid `jobId` →
241
+ nothing mappable (no settle).
370
242
  - `{ kind: 'continuable', subagentId }` → no terminal signal this round →
371
- no settle (documented limit — the child owns its turns).
243
+ no settle (documented limit — the child owns its turns); the value
244
+ authorizes the child-identity join below and nothing is copied onto a
245
+ settle.
372
246
  - any other successful value (foreground included) → settle `ok`; a failed
373
- result (`isError`) → settle `error`.
247
+ result (`isError` or an `error` payload) → settle `error`. A returned
248
+ foreground `runId` is the settle's `childId`, extracted independently of
249
+ the outcome (an error settle keeps its identity without becoming `ok`).
374
250
  Pairing is apply-scoped (in-memory `callId → dispatchRef` /
375
- `taskId → dispatchRef` maps created in the entry `apply`; an HMR restart
251
+ `jobId → dispatchRef` maps created in the entry `apply`; an HMR restart
376
252
  resets them, and completions outside the window stay unpaired). Every
377
253
  PAIRED settle carries the paired dispatch's identity (`role`/`planId`/
378
254
  `taskId` — same field names + semantics as the dispatch event; the registry
379
- background-task id is never written as `taskId`, `taskRef` is reserved for
380
- it). Unpaired payloads (non-dispatch tools, calls outside the pairing
381
- window) record NOTHING — the ledger stays dispatch-only, never a
382
- fabricated settle.
255
+ job id is never written as `taskId` — `taskId` stays the Assignment `Task N`
256
+ tag, `taskRef` is reserved for the registry id). Unpaired payloads
257
+ (non-dispatch tools, calls outside the pairing window) record NOTHING — the
258
+ ledger stays dispatch-only, never a fabricated settle.
259
+ - **Child identity (`subagent-link`, nonterminal)**: the child session id is
260
+ published upstream as a PARENT-OWNED `subagent/catalog` session event
261
+ (`{ version: 0, childId, childCreatedAt, mode, label }`; `label` = the
262
+ delegation `description`), appended by the tool body — for the continuable
263
+ path BEFORE the tool returns. The join spans a per-dispatch CALL WINDOW: the
264
+ pre-execute reserves the first raw-label slot under the live parent Session
265
+ object and captures its `seq` as the window start; a valid `background` /
266
+ `continuable` result at `tools/post-execute` makes that exact candidate
267
+ eligible (every other outcome retires it to a tombstone; a duplicate label
268
+ was already refused a candidate at reservation). Eligibility walks
269
+ `eventAt(seq)` over `[fromSeq, end)`, where `end` is the session's `seq`
270
+ CAPTURED when the candidate became eligible — recovering a catalog appended
271
+ before the tool returned — while ONE root-context `session/event` observer
272
+ feeds the same matcher for later arrivals against the session's CURRENT
273
+ `seq`: only that live observer follows the session forward, so a catalog
274
+ appended after eligibility still joins through it (the catch-up scan stays
275
+ frozen at its captured endpoint). A matched
276
+ candidate is consumed once and appends `{ v: 1, ts, kind: 'subagent-link',
277
+ agent?, childId, label, role, planId?, taskId?, taskRef? }` to the
278
+ DISPATCH's own workflow dir (`ts` = observation time), correlating the
279
+ catalog child back to the dispatch identity mstar recorded. It is an
280
+ IDENTITY record, NOT a completion: no `outcome`, no `verdict`, no `paired`
281
+ marker. A background one-shot link also carries its registry `taskRef`; a
282
+ continuable link omits it. Settle rows carry an optional `childId` — a
283
+ foreground `runId`, or for background only when the join has already
284
+ supplied one.
285
+ - **Join bounds (honest degrade)**: NO row when the label is missing/empty,
286
+ the dispatch unpaired, a background result carries no valid `jobId` (the
287
+ reserved candidate is retired — nothing mappable: no settle, no link),
288
+ the catalog version unknown, the mode not matching the result kind,
289
+ a continuable catalog naming a different child than the tool returned,
290
+ the slot map at capacity (500 labels per parent Session), or the slot
291
+ already consumed. The join is apply-scoped: no whole-history cold
292
+ scan and no `session/created` backfill — a catalog written before apply
293
+ (constructor seeds) can never label a new dispatch. Duplicate labels are
294
+ deterministic best-effort (first reservation + first matching catalog wins),
295
+ NOT proof of unique ownership. Not every provider emits a catalog — a remote
296
+ run without a `localAgent` produces none, so a dispatch may legitimately
297
+ have no link row.
383
298
  - **Catalog**: `state.agentFlow` carries the ledger view (`events` ≤ 50,
384
299
  latest-first, + `summary`); the model-facing `<mstar_engine_status>` text
385
300
  renders ONE compact `agent flow: …` line only when events > 0 (role totals
386
301
  top-5 + latest dispatch with HH:MM — the event detail lives in the
387
- structured source, never the model text). A ledger record (dispatch/settle)
388
- invalidates the affected workspace's TTL cache entry IMMEDIATELY
389
- (apply-scoped `harnessDir → cache key` reverse map + invalidation closure,
390
- plan `20260811-panel-f4-timeliness`) → the next pre-step rebuilds and (digest
391
- text change) re-injects the row — the 60 s TTL no longer bounds
392
- ledger-change latency; it still bounds non-ledger staleness.
302
+ structured source, never the model text). A ledger record
303
+ (dispatch/settle/link) invalidates the affected workspace's TTL cache entry
304
+ IMMEDIATELY (apply-scoped `harnessDir → cache key` reverse map +
305
+ invalidation closure) → the next pre-step rebuilds and (digest text change)
306
+ re-injects the row — the 60 s TTL no longer bounds ledger-change latency; it
307
+ still bounds non-ledger staleness.
393
308
  - **Maintainer view**: change the ledger shape (event schema, bounds, settle
394
309
  seam) and update the projections together — `gates/agent-flow.ts` (record /
395
- read / settle listener), `gates/catalog.ts` (agent-flow line + `source`
396
- view) and `client/panel/graph/project-graph.ts` (the ZoneView flow/agents
397
- projection) — the panel renders ONLY what the evidence shows.
310
+ read / settle / catalog-join listeners), `gates/catalog.ts` (agent-flow line
311
+ + `source` view) and `client/panel/graph/project-graph.ts` (the ZoneView
312
+ flow/agents projection) — the panel renders ONLY what the evidence shows.
398
313
 
399
314
  ## PM dispatch
400
315
 
@@ -433,15 +348,36 @@ the "queued messages" dock instead of reaching the parent (observed on dsh).
433
348
  The closing message is the guaranteed delivery channel; reserve `report` for
434
349
  MID-turn findings that change what the parent should do next.
435
350
 
351
+ ### Progress discipline — native workflow, never `/goal`
352
+
353
+ dsh progress is driven by the **native workflow**: the workflow snapshot phases
354
+ + the dispatch gates + **subagent settle notifications**. mstar **stops arming**
355
+ a goal on dsh — the surviving bridge is advisory-only (no `create` / `edit` /
356
+ `complete` / `pause` / `resume`, no goal read) — so no goal round loop drives
357
+ mstar work here. Never drive a dsh session with a `/goal` objective or a goal
358
+ round loop: `goal-round-driver` opens a round whenever the goal is active +
359
+ armed and the agent is idle, and it knows nothing about running subagents, so
360
+ an operator who arms `/goal` manually can still get rounds firing while a
361
+ dispatched child owns the critical path.
362
+
363
+ **Phase 2 continuous execution is a PM-local loop**: dispatch → **wait for the
364
+ child's settle notification** → next dispatch. When a dispatched child owns the
365
+ critical path, the correct action is to **wait** — not to open another unit of
366
+ work against the same worktree.
367
+
436
368
  ### QC default
437
369
 
438
- - **`Execution mode: sdd`**: **N=3** `subagent` dispatches — one per QC seat
439
- (`qc-specialist`, `qc-specialist-2`, `qc-specialist-3`), each body **Act as**
440
- the respective QC role + QC skill load. **MUST dispatch all three with
441
- `run_in_background: true` in one message** → the seats run CONCURRENTLY
442
- (background children; wall ≈ single seat); foreground (no
443
- `run_in_background`) runs serially (wall ≈ 3× single seat) and does NOT
444
- count as parallel tri. Cannot emit required **N** → **`Blocked`**.
370
+ - **`Execution mode: sdd`**: **N=3** seats — one per QC seat (`qc-specialist`,
371
+ `qc-specialist-2`, `qc-specialist-3`), each body **Act as** the respective QC
372
+ role + QC skill load. Read-only fan-out of N≥3 on dsh uses the native
373
+ **`workflow`** tool (the `mstar-qc-tri` script — § Read-only fan-out via the
374
+ `workflow` tool): one run, three concurrent children, one conversation
375
+ `workflow-run` node. When the tool is not mounted (the `ptc` preset hides it),
376
+ fall back to the `subagent` path below. **The `subagent` path MUST dispatch all
377
+ three with `run_in_background: true` in one message** → the seats run
378
+ CONCURRENTLY (background children; wall ≈ single seat); foreground (no
379
+ `run_in_background`) runs serially (wall ≈ 3× single seat) and does NOT count
380
+ as parallel tri. Cannot emit required **N** → **`Blocked`**.
445
381
  - **`inline`**: **N=1**.
446
382
 
447
383
  ### SDD implement (serial)
@@ -450,6 +386,63 @@ MID-turn findings that change what the parent should do next.
450
386
  task reviewer = a separate dispatch (SDD review role) — no sticky resume
451
387
  unless the host's continuable-subagent id is available and recorded.
452
388
 
389
+ ## Read-only fan-out via the `workflow` tool
390
+
391
+ dsh also exposes the upstream **`workflow`** tool
392
+ (`@deepseek-ai/dsh-tool-workflow`, mounted by the shipped agent presets; the
393
+ `ptc` preset disables it in favour of its own orchestration surface). It runs a
394
+ model-written plain-JavaScript script that fans children out inside ONE run; the
395
+ run is recorded as durable `tool-workflow/*` session events and the stock dsh UI
396
+ (`dsh-client-ui-workflow-run`) folds them into one conversation **`workflow-run`**
397
+ node the operator expands by phase and member. Use it for **read-only fan-out of
398
+ N ≥ 3 seats** — plan QC tri, large-repo audit categories, `/amazing-pr-review
399
+ deep` seats — and copy the `script` + `meta` + `args` from this skill →
400
+ `references/dsh-workflow-scripts.md`. For **1–2** delegations keep **`subagent`**
401
+ (the tool's own guidance): the two-seat default tier of `/amazing-pr-review`
402
+ shows two subagent cards and no `workflow-run` node, and that is expected.
403
+
404
+ **Read-only only.** A workflow child is a delegated child (the shipped `spawn`
405
+ provider pins `approval: never` for the whole delegation), and the run has **no
406
+ per-child pre-start veto seam** — so a script is never the channel for writable
407
+ work; writable fan-out stays on `subagent` behind the dispatch and lease gates.
408
+ Seats return findings in their result payload and must never depend on writing
409
+ files — the caller persists the seat reports.
410
+
411
+ **Every `agent()` prompt starts with the Assignment header** — `## Assignment`
412
+ plus `Execute as` / `Delegation` / `Task category` as the first lines:
413
+
414
+ ```markdown
415
+ ## Assignment
416
+
417
+ Execute as: qc-specialist
418
+ Delegation: forbidden
419
+ Task category: audit
420
+ ```
421
+
422
+ Role binding on dsh is prompt-only (there is no `agent` field), and the same
423
+ engine grammar is what the role-persona channel parses
424
+ (`packages/dsh/src/gates/role-persona.ts` reads only the header region) — so
425
+ `Execute as: qc-specialist` resolves the QC role persona for that child. Keep
426
+ body-quoted field examples out of the header region, and never pass the deferred
427
+ `agentType` option: the engine rejects it loudly.
428
+
429
+ | Operator types | N | Tool | `meta.name` | Operator sees |
430
+ |---|---|---|---|---|
431
+ | `/codebase-audit` (large repo) | ≥3 | native `workflow` | `mstar-audit-fanout` | conversation `workflow-run` node |
432
+ | `/amazing-pr-review deep` | ≥3 | native `workflow` | `mstar-pr-seats` | same |
433
+ | `/amazing-pr-review` default tier | 2 | `subagent` | — | two subagent cards, no node (expected) |
434
+ | Plan QC tri (PM already in session, no extra slash) | 3 | native `workflow` | `mstar-qc-tri` | conversation `workflow-run` node |
435
+ | Any 1–2 read-only delegation | 1–2 | `subagent` | — | expected |
436
+
437
+ `meta.name` is the gate identity — keep it kebab-case and on the recommended
438
+ list. With the default Config (`workflowNames` unset) every name is *unknown*,
439
+ which under the default `workflowGate: warn` is one `workflow.name.unknown`
440
+ **advisory that the run survives** — acceptable on a first run, not a failure. A
441
+ production overlay may set `workflowNames: ['mstar-qc-tri', 'mstar-audit-fanout',
442
+ 'mstar-pr-seats']` (and, separately, `workflowGate: hard`); both are operator
443
+ choices, never mstar defaults. The same run also reaches the panel's 事件记录 tab
444
+ through the agent-flow ledger (§ Agent-flow ledger).
445
+
453
446
  ## Commands and skills paths
454
447
 
455
448
  | Surface | Path / invocation |