@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
package/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # Blob hashes (git hash-object) of each side as of the last confirmation that
3
3
  # both languages say the same thing (dsh i18n contract: a pair is three
4
4
  # sibling files; editing either side obligates re-confirming and re-recording).
5
- README.md: 5d6f594ff5a64d84c2e5900d3118f744351cf32b
6
- README.zh.md: 054b5415b2fa53e5bfae1e78d22e3a9625886b4a
5
+ README.md: 38694c86b3d4bc15bb04dbcdb2a76756ceb2df72
6
+ README.zh.md: 74284ed74c76697032700864da84b7ec3e407be5
package/README.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  English | [中文](README.zh.md)
4
4
 
5
- ![dsh](https://img.shields.io/badge/dsh-0.1.5--rc.1-4B32C3.svg)
5
+ ![dsh](https://img.shields.io/badge/dsh-0.1.5--rc.2-4B32C3.svg)
6
6
 
7
- [Morning Star](https://github.com/btspoony/mstar-harness) as a first-class dsh (DeepSeek Harness) host — a cordis function plugin that mounts the mstar engine in-process, implements the engine `HostAdapter` (`host: 'dsh'`), guards `{HARNESS_DIR}/status.json` writes (validate + advisory; repair-escape under hard), blocks disallowed subagent dispatches when `Enforcement: hard` is on, lints `SKILL.md` writes under the mounted skill roots, mounts the mstar `skills/` mirror through the dsh skill-filesystem provider (single canonical mount), and appends a durable `mstar-engine-status` catalog row to every composed agent step. Boot with a dsh Loader app; everything acts through the seam's refusal/advisory channels, never by patching the tools.
7
+ [Morning Star](https://github.com/btspoony/mstar-harness) as a first-class dsh (DeepSeek Harness) host — a cordis function plugin that mounts the mstar engine in-process, implements the engine `HostAdapter` (`host: 'dsh'`), guards `{HARNESS_DIR}/status.json` writes (validate + advisory; repair-escape under hard), blocks disallowed subagent dispatches when `Enforcement: hard` is on, lints `SKILL.md` writes under the mounted skill roots, mounts the mstar `skills/` mirror through the dsh skill-filesystem provider (single canonical mount), and appends a durable `mstar-engine` catalog row (persisted-log readers also accept the legacy `mstar-engine-status` identity) to every composed agent step. Boot with a dsh Loader app; everything acts through the seam's refusal/advisory channels, never by patching the tools.
8
8
 
9
9
  ## Usage
10
10
 
@@ -14,7 +14,7 @@ How a dsh app consumes the plugin — install paths, configuration, what mounts
14
14
 
15
15
  The package ships as a workspace package (`workspaces: ["packages/*"]`) with the engine bundled into `dist/` at build time (`bun run build`; dist is gitignored). The install path is the **profile bundle**, added to the shipped `web` profile (`dsh --profile web` — the ready-made web app profile, `dsh web`), through the `dsh.bundle.patch` manifest — a patch layer mounted over the dsh-base defaults:
16
16
 
17
- **One-command CLI entry (recommended)** — `npx @mstar-harness/cli init --target dsh` installs the full capability in one go: it runs the two `dsh plugin --profile web add` installs below in order (the mstar bundle first, then `dsh-llm-fallbacks`), and `npx @mstar-harness/cli doctor --target dsh` reports each plugin row as `uninstalled` / `disabled` / `mounted`. It is the same two-command install, orchestrated; `--no-fallbacks` skips the second row (and with it the seeded roles — see What you get below).
17
+ **One-command CLI entry (recommended)** — `npx @mstar-harness/cli init --target dsh` installs the full capability in one go: it runs the two `dsh plugin --profile web add` installs below in order (the mstar bundle first, then `dsh-llm-fallbacks`), and `npx @mstar-harness/cli doctor --target dsh` reports each plugin row as `uninstalled` / `disabled` / `mounted` / `drifted` (`drifted` = the fallbacks row is installed at a version other than the pinned one; `init --target dsh` re-adds the pin, `doctor` exits non-zero until then). It is the same two-command install, orchestrated; `--no-fallbacks` skips the second row (and with it the seeded roles — see What you get below).
18
18
 
19
19
  **(a) Registry install (published form)** — the npm package carries the built `dist/` (no build step on install):
20
20
 
@@ -72,9 +72,11 @@ The shipped headless template auto-initializes on first use (`@deepseek-ai/dsh-b
72
72
  | `rolePersonas` | `Record<string, string>` | unset (bundled mirror default) | mstar role id (`Execute as`) → persona text; the native subagent persona channel's **override** source — a role-matched start (one-shot `start` or the opt-in continuable `startContinuable`) merges the persona into the native request `persona` slot (the child embodies the role persona INSTEAD OF the deployment persona; persisted + reapplied on resume). Merge order: the request's own `persona` wins AS-IS (a caller-set persona is never overridden — no role merge); otherwise a non-empty entry beats the bundled `harness-agents/` mirror default, an **empty-string** entry is treated as unset and falls through to the mirror default, and an absent entry uses the mirror default (see LLM fallbacks integration). |
73
73
  | `skillRoots` | `string[]` | unset (no custom-root registration) | Additional skill roots registered with the dsh skill-filesystem provider (`customSkillDirs` semantics — scanned before user roots). Dev-time: the mirror `<repo-root>/skills` absolute path. |
74
74
  | `bundledSkillDir` | `string` | packaged `harness-skills/` mirror (package-relative) | Bundled skill root registered with the dsh skill-filesystem provider (`bundledSkillDir` semantics — scanned last, trusted). Defaults to the package's OWN `harness-skills/` mirror (synced by `bundle-assets`; gitignored) — package-relative, NOT cwd-anchored. An explicit value wins. |
75
- | `catalogTtlMs` | `number` | `60000` | Pre-step catalog cache refresh interval (ms): how often the per-workspace unified `mstar-engine-status` catalog row (watermark + iteration gate + workspace-state digest) re-reads `status.json` / the compass / the knowledge index. The hot path is a timestamp compare + cache hit between refreshes; a mid-session plan/compass/residual change lands within one interval. |
75
+ | `catalogTtlMs` | `number` | `60000` | Pre-step catalog cache refresh interval (ms): how often the per-workspace unified `mstar-engine` catalog row (watermark + iteration gate + workspace-state digest) re-reads `status.json` / the compass / the knowledge index. The hot path is a timestamp compare + cache hit between refreshes; a mid-session plan/compass/residual change lands within one interval. |
76
76
  | `workflowGate` | `'off' \| 'warn' \| 'ask' \| 'hard'` | `'warn'` | Workflow/ralph gate mode (see Gates → Workflow / ralph gate). `off` = pass-through with no verdict row; `warn` = advisory-only; `ask` = first-seen names route through the approval waterfall (P-c); `hard` = policy violations veto before any child starts. Default `warn` changes NO hard behavior — the gate is advisory-only unless the deployment opts into `ask`/`hard`. |
77
- | `workflowNames` | `string[]` | unset | Workflow name allowlist (P-a): `meta.name` values treated as KNOWN by the gate. Empty or absent ⇒ **every** name is unknown (documented — the gate is NOT "allow all" by omission). Ralph calls carry no `meta.name` — P-a never applies to them. |
77
+ | `workflowNames` | `string[]` | unset | Workflow name allowlist (P-a): `meta.name` values treated as KNOWN by the gate. Empty or absent ⇒ **every** name is unknown (documented — the gate is NOT "allow all" by omission). Ralph calls carry no `meta.name` — P-a never applies to them. Recommended for the mstar read-only fan-out path: `['mstar-qc-tri', 'mstar-audit-fanout', 'mstar-pr-seats']`. |
78
+
79
+ **Recommended operator overlay.** The three recommended `workflowNames` above cover the mstar read-only fan-out path (plan QC tri, large-repo `/codebase-audit`, `/amazing-pr-review deep`); listing them is an operator overlay in the profile layer, never an mstar default. With the shipped empty list every name is *unknown*, which under the default `workflowGate: 'warn'` is one `workflow.name.unknown` advisory the run survives. A production deployment that also wants unknown names vetoed sets `workflowGate: 'hard'`; the shipped default stays `'warn'`.
78
80
 
79
81
  `bundledSkillDir` defaults to the package's OWN `harness-skills/` mirror (see Skills mount) — an explicit Config value still wins. A relative override remains **cwd-anchored** (skill-filesystem `join()` semantics against the dsh process cwd), so deployments overriding the default should pass an **absolute path in the profile layer** (see `bundle/README.md`).
80
82
 
@@ -99,7 +101,7 @@ The registry rows mount before the plugin so `ctx.skills` / `ctx.tools` / `ctx.c
99
101
  - **Seam lints** — `DESIGN.md` / audit-plan / knowledge-doc / roles-dir writes under the harness get their artifact-specific engine lints.
100
102
  - **Model-facing tools** — `mstar_sdd_workspace`, `mstar_sdd_task_brief`, `mstar_iteration_gate`, `mstar_design_md_validate`, `mstar_audit_validate`, `mstar_compound_validate`, `mstar_roles_validate` register on `ctx.tools`. The `mstar_iteration_gate` mirror takes the v3 input `snapshot_path` (`{HARNESS_DIR}/workflows/<id>/snapshot.json` — mirror of `mstar iteration gate --workflow <id>`; the old `status_path` root input is gone with the v1 read path).
101
103
  - **Bundled commands** — `ctx.commands` registrations for `/iteration-start`, `/iteration-drive`, `/iteration-loop`, `/codebase-audit` (from the packaged `harness-commands/` mirror; each declares a frontmatter `input` hint so the web client claims `/name ` and waits for the user's follow-up args instead of executing immediately; handlers steer the command body + user args into the receiving agent).
102
- - **Pre-step catalog row** — every composed agent step appends ONE unified `mstar-engine-status` catalog message: the watermark (unified mstar version, harness dir, enforcement), the iteration phase-gate section (when a steering compass resolves) and the workspace-state digest (plan registry, open residuals, branch/policy anchors, active leases, knowledge digest, compass direction — when the workspace has a `status.json`). The row is digest-gated (injected once per turn, re-injected only when it changed) and shares one TTL-cached per-workspace build (`catalogTtlMs`, default 60 s).
104
+ - **Pre-step catalog row** — every composed agent step appends ONE unified `mstar-engine` catalog message: the watermark (unified mstar version, harness dir, enforcement), the iteration phase-gate section (when a steering compass resolves) and the workspace-state digest (plan registry, open residuals, branch/policy anchors, active leases, knowledge digest, compass direction — when the workspace has a `status.json`). The row is digest-gated (injected once per turn, re-injected only when it changed) and shares one TTL-cached per-workspace build (`catalogTtlMs`, default 60 s).
103
105
 
104
106
  ### Enforcement semantics
105
107
 
@@ -173,7 +175,7 @@ Persona delivery rides dsh's NATIVE `SubagentStartRequest.persona` slot (`@deeps
173
175
 
174
176
  ### Role seeds + adoption advisory
175
177
 
176
- When the optional `dsh-llm-fallbacks` capability is **mounted** (the second install command — see Install paths), the mstar plugin **zero-config declares the 13 `mode: subagent` mstar role seeds** into the fallbacks seed registry: persona = the `harness-agents/` mirror `description` (verbatim) + one mandatory-load guide line (`Load mstar-roles (references/<role-id>.md) first — identity comes before skills; load topic skills only when the Assignment activates them via its Skill presets field.`); a persona carrying the `{{...}}` interpolation hazard is skipped + warned, never declared. The declaration **merge-preserves the currently-seeded non-mstar ids** from the readback — e.g. the 7 omp-style preset roles the upstream package self-declares at its own apply: upstream `declare` REPLACES the whole registry, so without preservation a mstar-only batch would strip preset ids of their seeded annotations (rows remain, unseeded). The declaration re-fires idempotently on every fallbacks (re-)apply (HMR/fiber swap) — never from a one-shot latch — so either boot order (presets first or mstar first) converges to the same 20-id fully-seeded registry. Boot-time convergence is a bounded retry: the provider's seed write channel binds one macrotask after its apply, so a first-attempt reject with `seeds: settings service is unavailable` inside that apply window is retried (3 attempts across the provider's apply window) and a transient reject converges on its own; only if every attempt ultimately fails does the declaration log exactly one terminal error, while the advisory's decision-point re-declare stays available as the retry path. **No manual `roles.list` edit is required.**
178
+ When the optional `dsh-llm-fallbacks` capability is **mounted** (the second install command — see Install paths), the mstar plugin **zero-config declares the 13 `mode: subagent` mstar role seeds** into the fallbacks seed registry: persona = the `harness-agents/` mirror `description` (verbatim) + one mandatory-load guide line (`Load mstar-roles (references/<role-id>.md) first — identity comes before skills; load topic skills only when the Assignment activates them via its Skill presets field.`); a persona carrying the `{{...}}` interpolation hazard is skipped + warned, never declared. The declaration **merge-preserves the currently-seeded non-mstar ids** from the readback — e.g. the 5 upstream preset roles the upstream package self-declares at its own apply: upstream `declare` REPLACES the whole registry, so without preservation a mstar-only batch would strip preset ids of their seeded annotations (rows remain, unseeded). The declaration re-fires idempotently on every fallbacks (re-)apply (HMR/fiber swap) — never from a one-shot latch — so either boot order (presets first or mstar first) converges to the same 18-id fully-seeded registry. Boot-time convergence is a bounded retry: the provider's seed write channel binds one macrotask after its apply, so a first-attempt reject with `seeds: settings service is unavailable` inside that apply window is retried (3 attempts across the provider's apply window) and a transient reject converges on its own; only if every attempt ultimately fails does the declaration log exactly one terminal error, while the advisory's decision-point re-declare stays available as the retry path. **No manual `roles.list` edit is required.**
177
179
 
178
180
  A warn-only advisory pass (logger `mstar/fallbacks-advisory`) runs **once per apply** — attempted at apply and, when the fallbacks row mounts after `dsh` (the loader mounts entries concurrently), once at the first `subagent/start` decision point. With the service present, the pass FIRST awaits the idempotent re-declare (closing the boot race) then reads the EFFECTIVE state (`getEffectiveRoles`) and reports, bounded to **at most one warn per category**:
179
181
 
@@ -241,13 +243,21 @@ Every engine module attaches to a dsh surface — delivered except the lint modu
241
243
 
242
244
  ## Engine-status catalog
243
245
 
244
- An advisory `agent/pre-step` waterfall listener appends ONE **`mstar-engine-status`** catalog MessageSource to every composed step — the first-party `plugin` arm, exactly `{ kind: 'plugin', plugin: 'mstar-engine-status', form: 'catalog' }` and **no other member** (mirroring the dsh tool-skill precedent): the model-visible `<mstar_engine_status>` block renders the watermark fields — **mstar version** (plugin own manifest; the single-version invariant pins the bundled engine to the same version), **harness dir** (resolved `{HARNESS_DIR}`, `none` when absent), and **enforcement** (compass mode, `soft` / `hard (compass)`) — plus the **iteration phase-gate section** (when a steering compass + `status.json` resolve: iteration id, transition, all-plans-done, gate verdict + violation codes — the `mstar iteration gate` tool result shape) and the **workspace-state digest section** (when the workspace has a `status.json`): **plans** (`id(status)` registry), **residuals** (open counts by severity), **branch** (base → target, spec integration), **policy** (push policy, worktree mode, control root), **leases** (active plan execution leases: holder + worktree), **knowledge** (knowledge-index doc count + categories) and **direction** (the steering compass's problem-statement one-liner). The listener calls `next()` first and builds on the delegated decision — it never vetoes a step and never replaces the composed messages. Model-visible ⟺ logged: the persisted source carries no facts — the row is the durable **anchor** and the exact emitted payload is snapshotted per session at `{HARNESS_DIR}/snapshots/engine-status.json` in this same digest-gated emission, so the session log plus that store reconstruct the row without re-parsing the block (dsh packages/AGENTS.md). No released session-format edge admits extra members on a message source, so nothing but the `plugin` arm is ever written there. The workflow panel reads that snapshot on demand over the host's shared `/api` typert gateway: the **host gateway owns the route** (a plugin cannot register one; a plain `connection.rpc.handle` channel mounts through `webServer` on the calling fiber and would pend this row's boot — the mechanism this package deliberately does not use) and this package contributes the `mstar/engineStatus` endpoint descriptor through an optional `ctx.typert.register(...)` child. The browser half calls `connection.rpc.call('/api', 'mstar/engineStatus', { args: { sessionId, cwd } })` and receives that session's stored snapshot, or an explicit `unavailable` reason (never another session's data, never silently-empty fields). **Trust boundary, stated plainly:** the endpoint authenticates nothing of its own — the transport fence (host/origin + browser auth) establishes only that the caller is a page this host served, so *possession of a `(sessionId, cwd)` pair plus reachability of the local `/api` gateway is the capability*. An answer is scoped to the REQUEST: the lookup is keyed by the asserted id, the stored record's `cwd` must equal both the asserted and the server-resolved session `cwd`, and the client re-checks the echoed id and workspace before rendering — so one session's request can never be served another session's row. The payload is a workspace digest (version, harness dir, enforcement, plan/residual/lease counters) that the panel's own session transcript already exposes; it is not treated as a secret, and the distinct `unavailable` reasons make the endpoint a weak session-existence oracle for a caller who already holds the id. Fiber disposal removes the listener (HMR-safe).
246
+ An advisory `agent/pre-step` waterfall listener appends ONE **`mstar-engine`** catalog MessageSource to every composed step — the first-party `plugin` arm, exactly `{ kind: 'plugin', plugin: 'mstar-engine', form: 'catalog' }` and **no other member** (mirroring the dsh tool-skill precedent): the model-visible `<mstar_engine_status>` block renders the watermark fields — **mstar version** (plugin own manifest; the single-version invariant pins the bundled engine to the same version), **harness dir** (resolved `{HARNESS_DIR}`, `none` when absent), and **enforcement** (compass mode, `soft` / `hard (compass)`) — plus the **iteration phase-gate section** (when a steering compass + `status.json` resolve: iteration id, transition, all-plans-done, gate verdict + violation codes — the `mstar iteration gate` tool result shape) and the **workspace-state digest section** (when the workspace has a `status.json`): **plans** (`id(status)` registry), **residuals** (open counts by severity), **branch** (base → target, spec integration), **policy** (push policy, worktree mode, control root), **leases** (active plan execution leases: holder + worktree), **knowledge** (knowledge-index doc count + categories) and **direction** (the steering compass's problem-statement one-liner). The listener calls `next()` first and builds on the delegated decision — it never vetoes a step and never replaces the composed messages. Model-visible ⟺ logged: the persisted source carries no facts — the row is the durable **anchor** and the exact emitted payload is snapshotted per session at `{HARNESS_DIR}/snapshots/engine-status.json` in this same digest-gated emission, so the session log plus that store reconstruct the row without re-parsing the block (dsh packages/AGENTS.md). No released session-format edge admits extra members on a message source, so nothing but the `plugin` arm is ever written there. **Anchor identity (renamed, with read compat)**: the emitted `plugin` value is `mstar-engine` (was `mstar-engine-status`); rows emitted by shipped builds before the rename persist the legacy identity in already-written session logs, and the panel's anchor reader treats BOTH `mstar-engine` and `mstar-engine-status` as this plugin's first-party arm — any other `plugin` value is not an anchor and the session degrades to the explicit `waiting` state (never guessed data). The workflow panel reads that snapshot on demand over the host's shared `/api` typert gateway: the **host gateway owns the route** (a plugin cannot register one; a plain `connection.rpc.handle` channel mounts through `webServer` on the calling fiber and would pend this row's boot — the mechanism this package deliberately does not use) and this package contributes the `mstar/engineStatus` endpoint descriptor through an optional `ctx.typert.register(...)` child. The browser half calls `connection.rpc.call('/api', 'mstar/engineStatus', { args: { sessionId, cwd } })` and receives that session's stored snapshot, or an explicit `unavailable` reason (never another session's data, never silently-empty fields). **Trust boundary, stated plainly:** the endpoint authenticates nothing of its own — the transport fence (host/origin + browser auth) establishes only that the caller is a page this host served, so *possession of a `(sessionId, cwd)` pair plus reachability of the local `/api` gateway is the capability*. An answer is scoped to the REQUEST: the lookup is keyed by the asserted id, the stored record's `cwd` must equal both the asserted and the server-resolved session `cwd`, and the client re-checks the echoed id and workspace before rendering — so one session's request can never be served another session's row. The payload is a workspace digest (version, harness dir, enforcement, plan/residual/lease counters) that the panel's own session transcript already exposes; it is not treated as a secret, and the distinct `unavailable` reasons make the endpoint a weak session-existence oracle for a caller who already holds the id. Fiber disposal removes the listener (HMR-safe).
245
247
 
246
248
  The row is **digest-gated**: per agent+workspace it is injected once per turn and re-injected only when its rendered text changed — a 20-step turn shows the catalog once, not 20 times. The source shares ONE per-workspace cache entry, built at boot for an explicit `harnessDir` (else on the workspace's first pre-step) and TTL-refreshed (`catalogTtlMs`, default 60 s) — the hot path is a timestamp compare + Map lookup between refreshes, and a mid-session plan/compass/residual change lands within one interval.
247
249
 
248
- ## Agent-flow ledger (workflow rows)
250
+ ## Agent-flow ledger
251
+
252
+ The agent-flow ledger lives in the ACTIVE workflow dir — `{HARNESS_DIR}/workflows/<id>/agent-flow.jsonl` (JSON Lines, bounded to the most recent 500 events) — the same JSONL the catalog's `state.agentFlow` evidence reads. With no active lifecycle the record is SKIPPED with a one-time warn — never the harness root, never a terminal snapshot dir. It records the ACTUAL subagent dispatch and settle evidence (advisory and try/catch-contained — a failing ledger write never blocks a dispatch or a settle):
253
+
254
+ - **Dispatch** — one row per Assignment-shaped dispatch, recording the dispatch identity: `role` (normalized at the write boundary — trimmed, with ONE leading `@` stripped, so `@explore` and `explore` are one actor, not two), `planId`, the Assignment `Task N` tag as `taskId`, `taskCategory`, and the verdict.
255
+ - **Settle** — recorded ONLY for a REAL paired completion. A foreground call settles in the `tools/post-execute` listener; a `{ kind: 'background', jobId }` result is paired on the registry **`jobId`** (a jobs-registry key, never a child session id) and settles when `ctx.inject(['jobs'])` → `jobs.onJobDone` reports the terminal snapshot (`completed → ok` / `killed → denied` / `failed → error`, `durationMs` when available). Every paired settle carries the dispatch identity (the `Task N` tag stays `taskId`; `taskRef` is reserved for the registry job id and rides on a background settle only) plus an OPTIONAL `childId` — a foreground `runId`, or a background child id the catalog join already supplied. A missing or oversized optional id omits the FIELD only: the real completion still records, and an id is never truncated or re-keyed. Unpaired payloads record NOTHING — never a fabricated settle.
256
+ - **`subagent-link` (nonterminal)** — the child session id a dispatch actually started, published upstream as a PARENT-OWNED `subagent/catalog` session event (`label` = the delegation `description`; the continuable path appends it BEFORE the tool returns). A per-dispatch CALL WINDOW join — slot reservation at pre-execute, eligibility on a valid `background` / `continuable` result, an `eventAt(seq)` catch-up scan plus ONE live root-context `session/event` observer — correlates that catalog child back to the dispatch identity (`role` / `planId` / `taskId`, plus `taskRef` for a background one-shot) in the DISPATCH's own workflow dir. It is an IDENTITY record, **not** a completion: no `outcome`, no `verdict`, no `paired` marker. The join is apply-scoped — no whole-history cold scan and no `session/created` backfill, so a catalog written before apply can never label a newer dispatch — and a dispatch whose provider emits no catalog simply has no link row.
257
+
258
+ ### Workflow rows
249
259
 
250
- The agent-flow ledger — `{HARNESS_DIR}/agent-flow.jsonl`, the same JSONL the catalog's `state.agentFlow` evidence reads — also records **workflow / ralph fan-out runs**: a session-event consumer (logger `mstar/workflow-ledger`, registered at apply) maps the FOUR durable `tool-workflow/*` session events into three new ledger kinds. Source of record is the **durable session events** appended into the CALLING PARENT session's log (top-level runs only — nested transport calls record nothing upstream), **not** the in-memory `workflow/*` emits (roadmap §10.4 N4): the session log is the replayable truth, so the consumer covers it with a **cold scan at apply** (constructor-seeded events never hit the firehose — `firstLiveSeq`) plus a live **`session/event` firehose** listener, deduped by ONE **durable per-session watermark** — the session-log `seq` position — persisted to `{HARNESS_DIR}/workflow-ledger-cursors.json` (a small bounded sidecar next to the ledger, written atomically temp-file + rename).
260
+ The same ledger also records **workflow / ralph fan-out runs**: a session-event consumer (logger `mstar/workflow-ledger`, registered at apply) maps the FOUR durable `tool-workflow/*` session events into three new ledger kinds. Source of record is the **durable session events** appended into the CALLING PARENT session's log (top-level runs only — nested transport calls record nothing upstream), **not** the in-memory `workflow/*` emits (roadmap §10.4 N4): the session log is the replayable truth, so the consumer covers it with a **cold scan at apply** (constructor-seeded events never hit the firehose — `firstLiveSeq`) plus a live **`session/event` firehose** listener, deduped by ONE **durable per-session watermark** — the session-log `seq` position — persisted to `{HARNESS_DIR}/workflows/<id>/workflow-ledger-cursors.json` (a small bounded sidecar next to the ledger, written atomically temp-file + rename).
251
261
 
252
262
  | `tool-workflow/*` event | Ledger row | Fields |
253
263
  | --- | --- | --- |
@@ -259,7 +269,7 @@ The agent-flow ledger — `{HARNESS_DIR}/agent-flow.jsonl`, the same JSONL the c
259
269
 
260
270
  A FOURTH kind, **`workflow-verdict`**, is written by the workflow/ralph GATE (not this consumer) — one row per gated call (`tool`, `workflow`/`objective`, `mode`, verdict `ok`/`advisory`/`denied`/`ask`, violation `code`) — see Gates → Workflow / ralph gate. Display identity fields (`workflow` / `objective`) carry the same 1024-char cap; the verdict's violation code is never guessed (P-a `workflow.name.unknown` vs P-b `workflow.lease.uncovered`).
261
271
 
262
- **Dedupe + replay scope.** The durable watermark is the dedupe mechanism: **one row per `(runId, kind, seq)`** across cold+live overlap AND across plugin **re-applies / restarts** — a re-registration consults the persisted watermark instead of starting with empty cursors. A session **created after apply** with a constructor-seeded log (resumed / forked conversation — its seeds never publish on the firehose) is cold-scanned **once** on the upstream `session/created` event, and the watermark keeps that backfill idempotent too. The watermark sidecar is bounded (per-harness session cap, eviction preferring sessions no longer live) and fully contained: an unreadable/unwritable watermark degrades to in-memory-only with one warn — a restart then re-records (honest under-dedupe, never data loss, never gating).
272
+ **Dedupe + replay scope.** The durable watermark is the dedupe mechanism: **one row per `(runId, kind, seq)`** across cold+live overlap AND across plugin **re-applies / restarts** — a re-registration consults the persisted watermark instead of starting with empty cursors. A session **created after apply** with a constructor-seeded log (resumed / forked conversation — its seeds never publish on the firehose) is cold-scanned **once** on the upstream `session/created` event, and the watermark keeps that backfill idempotent too. A **forked** conversation's scan starts at its `inheritedEventCount`: the inherited prefix is the parent session's history, so the child records only its **own** events — never a second copy of the parent's rows attributed to the child. The watermark sidecar is bounded (per-harness session cap, eviction preferring sessions no longer live) and fully contained: an unreadable/unwritable watermark degrades to in-memory-only with one warn — a restart then re-records (honest under-dedupe, never data loss, never gating).
263
273
 
264
274
  **childId linkage + member counts.** The `workflow-agent` row preserves the published member's `childId` (the child session id); the run's display `name` lives on the `workflow-run` row only, and the panel resolves it for agent/end rows via the window lookup (same `runId` — a member row itself carries no name). The panel attaches the member COUNT to the `workflow-run` row (the window's `workflow-agent` rows for that `runId`; window-bound — members truncated out of the ≤50-event window are honestly absent, never a 0 guess).
265
275
 
@@ -282,185 +292,120 @@ layer or install step** (spec §6.1). The web app serves the bundle at
282
292
  `/plugins/@mstar-harness/dsh/client.js` and loads it through the
283
293
  closure-factory loader handoff (`window.__ModuleLoader__.load({ id, factory })`).
284
294
 
285
- The client entry registers a **`conversation.view`** view-ring tab
286
- (`id: 'mstar-workflow'`, `order: 20` — the trajectory precedent shape), labeled
287
- **"MStar Workflow"** (en) / **"MStar 工作流"** (zh) through the `mstar-panel`
288
- locale namespace. The panel is the **MStar Workflow layout**: a fixed 300px
289
- right sidebar — plans (≤5, time-desc, `+N more`), open residual findings (≤10,
290
- severity chips, overflow hint), policy (**enforcement first**, then push /
291
- worktree / control worktree), leases, knowledge, direction — over a bottom
292
- **fixed meta dock** (version + harness dir; small muted, does not scroll with
293
- the sidebar digest; the former header row was removed), an **HTML/CSS zone
294
- dashboard** as the main body, and a freshness footer (`last-updated
295
- HH:MM:SS` + the catalog-re-emission refresh note). The branches block moved
296
- out of the sidebar to the iteration zone (plan `20260810-panel-canvas-zones`).
297
- Below 860px the sidebar stacks under the main area.
298
-
299
- The canvas is a pure render of the latest `mstar-engine-status` catalog row
300
- (from the `useSession` snapshot — refresh follows the snapshot, no polling):
301
- the page fills the Tab (no page-level scrolling — the zone container is the
302
- only scroll body) and the **zone dashboard** (replacing the react-flow cyclic
303
- graph, plan `20260810-panel-canvas-zones`) lays out three zones — the
304
- **iteration zone** (Step 1–5 as 5 equal full-width unit blocks with pure-number
305
- badges + an `N/5` summary — no 步骤/Step wording, plan `20260811-panel-f2-quickfix`;
306
- active-highlight / inactive dimmed states; the steps carry a FOUR-STATE machine —
307
- `current` / `next` / `done` / `idle` (plan `20260812-panel-f5-iteration-zone-fix`
308
- Task 1): every step BEFORE the current one projects `done`「已完成」(completed —
309
- a finished Step 1 must not read as idle while Step 2 is current), `next` is the
310
- single forward target, `idle` is schema-only — and the branch panel: iteration
311
- base / target / spec integration, rendered only while active; the expanded
312
- head is a LEFT-RIGHT SPLIT — branches (small left half, WIDTH-CAPPED —
313
- `flex: 0 1 260px` + `max-width: 280px`, never stretches with the container; the
314
- <860px column stack resets to content height) + steps (large right half,
315
- `flex: 1 1 0` absorbing the remaining width) via `data-iteration-head-split`,
316
- stacking on narrow widths, and NO
317
- branch panel when there is no active iteration; the current step follows the
318
- steering compass: `compassStatus: 'active'` (Phase 1 in flight) → Step 1
319
- (iteration-start) is CURRENT with verdict `unknown` — no PASS/FAIL badge —
320
- plan `20260811-panel-f4-iteration-zone`); the **iteration info section is
321
- SHARED by the tasks AND agents tabs** (plan `20260812-panel-f5-design-system`
322
- Task 8, user round-4 decision #4 — one `IterationInfoSection` component,
323
- both tabs render the same `view.iteration` block: summary + steps +
324
- branches); the **tasks zone** (5-column
325
- kanban: Todo / InProgress / InReview / Done / `blocked-unknown` — the
326
- Blocked state and the former unknown catch-all fold into ONE merged column
327
- titled「受阻/未知」/「Blocked / Unknown」, plan `20260813-panel-quick-fixes`
328
- Task 1 — with count badges; every column caps its rendered rows at
329
- `PLAN_CAP` and shows a clickable 「更多」/「收起」 expand button
330
- (`data-kanban-more` anchor) unfolding the full column — the projection
331
- keeps ALL plan rows, the cap is a render concern never a discard) and the **agent-execution zone** (the four EXPECTED_ROLE_FLOW stage/phase
332
- columns — review-edit-chain → sdd-implement → qc-tri → qa-gate (the
333
- terminal stage; the former `sdd-task-review` stage is removed, its SDD L2
334
- reviewer is now the pipeline role `code-reviewer`, v2.1.1) — a strict
335
- FOUR-column layout with NO standalone unknown column (plan
336
- `20260812-panel-f5-design-system` Task 5, user 2026-08-12 round-2 decision —
337
- the former rightmost unknown column of plan `20260812-panel-f5-agent-layout`
338
- is superseded): the `general` bucket sinks into an **unknown sub-partition
339
- at the bottom of the `qa-gate` column** (a `data-sub-bucket="unknown"`
340
- caption row 「unknown / 未匹配角色」 after the last qa-gate card, then the
341
- general cards; the standalone on-demand column was already removed in the
342
- agent-layout plan); `explore` is removed — no card, no column. The `sdd-implement`
343
- column splits into **sub-buckets** by the projected `entity.bucket` (never
344
- a render guess): the **implementor** partition above — flow roles in the
345
- stage's original order (fullstack-dev / fullstack-dev-2 / frontend-dev),
346
- then the on-demand roles (ops-engineer / prompt-engineer, carrying the
347
- **on-demand badge** — no standalone on-demand column) — and the
348
- **sdd-reviewer** partition below (code-reviewer, the SDD L2 task reviewer),
349
- with implementor / sdd-reviewer caption labels; `zone: 'on-demand'` entities
350
- live in the implementor partition, `zone: 'general'` entities render in the
351
- qa-gate column's bottom unknown sub-partition. The agent canvas is laid out
352
- in **TWO side-by-side Phase groups** (plan `20260812-panel-f5-design-system`
353
- Task 8, user 2026-08-12 round-4 decision; side-by-side layout per plan
354
- `20260813-panel-agent-canvas-legend-layout` Task 2): the **Phase 1 group on
355
- the LEFT** (review-edit-chain — the sequential Review & Edit chain:
356
- product-manager → architect → writing-specialist) and the **Phase 2 group on
357
- the RIGHT** (sdd-implement → qc-tri → qa-gate — the iterative plan loop),
358
- top-aligned (all group label rows share the same `y = PAD_Y`), each with its
359
- group label row; the **Phase-2 label annotates the CURRENT PLAN** — the
360
- first InProgress `state.plans[]` row (`data-canvas-group-plan`, projected
361
- `activePlanId`; `+N more` when several plans run in parallel, muted
362
- 「无进行中 plan」 when none). The subagent **entity cards** aggregate **by role** from actual
363
- dispatch evidence — the same role across sessions folds into one card ×N,
364
- and every off-roster dispatch (the former `generalPurpose` SDD reviewer,
365
- `scout`, anonymous `role === ''`) folds into the single `general` bucket
366
- entity (the card is role-titled — the role id; the agent session id / task
367
- tag ride the record line, never the title) — role chip / status point / ×N
368
- count; running entities carry the
369
- business glow-pulse highlight (on the ROUNDED `.card-body` — the card is a
370
- single rounded element, no square outline overlay, plan
371
- `20260812-panel-f5-design-system` Task 5), un-evidenced stages render the dashed
372
- "待执行" pending placeholder with their expected role chips, un-evidenced
373
- KNOWN_AGENTS members render dashed idle cards (the full 14-role roster is
374
- never hidden), and the header
375
- shows the `N executing · M pending` summary; cards carry the projected
376
- **emphasis tier** (plan `20260812-panel-f5-design-system` Task 4, design
377
- doc §3): `emphasis: 'current' | 'next' | 'off' | null` — the iteration's
378
- current-phase roles render at **100%** chrome intensity, later-phase
295
+ The client entry registers the panel as a **right-Sidebar page tab type**
296
+ through the public two-stage seat contract: `ctx.sidebarRightTabs.register(definition)`
297
+ with `id: '@mstar-harness/dsh'`, `kind: 'mstar-workflow'` (a page type — no
298
+ `patterns`/`canOpen`/`priority`, opened by kind), a locale-following `title()`
299
+ thunk, and exactly one **guide-page capsule** (`order: 20`, description + the
300
+ MStar glyph icon) — picking the capsule opens the panel in that pane's slot
301
+ (replacing the guide tab, expanding the column), and a second pick focuses the
302
+ existing tab (the host's page dedup rule) — no auto-open, no duplicate surface.
303
+ The old **`conversation.view`** view-ring tab is **removed** (a migration, not
304
+ a second surface): the sidebar tab replaces the conversation-area tab. The
305
+ body and its chip title register as keyed seats under the same id
306
+ (`sidebar.right.pane.tab` + `sidebar.right.pane.tab.title`; the chip is the
307
+ glyph + a title captured at open time — it does not follow a mid-session
308
+ locale switch). The body renders **nothing** while `tab.visible === false`
309
+ (collapsed column or a sibling pane tab active — no projection, no DOM), and
310
+ the selected section lives in the entry's slot store keyed by the tab record,
311
+ so it survives the docked body's unmount. Labels come from the `mstar-panel`
312
+ locale namespace: **"Morning Star Workflow"** (en) / **"启明星工作流"** (zh).
313
+
314
+ The panel is a pure render of the latest catalog snapshot (from the host's
315
+ `/api/mstar/engineStatus` endpoint — refresh follows the fetch, no polling):
316
+ the **narrow-column layout** is a single flex column bound to the sidebar
317
+ pane's definite height with exactly three zones — the **section nav**
318
+ (任务迭代 / 代理执行 / 事件记录, flex: none), the panel-owned **single scroll
319
+ body** (`[data-mstar-scroll]` — the ONLY `overflow-y` element in the panel; no
320
+ element scrolls horizontally; `data-mstar-graph` rides it), and the pinned
321
+ **meta dock** (version + harness dir; never scrolls). The workspace-state
322
+ digest (plans ≤5 time-desc + `+N more`, open residual findings ≤10 with
323
+ severity chips, policy with **enforcement first** then push / worktree /
324
+ control worktree, leases, knowledge, direction) sits IN FLOW at the end of
325
+ the scroll body — the fixed 300px sibling column and its nested scroller are
326
+ gone — and the freshness footer (`last-updated HH:MM:SS` + turn) closes the
327
+ scroll body. The host composer-overlay opt-in is no longer needed (the
328
+ sidebar pane body is a definite-height box) and the obsolete viewport media
329
+ queries are deleted: the shell carries `container-type: inline-size` and the
330
+ width rules are **container queries** — below 480px container width the
331
+ padding and group gap tighten (compact rhythm at the 300px floor); at ≥720px
332
+ the shared group grid spreads to two columns
333
+ (`repeat(auto-fit, minmax(280px, 1fr))`) — the only structural change any
334
+ width makes (one DOM, one tree; no JS layout measurement). Missing fields
335
+ degrade to explicit empty/last-known states (muted, never orange warn
336
+ boxes); `waiting` keeps the muted hint; no harness renders the centered
337
+ inactive-state card that activates automatically on detection.
338
+
339
+ The three sections stack in the single scroll body. **任务迭代** renders the
340
+ iteration head (collapsed summary when inactive) + the **vertical** 5-step
341
+ stepper (one row per step — badge · phase · chip · reserved verdict seat; the
342
+ four-state `current` / `next` / `done` / `idle` machine preserved: every step
343
+ BEFORE the current one projects `done`「已完成」, `next` is the single forward
344
+ target, `idle` is schema-only; Step 1 is the current step while the steering
345
+ compass is `status: active` — no PASS/FAIL badge — and Step 5 can never be
346
+ current) + the branch panel (iteration base / target / spec integration,
347
+ rendered only while active) + the plan board as **five stacked status
348
+ groups** in constant order — Todo / InProgress / InReview / Done / the merged
349
+ 「受阻/未知」/「Blocked / Unknown」column — with every `data-kanban-column` /
350
+ `data-kanban-arrow` / `data-kanban-count` anchor preserved; each group caps
351
+ its rendered rows at `PLAN_CAP` with the clickable 「更多」/「收起」 expander
352
+ (`data-kanban-more`) unfolding the full column (the projection keeps ALL plan
353
+ rows — the cap is a render concern, never a discard) — then the project
354
+ rollup. **事件记录** renders its two partitions (Agent 流转事件 / 违规记录) as
355
+ flow content — every row an expandable native `<details>` carrying the full
356
+ catalog fields (a missing field renders 「—」, never a guessed value; the
357
+ workflow-run rows keep their name / member-count / stop-reason fields), with
358
+ no partition-owned scrollers. **代理执行** renders the agents as a **vertical
359
+ grouped list** — the absolutely-positioned canvas with its SVG edge layer,
360
+ card ports, and pointer pan is DELETED: two phase groups in constant order,
361
+ Phase 1 (`iteration-start` — the sequential review-edit-chain:
362
+ product-manager → architect → writing-specialist) above Phase 2
363
+ (`autonomous-execute` — the iterative plan loop; its label annotates the
364
+ CURRENT PLAN: the projected `activePlanId` (`data-agent-group-plan`), `+N
365
+ more` when several plans run in parallel (`data-agent-group-plan-more`),
366
+ muted「无进行中 plan」when none (`data-agent-group-no-plan`)); each phase group
367
+ stacks its stage groups — `sdd-implement` splits into the **implementor**
368
+ partition (flow roles fullstack-dev / fullstack-dev-2 / frontend-dev in flow
369
+ order, then the on-demand roles ops-engineer / prompt-engineer carrying the
370
+ dashed **on-demand badge**) above the **reviewer** partition (code-reviewer),
371
+ and the `general` bucket sinks into the `unknown` sub-bucket at the bottom of
372
+ the LAST stage group, rendered only when it has members; every stage group
373
+ renders the full 14-role roster as **full-width flow rows** (idle rows
374
+ dashed-muted — the roster is never hidden) with role chip / status point /
375
+ `×N` count / record line (session id · task tag — auxiliary, never the
376
+ title), the `N executing · M pending` summary in the header
377
+ (`data-agent-summary-*`), and the three-status legend in flow below the list.
378
+ Rows carry the projected **emphasis tier**: `emphasis: 'current' | 'next' |
379
+ 'off' | null` — current-phase roles at **100%** chrome intensity, later-phase
379
380
  expected roles at **75%**, already-passed / stage-less (on-demand, general)
380
- roles at **45%**, and `null` (no iteration / unresolved transition) applies
381
- NO override — always a chrome **alpha mix** (`--mstar-canvas-emphasis-*`
382
- tokens; never a whole-card `opacity`, so the status point + running glow
383
- stay opaque). Settled entities get a **standalone GREEN DONE FRAME + green ✓**
384
- (plan `20260812-panel-f5-design-system` Task 8 — user round-4 feedback #1/#3:
385
- `data-agent-done="true"`, a full-strength success border + 1px ring on the
386
- rounded card body + the ✓ in the status point) **ONLY when `emphasis ≠ 'off'`**
387
- — an off-tier role (already-passed / stage-less on-demand + general) renders
388
- the muted dot instead and NEVER shows the completion marker. The agent
389
- canvas filters dispatch evidence to the **current iteration's plans only**
390
- (plan `20260813-panel-quick-fixes` Task 2): the steering compass
391
- `iterationId` when active, else the nearest iteration from the catalog
392
- `plans[].iterationRefs` (most-recent plan by 8-digit id date prefix +
393
- doneAt); provably cross-iteration events produce no entity/edge — the
394
- roster keeps its idle cards, and plan-less / unknown-plan / standalone
395
- dispatches are never hidden. Status honesty (Task 2): `advisory` is no
396
- longer terminal — a soft-enforcement dispatch falls through to its paired
397
- settle (green ✓ when a settle exists, `running` when none), `denied` stays
398
- terminal, and the advisory verdict still renders in the event log. The
399
- canvas legend sits BELOW the viewport (Task 3, moved from above). Edges — plan `20260812-panel-f5-design-system` Task 5 (design
400
- doc §2): the `expected` stage skeleton arrows AND the ANIMATED **next** edge
401
- (the former `@keyframes agent-dash-flow` dash-flow arrow of plan
402
- `20260810-panel-agent-flow-zone`) are **REMOVED** — flow order is implied
403
- by the fixed column order + column labels, the current position by the
404
- running card glow + status point — leaving TWO semantic kinds: the
405
- evidence-driven **`actual` handoff** edges (same-plan ts-adjacent dispatch
406
- entity-key pairs, `general` endpoints filtered, ≤1 per entity pair) drawn as
407
- **bezier `C` curves** anchored to card **ports** — 4 fixed edge-midpoint
408
- ports (north / south / east / west; static-invisible, hover-revealed as
409
- small dots) with the arrow tip pulled back to a **10px standoff** off the
410
- port — the arrow follows the line's local tangent at the anchor (**H1**),
411
- and no line's stroke or arrow crosses any text (**H2**: standoff + side-gap
412
- routing, design doc §2.0/§2.5/§2.6; tightened in plan
413
- `20260813-panel-quick-fixes` Task 3 — same-column vertical flows whose
414
- center-x line would cross an in-between card body reroute into the column's
415
- LEFT side gap (forward AND reverse), and reverse horizontal beziers keep
416
- direction-aware control points BETWEEN the endpoints, never bulging into
417
- the adjacent column) — plus the **bidirectional supervise
418
- line** (plan `20260812-panel-f5-agent-layout` Task 1/2) — one static
419
- design-knowledge sub-bucket edge inside the `sdd-implement` column
420
- (implementor ↔ sdd-reviewer — the mstar-sdd mutual-supervision contract),
421
- now anchored at the **side-gap vertical anchor** (`x = card right edge +
422
- 18px`, vertical bezier flow, arrows along the vertical tangent — design doc
423
- §2.5/§2.7); dim dashed by default, lit business SOLID when the projected
424
- `evidenced` flag is true — evidence-driven lighting, never a fabricated
425
- activation) — with the
426
- agent-flow event strip migrated into the **事件记录 (Event Log) tab** — a
427
- non-canvas log page (spec F1.5, plan `20260811-panel-event-log`): two
428
- partitions (**Agent 流转事件** / **违规记录**), every row an expandable
429
- native `<details>` carrying the full catalog fields (a missing field renders
430
- 「—」, never a guessed value), muted empty states — the two partitions
431
- render SIDE BY SIDE in a locked-height two-column grid
432
- (`repeat(2, minmax(0, 1fr))` — the page never scrolls as a whole; each
433
- partition pins its title and owns an internal `overflow-y` scroll on its
434
- row list; plan `20260813-panel-quick-fixes` Task 4 root-caused the
435
- whole-page scroll — the panel root opts into the host
436
- `data-conversation-composer-overlay` (the host's full-height opt-in), so
437
- `height:100%` resolves and `.rowList`'s `overflow-y: auto` scrolls INSIDE
438
- the partition (the host page no longer scrolls), with bottom clearance
439
- reserving the floating composer via the host-published
440
- `--dsh-composer-height`), falling back to two stacked 50/50 locked rows below 1200px (the
441
- `data-event-log-*` anchors unchanged, plan `20260811-panel-f3-agent-general`)
442
- — the canvas-corner
443
- **`AgentEventDock`** is REMOVED with the page (无双份日志, spec §5; the
444
- fixed footer bar — zone legend + gate summary + violations — died with the
445
- WorkflowCanvas zone dashboard in the tabs-shell plan; the footer that
446
- remains is the freshness marker). Empty branches (spec §2, plan
447
- `20260812-panel-f5-agent-layout` Task 3): waiting keeps the muted hint, and
448
- NO harness renders a **centered inactive-state card** — folder icon + 「No
449
- Morning Star harness detected」 title + hint copy (the detail panel stays
450
- inactive — no tabs, no sidebar — and activates automatically once a harness
451
- is detected; the `data-mstar-empty="no-harness"` anchor stays on the title,
452
- `data-mstar-graph` on the main container). Below 1200px the zones stack vertically. Projection is the pure
381
+ roles at **45%**, `null` (no iteration / unresolved transition) applies NO
382
+ override — always a chrome **alpha mix** (`--mstar-canvas-emphasis-*` tokens;
383
+ never a whole-row `opacity`, so the status point + running glow stay opaque).
384
+ Settled entities get a **standalone GREEN DONE FRAME + green ✓**
385
+ (`data-agent-done="true"` — success border + 1px ring on the rounded row body
386
+ + the ✓ in the status point, full-strength evidence states) **ONLY when
387
+ `emphasis ≠ 'off'`** — an off-tier role renders the muted dot instead and
388
+ NEVER shows the completion marker. The agents page contains zero `<svg>`
389
+ elements, zero `data-agent-port` / `data-canvas-*` anchors, and no pan
390
+ transform. Dispatch evidence still projects for the **current iteration's
391
+ plans only** (the steering compass `iterationId` when active, else the
392
+ nearest iteration from the catalog `plans[].iterationRefs`); provably
393
+ cross-iteration events produce no entity — the roster keeps its idle cards,
394
+ and plan-less / unknown-plan / standalone dispatches are never hidden. Status
395
+ honesty: `advisory` is not terminal — a soft-enforcement dispatch falls
396
+ through to its paired settle (green ✓ when a settle exists, `running` when
397
+ none), `denied` stays terminal, and the advisory verdict still renders in the
398
+ event log. The **iteration info section is SHARED by the tasks AND agents
399
+ pages** — one `IterationInfoSection` component, both pages render the same
400
+ `view.iteration` block (summary + steps + branches). Projection is the pure
453
401
  `projectGraph(source)` function (schema constants strictly separated from
454
- catalog evidence; never throws; missing fields degrade to explicit
455
- empty/last-known states — muted empty states, never orange warn boxes)
456
- producing a data-only `ZoneView`; `WorkflowCanvas` renders it as plain
457
- HTML/CSS.
402
+ catalog evidence; never throws).
458
403
 
459
- **Dependency**: the zone dashboard carries **no graph library** — the
404
+ **Dependency**: the panel's client bundle carries **no graph library** — the
460
405
  `@xyflow/react` devDependency (previously inlined into `dist/client.js` at
461
- build time) was removed with the react-flow rendering layer (plan
462
- `20260810-panel-canvas-zones`), and the plain-`.css` text loader whose only
463
- consumer was `@xyflow/react/dist/style.css` is gone too (`CLIENT_EXTERNALS`
406
+ build time) was removed with the react-flow rendering layer, and the
407
+ plain-`.css` text loader whose only consumer was
408
+ `@xyflow/react/dist/style.css` is gone too (`CLIENT_EXTERNALS`
464
409
  is unchanged — react / react-dom and the `@deepseek-ai/dsh-client-*` platform
465
410
  modules stay external). The build script asserts the removal end to end: the
466
411
  emitted bundle must contain **no `xyflow`/`reactflow` markers**, zero
@@ -468,11 +413,11 @@ emitted bundle must contain **no `xyflow`/`reactflow` markers**, zero
468
413
  the web loader executes plugin bundles as classic `<script>`s, where a
469
414
  literal `import.meta` is a parse-time SyntaxError (a zustand v4
470
415
  `import.meta.env` read is defined away at build; see the iteration
471
- install-verification guide §6). Bundle size at this plan's wrap-up: **145,159 B
416
+ install-verification guide §6). Current bundle size: **145,159 B
472
417
  raw / 29,460 B gzip** (re-measure per the iteration install-verification
473
418
  guide — the bundle shrank to ~85 KB when react-flow was removed and grew
474
- back with the agent-execution zone's entity rendering, then again with the
475
- F5 emphasis tiers + edge rework).
419
+ back with the agents page's entity rendering, then again with the
420
+ emphasis-tier styling).
476
421
 
477
422
  Install / verify (the client half rides the same bundle-row install as the
478
423
  server half):
@@ -511,9 +456,12 @@ the iteration from plan ids (8-digit date prefix) + doneAt — a
511
456
  deterministic, documented heuristic, and only provably cross-iteration
512
457
  events are dropped; no historical
513
458
  back-scan of a resumed long log (the server re-emits the row at every turn's
514
- first step, digest-gated); no custom top-level slot (the `conversation.view`
515
- tab is the only session-level panel seat available without dsh-private layout
516
- changes — spec §1). Panel acceptance is dual-track: in-loop browser harness
459
+ first step, digest-gated); the sidebar chip title is captured at open time (a
460
+ mid-session locale switch does not flip it — accepted for glyph parity with
461
+ first-party chips); a docked body renders nothing while the column is
462
+ collapsed or another pane tab is active (`tab.visible === false` — no
463
+ projection, no DOM, zero per-snapshot cost while hidden). Panel acceptance is
464
+ dual-track: in-loop browser harness
517
465
  verification against the rebuilt bundle plus user-restart final GUI acceptance —
518
466
  rerun steps in the install-verification guide §8.
519
467
 
@@ -540,7 +488,7 @@ The dev-time seam surfaces (types, event shapes, runtimes) are the REAL `@deepse
540
488
 
541
489
  #### What the model sees
542
490
 
543
- Every composed step carries one `mstar-engine-status` catalog user message (the `<mstar_engine_status>` watermark block — see the Engine-status catalog section). Gate decisions add: the dispatch veto as the registry-materialized `PreToolDecision { kind: 'deny', reason }` error; the status gate as the `mstar/status-gate` advisory (warn pass, hard-mode repair escape, or degraded allow); the dispatch gate as the `mstar/dispatch-gate` advisory (warn pass or degraded); the skill lint gate as the `mstar/skill-lint` advisory (warn pass, hard-mode repair escape, or degraded allow). Every model-visible row is reconstructable from the session log (catalog-form sources + advisory events).
491
+ Every composed step carries one `mstar-engine` catalog user message (the `<mstar_engine_status>` watermark block — see the Engine-status catalog section). Gate decisions add: the dispatch veto as the registry-materialized `PreToolDecision { kind: 'deny', reason }` error; the status gate as the `mstar/status-gate` advisory (warn pass, hard-mode repair escape, or degraded allow); the dispatch gate as the `mstar/dispatch-gate` advisory (warn pass or degraded); the skill lint gate as the `mstar/skill-lint` advisory (warn pass, hard-mode repair escape, or degraded allow). Every model-visible row is reconstructable from the session log (catalog-form sources + advisory events).
544
492
 
545
493
  **Leaf delivery discipline (PM 2026-08-12):** leaf subagents hand back their Completion Report in the **final (closing) message**, not via the `report` tool — the dsh tool-subagent-report default `reportDelivery: quiet` routes a report into the parent's next-step queue, where it strands when the parent's turn has ended (no step boundary follows). The closing message is the guaranteed delivery channel; reserve `report` for mid-turn findings that change what the parent should do next (SSOT: `skills/mstar-host/references/dsh.md` → PM dispatch).
546
494
 
@@ -575,7 +523,7 @@ The catalog row is appended at the END of the composed step messages, after dele
575
523
  - **CLI `HOST_SIGNALS` lacks the `subagent` token** — the engine `ToolSignal` union includes it and `detectHost` handles it, but `packages/cli` `HOST_SIGNALS` is not updated yet, so `mstar host detect --signals subagent` would reject until the CLI list is updated on upstreaming.
576
524
  - **Entry is a module index over `src/gates/*`** — the split shipped: `src/index.ts` re-exports the frozen 56-name export surface (31 value + 25 type-only names; `Config` counts once) from the gate modules (`_shared` / `status` / `skill-lint` / `seams` / `dispatch` / `catalog` / `tools` / `adapter`) and keeps the plugin manifest, the single cordis augmentation point, the command registration, and the `apply()` startup wiring. The surface is frozen by `tests/export-surface.spec.ts` — the runtime value-export set plus, under `typecheck:tests` (`bunx tsc --noEmit -p tests/tsconfig.json`), the value-namespace identity and the per-name type-only probes.
577
525
  - **Engine dsh rows are upstreaming-destined** — the dsh changes to engine `host.ts` (`DetectResult`, `ToolSignal`, `resolveSkillRoot`) live in the mstar-workflow engine mirror and are intended for a user-authorized upstream PR into mstar-harness; the `mstar-host` skill mirror (§ Detect / § Resolve loaded skill root / `references/dsh.md`) updates with it.
578
- - **Iteration stepper: Step 1 is compass-driven, Step 5 is schema-driven** — the zone dashboard's Step 1 (iteration-start) is the current step while the steering compass is `status: active` (Phase 1 in flight — no gate verdict, so no PASS/FAIL badge); Step 5 (merge-ready) is a schema constant the engine gate never lights as current (transition covers Phase 2→3→4 only), so it always renders idle — recorded in the iteration guide, not a defect. The full panel-limitation list lives in the Web client plugin section.
526
+ - **Iteration stepper: Step 1 is compass-driven, Step 5 is schema-driven** — the workflow panel's Step 1 (iteration-start) is the current step while the steering compass is `status: active` (Phase 1 in flight — no gate verdict, so no PASS/FAIL badge); Step 5 (merge-ready) is a schema constant the engine gate never lights as current (transition covers Phase 2→3→4 only), so it always renders idle — recorded in the iteration guide, not a defect. The full panel-limitation list lives in the Web client plugin section.
579
527
  - **`dsh-llm-fallbacks` is an optional dev-time-only dependency** — dsh natively covers subagent customization, so fallbacks is strictly optional: `src/` carries zero imports of it (runtime and type — the consumed surface is the local structural mirror `fallbacks-structural.ts`, kept in sync by the probe's exact-keys drift gate plus the `typecheck:tests` real → view assignability check), `package.json` carries it only under `devDependencies` (type mirroring + the real-package test harness), and `dist/` carries no import and no type reference (only ONE string literal naming the package — the probe's loader-entry match; the advisory logs say `fallbacks`). Activation is a SEPARATE explicit install (two-command contract), never transitive; there is no `--external` guard anymore — a future value import must re-add a runtime dependency by design.
580
528
  - **Role→model override NOT delivered this batch** — routing a role to a fallbacks `model` (or persona via fallbacks rules) would require rewriting the child's `agentOptions` on the start request, but start-request options are caller-controlled (tool-subagent's own Config; call args are `description`/`prompt`/`run_in_background` only, deep-frozen). Awaits upstream `fallbacks-explicit-role-tool` or the N-B1 systemPrompt adoption (roadmap §10.4).
581
529
  - **Persona delivery is dsh-native — no additive section** — the role persona merges into `SubagentStartRequest.persona` (one-shot `start` AND the opt-in continuable `startContinuable`) and SHADOWS the deployment persona for role-matched children (child embodies the role; persisted + reapplied on resume). There is NO `mstar:role-persona` system-prompt section anymore.