@mstar-harness/dsh 3.4.0 → 3.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -50,7 +50,7 @@ The **two-command install is the contract** — folding a `dsh-llm-fallbacks` ro
50
50
  | `harnessDir` | `string` | repo `.mstarc` `[config] harness_dir`, else per-session workspace probe (`.mstar/` → `.agents/` → `.plans/` → `plans/`, from the session workspace root — **never the launch cwd**) | Explicit harness root; wins over engine probing. **Required for repos whose harness root is not a probed name**; the probe starts from the session workspace root (never the launch cwd) and STOPS there — it never walks above the session workspace, so a harness dir above it (e.g. a global `~/.mstar`) is never adopted. |
51
51
  | `enforcement` | `'hard' \| 'soft'` | compass, else warn-only | Per-deployment override. Precedence: Config wins; else the Assignment's own `**Enforcement**: hard` header flag (dispatch gate only); else the repo `.mstarc` `[config] enforcement`; else the iteration compass frontmatter; else warn-only. Config / `.mstarc` `soft` are the ONLY local rollbacks — an Assignment-level `soft` does NOT override a hard compass. |
52
52
  | `dispatchTools` | `string[]` | `['subagent', 'subagent_fork']` | Delegation tool names the dispatch gate matches — the dsh preset's TWO delegation tools, `subagent` and its fork sibling `subagent_fork` (both carry Assignment-shaped `{ description, prompt }` args; a `toolName` config may rename instances). |
53
- | `dispatchBinding` | `string` | unset (precheck skipped) | The dispatching agent's own harness role; an Assignment whose `Execute as` equals it is self-recursion. |
53
+ | `dispatchBinding` | `string` | unset fail-closed `empty-binding` under hard | The dispatching agent's own harness role (the anti-recursion CALLER); an Assignment whose `Execute as` equals it is self-recursion. |
54
54
  | `roleMap` | `Record<string, string>` | unset | mstar role id (`Execute as`) → dsh-llm-fallbacks role id. A taxonomy bridge for logging + future rule-driven interop ONLY — never consulted by the decoration (see LLM fallbacks integration). |
55
55
  | `rolePersonas` | `Record<string, string>` | unset (bundled mirror default) | mstar role id (`Execute as`) → persona text; the role-based subagent decoration's **override** source — a role-matched `subagent/start` registers the persona as the child's `mstar:role-persona` system-prompt section; when unset for a role, the bundled `harness-agents/` mirror default is used (see LLM fallbacks integration). |
56
56
  | `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. |
package/README.zh.md CHANGED
@@ -50,7 +50,7 @@ dsh plugin --profile web add dsh-llm-fallbacks
50
50
  | `harnessDir` | `string` | 仓库 `.mstarc` `[config] harness_dir`,否则按会话工作区探测(`.mstar/` → `.agents/` → `.plans/` → `plans/`,从会话工作区根目录开始——**绝不从启动 cwd**) | 显式 harness 根目录;优先于 engine 探测。**harness 根不在探测名列表中的仓库必须配置**;探测从会话工作区根开始(绝不从启动 cwd)并在那里**停止**——永不越过会话工作区向上,因此其上方的 harness 目录(如全局 `~/.mstar`)永远不会被采纳。 |
51
51
  | `enforcement` | `'hard' \| 'soft'` | compass,否则仅告警 | 按部署覆盖。优先级:Config 优先;否则取 Assignment 自身的 `**Enforcement**: hard` 头字段(仅派发闸门);否则取仓库 `.mstarc` `[config] enforcement`;否则取迭代 compass frontmatter;否则仅告警。Config / `.mstarc` `soft` 是仅有的本地回滚——Assignment 级 `soft` 不能覆盖 hard compass。 |
52
52
  | `dispatchTools` | `string[]` | `['subagent', 'subagent_fork']` | 派发闸门匹配的委派工具名——dsh preset 的**两个**委派工具:`subagent` 及其 fork 兄弟 `subagent_fork`(两者都携带 Assignment 形态的 `{ description, prompt }` 参数;`toolName` 配置可重命名实例)。 |
53
- | `dispatchBinding` | `string` | 未设置(跳过预检) | 派发方 agent 自身的 harness 角色;Assignment 的 `Execute as` 等于它即自我递归。 |
53
+ | `dispatchBinding` | `string` | 未设置 → hard 下 fail-closed `empty-binding` | 派发方 agent 自身的 harness 角色(反递归 caller);Assignment 的 `Execute as` 等于它即自我递归。 |
54
54
  | `roleMap` | `Record<string, string>` | 未设置 | mstar 角色 id(`Execute as`)→ dsh-llm-fallbacks 角色 id。**仅**作日志与未来规则驱动互操作的分类桥——装饰从不读取它(见 LLM fallbacks integration)。 |
55
55
  | `rolePersonas` | `Record<string, string>` | 未设置(打包镜像默认) | mstar 角色 id(`Execute as`)→ persona 文本;基于角色的 subagent 装饰的**覆盖**来源——角色匹配的 `subagent/start` 会把 persona 注册为子会话的 `mstar:role-persona` system-prompt 段;某角色未设置时使用打包的 `harness-agents/` 镜像默认值(见 LLM fallbacks integration)。 |
56
56
  | `skillRoots` | `string[]` | 未设置(不注册自定义根) | 向 dsh skill-filesystem 提供者注册的额外技能根(`customSkillDirs` 语义——先于用户根扫描)。开发期:镜像 `<repo-root>/skills` 的绝对路径。 |
package/bundle/README.md CHANGED
@@ -79,7 +79,7 @@ The `mstar` row accepts the plugin `Config` (see `src/index.ts`):
79
79
  | `harnessDir` | unset (resolved per session workspace) | explicit `{HARNESS_DIR}` root — **required for repos whose harness root is not a probed name** (`.mstar/` → `.agents/` → `.plans/` → `plans/`); set it to the absolute root in the profile layer (or declare `[config] harness_dir` in a repo `.mstarc` — gitignored local config, honored above probing). Without either, the probe starts from the SESSION workspace root (the session cwd — **never the process/launch cwd**) and **stops there** — it never walks above the session workspace, so a global `~/.mstar` is never adopted |
80
80
  | `enforcement` | **unset — default OFF** | `hard` / `soft` override; absent → the repo `.mstarc` `[config] enforcement`, else the iteration compass decides, warn-only when no compass hardens (never a global always-on hard gate) |
81
81
  | `dispatchTools` | unset (plugin default `['subagent', 'subagent_fork']`) | delegation tool names the dispatch gate matches — the dsh preset's TWO delegation tools (`subagent` + its fork sibling `subagent_fork`, both Assignment-shaped); a custom list overrides the default wholesale, so it must include `subagent_fork` to keep fork dispatches gated |
82
- | `dispatchBinding` | unset | the dispatching agent's role for the anti-recursion precheck |
82
+ | `dispatchBinding` | unset → fail-closed `empty-binding` under hard | the dispatching agent's own role (the anti-recursion CALLER) for the precheck |
83
83
  | `skillRoots` | unset | additional skill roots (custom mirrors) |
84
84
  | `bundledSkillDir` | unset → plugin resolves its OWN packaged `harness-skills/` mirror package-relative | bundled skill mount — the repo-root `skills/` mirror synced by `bundle-assets` at build time (gitignored), resolved package-relative (NOT cwd-anchored). An explicit value wins; a RELATIVE override stays cwd-anchored, so pass an absolute path in the profile layer |
85
85
  | `catalogTtlMs` | unset → `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 |
package/dist/index.js CHANGED
@@ -1841,8 +1841,10 @@ function composeDispatchGate(text, opts = {}) {
1841
1841
  const violations = [];
1842
1842
  const writable = opts.writable !== false;
1843
1843
  violations.push(...validateAssignmentFields(text, { writable }).violations);
1844
- const agent = opts.agent ?? "";
1845
- violations.push(...antiRecursionPrecheck(agent, parseAssignmentFields(text).executeAs ?? "").violations);
1844
+ const caller = opts.caller ?? "";
1845
+ if (caller.trim() !== "" || opts.callerRequired === true) {
1846
+ violations.push(...antiRecursionPrecheck(caller, parseAssignmentFields(text).executeAs ?? "").violations);
1847
+ }
1846
1848
  if (writable) {
1847
1849
  const forms = parseAssignmentBranchForms(text);
1848
1850
  const branch = forms.createForm?.name ?? forms.workingBranch ?? forms.directOn?.branch ?? process.env.MSTAR_WORKING_BRANCH;
@@ -1865,7 +1867,7 @@ function antiRecursionPrecheck(subagentType, executeAs) {
1865
1867
  return {
1866
1868
  ok: false,
1867
1869
  violations: [
1868
- violation3("critical", "dispatch.anti-recursion.empty-binding", `empty host role binding — the host cannot report which agent is calling, so anti-recursion cannot be proven (a dispatch could silently recurse)`, "set the host role-binding field (omp task entry `agent` / opencode `subagent` / cursor `subagent_type` / dsh `dispatchBinding`) before dispatching")
1870
+ violation3("critical", "dispatch.anti-recursion.empty-binding", `empty caller role binding — the host cannot report which agent is calling, so anti-recursion cannot be proven (a dispatch could silently recurse)`, "declare the dispatching agent's own role binding (dsh Config `dispatchBinding`) before dispatching")
1869
1871
  ]
1870
1872
  };
1871
1873
  }
@@ -4609,7 +4611,7 @@ function dispatchGateCore(config, harnessDir, prompt) {
4609
4611
  const header = assignmentHeaderRegion(prompt);
4610
4612
  const violations = [...worktreeL2Violations(header), ...worktreeL1Violations(harnessDir, header)];
4611
4613
  const writable = isReadOnlyAssignmentRole(parseAssignmentFields(header).executeAs ?? "") ? false : undefined;
4612
- violations.push(...composeDispatchGate(header, { agent: config.dispatchBinding ?? "", writable }).violations);
4614
+ violations.push(...composeDispatchGate(header, { caller: config.dispatchBinding ?? "", callerRequired: true, writable }).violations);
4613
4615
  return { violations, writable };
4614
4616
  }
4615
4617
  function workflowGateInputOf(exec) {
@@ -40,6 +40,8 @@ description: Morning Star 派发与委派门禁 —— 仅 PM 可增派 subagent
40
40
 
41
41
  **Assignment 顶部反模式块**:每个 PM Assignment 开头均有 **`**You are a leaf executor. You MUST NOT:**`** 块(含 IDENTITY + CAPABILITY BOUNDARY + prohibitions),PM 按此 Assignment 的角色+上下文定制反模式清单。leaf executor 收到 Assignment 后须 **首先** 阅读该块;命中任一条 → **停止**(亲自完成或 `Blocked`)。详见 **`mstar-roles/references/project-manager/dispatch-and-assignment.md`**。
42
42
 
43
+ > **Engine 执行范围(caller-scoped,#156)**:engine `antiRecursionPrecheck` 比较的是**派发方自身角色**(caller)与新 Assignment 的 `Execute as`(target)。只有 **dsh**(Config `dispatchBinding`)能观察派发方身份并在 engine 层硬执行(含 `callerRequired` 空绑定 fail-closed);omp / OpenCode / Cursor 的角色绑定字段是**派发目标**——目标 == `Execute as` 正是 C5 合规派发模式——这些宿主上红线保持 prompt 级约束(本节),engine 不做判定。
44
+
43
45
  ## 调度防串扰(强制;leaf executor 已在上方读过反递归红线,此处为完整规则供 PM/对照用)
44
46
 
45
47
  - 只有 **`project-manager`** 可以决定增加/并行 subagent;承接方**默认不得二次分派**。
@@ -58,7 +60,7 @@ description: Morning Star 派发与委派门禁 —— 仅 PM 可增派 subagent
58
60
  - **QC 单席(例外)**:`Execution mode: inline`(hotfix 等),或 Assignment 显式 `QC mode: single` / `QC mode: single — override: <reason>` → `qc-specialist` ×1,`N=1`,写 `{SDD_DIR}/review/qc.md`。
59
61
  - **QC targeted re-review**:Assignment 含 **`QC re-review: targeted — reviewers: …`** 时,**N** = 所列席位数(1–3),同条消息发满 **N**。
60
62
  - **先自检再发送**:发送前核对「Assignment 条数 = 本条消息中的实际 **派发** 调用条数」。
61
- - **先自检字段再发送(与 count 同级门禁)**:核对**每条** invoke 都携带与 **`Execute as`** 匹配的角色绑定字段——omp **`agent`** / Cursor **`subagent_type`** / OpenCode **`subagent`** / Kimi·ZCode **`subagent_type`**。**漏写或取默认通用值**(omp 漏 `agent` ⇒ 自动回退 generic `task`,无报错)= **派发未完成**,与 paste-only(零 invoke)**同等级**:当场补齐重发,不得进入下一 gate。**N=1 顺序链(Review & Edit)不豁免**——count 门在 N=1 恒过,**字段门是唯一保护**。
63
+ - **先自检字段再发送(与 count 同级门禁)**:核对**每条** invoke 都携带与 **`Execute as`** 匹配的角色绑定字段——omp **`agent`** / Cursor **`subagent_type`** / OpenCode **`subagent`** / Kimi·ZCode **`subagent_type`**;宿主列以 **`mstar-host`** §Detect active host 的 tool-shape 检测为准(禁以 config 路径/仓库内容判定)。**漏写或取默认通用值**(omp 漏 `agent` ⇒ 自动回退 generic `task`,无报错)= **派发未完成**,与 paste-only(零 invoke)**同等级**:当场补齐重发,不得进入下一 gate。**N=1 顺序链(Review & Edit)不豁免**——count 门在 N=1 恒过,**字段门是唯一保护**。
62
64
  - **前置步骤与派发回合分离(防串行 rollout)**:为派发准备的 **`bash` / `read` / `glob` / `grep`**(如 `merge-base`、`Review range`、`git rev-parse`)**不计入** `N` 次派发;可在上一条仅含准备的消息完成。准备完成后,**下一条派发消息**须**一次性**含 **`N` 次** Task / subagent invoke。**禁止**先发 `1` 次、等返回再补发其余 `N-1` 次。
63
65
  - **未齐不发(emit zero until batch-ready)**:需并发 `N≥2` 而当前只能发 `1` 条时,本条应发 **`0` 条派发 invoke`**(可继续 read/bash 补齐),**禁止**「先发一个顶一下」;`N` 份 payload 就绪后**单次消息发满 `N`**。见 **`mstar-host`** → `references/parallel-dispatch.md`(具备 invoke / Task / subagent 工具的宿主共用)。
64
66
 
@@ -472,7 +472,9 @@ The dsh web client resolves slash commands against a client-side lexicon driven
472
472
  - The dispatch gate needs the dispatching agent's own role for the
473
473
  anti-recursion precheck: declare it via Config **`dispatchBinding`** (dsh
474
474
  exposes no per-agent role on the tool-execution context). Under hard
475
- enforcement with no binding, the plugin logs the absence.
475
+ enforcement with no binding, the plugin logs the absence AND every
476
+ Assignment-shaped dispatch fails closed (`dispatch.anti-recursion.
477
+ empty-binding` → deny) until the binding is set.
476
478
 
477
479
  ## Files, shell, and approvals
478
480
 
@@ -211,7 +211,8 @@ Cannot emit required **N** → **`Blocked`**.
211
211
  ## In-process engine binding (omp ≥ 17.2.11)
212
212
 
213
213
  - **Surfaces** (repo root = plugin root): `hooks/pre/mstar-gates.ts` — one `tool_call` pre-hook that returns `{ block: true, reason }` (structured refusal the model sees as the tool error) or `undefined` (pass); `tools/mstar_{status_validate,dispatch_validate,lease_verify,path_resolve,iteration_gate,worktree_check}/index.ts` — six model-callable validator tools (engine validators only, Zod params via `pi.zod`).
214
- |- **Enforcement semantics**: block ONLY under `Enforcement: hard`. The status gate reads the repo `.mstarc` `[config] enforcement`, else the harness compass frontmatter (`enforcement: hard`, active/locked iterations only); the dispatch gate reads each Assignment's own header flag (`assignmentHeaderRegion` — a body example never hardens). Soft / no flag silent pass. Rollback = unset the flag (or `.mstarc` `soft`). Never global.
214
+ |- **Enforcement semantics**: block ONLY under `Enforcement: hard`. Both gates read the repo `.mstarc` `[config] enforcement`, else the harness compass frontmatter (`enforcement: hard`, active/locked iterations only); the dispatch gate ALSO honors each Assignment's own header flag (`assignmentHeaderRegion` — a body example never hardens). A hard repo setting therefore hardens flag-less dispatches (Gate 1 / dsh `resolveDispatchHard` parity). Soft-mode dispatch violations are warn-logged through the extension logger (never blocked); soft status-write violations stay a silent pass. Rollback = unset the flag (or `.mstarc` `soft`). Never global.
215
+ - **Anti-recursion scope (issue #156)**: the engine's `antiRecursionPrecheck` is **caller-scoped** — it compares the DISPATCHING agent's own role against the new Assignment's `Execute as`. omp's `tool_call` event carries no caller identity and the task entry `agent` is the spawn TARGET, which equals `Execute as` on every compliant dispatch (C5 above) — so Gate 2 does NOT run the precheck on omp (the pre-#156 wiring hard-blocked every compliant hard-mode dispatch on `self-type`, or on `empty-binding` when `agent` was omitted). The NEVER red line stays prompt-level on this host (`mstar-dispatch-gates`); dsh enforces it in-engine via Config `dispatchBinding`.
215
216
  - **Engine dependency**: the adapters import the published engine package (root `package.json` `dependencies` entry). omp git/npm plugin installs run `bun install <spec>` in the plugins tree → declared deps installed; a bare `-l` / `omp plugin link` symlink install without `node_modules` cannot resolve the modules.
216
217
  - **Graceful degradation (explicit)**: module load failure → `mstar_*` tools skipped, hook absent (no blocking), `commands/*.md` shell-out fallback intact. Caveat: a partial failure is SILENT — no in-band signal that gates are off; verify with `omp -p '/extensions'`.
217
218
  |- **`MSTAR_HARNESS_DIR` override / `.mstarc`**: the hook and tools discover `{HARNESS_DIR}` via `resolveHarnessDir` — a repo `.mstarc` `[config] harness_dir` (gitignored local config) first, then the probe `.mstar/` → `.agents/` → `.plans/`/`plans/`. Repos using a non-standard harness root can declare it in `.mstarc` or MUST export `MSTAR_HARNESS_DIR` (absolute path) in the omp session env — without either the status gate does not cover those roots and tools like `mstar_path_resolve` / `mstar_lease_verify` error out (parity with the opencode binding).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mstar-harness/dsh",
3
- "version": "3.4.0",
3
+ "version": "3.4.1",
4
4
  "description": "Morning Star harness dsh (DeepSeek Harness) cordis function plugin — in-process engine gates (status/dispatch/lease) with hard refusal channels.",
5
5
  "keywords": [
6
6
  "dsh",