@mstar-harness/dsh 3.1.1 → 3.1.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.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  English | [中文](README.zh.md)
4
4
 
5
- ![dsh](https://img.shields.io/badge/dsh-0.1.0--rc.8-4B32C3.svg)
5
+ ![dsh](https://img.shields.io/badge/dsh-0.1.1--rc.1-4B32C3.svg)
6
6
 
7
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.
8
8
 
@@ -561,7 +561,7 @@ The catalog row is appended at the END of the composed step messages, after dele
561
561
  - **Entry is a module index over `src/gates/*`** — the split shipped: `src/index.ts` (371 lines) re-exports the frozen 47-name export surface (28 value + 19 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.
562
562
  - **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.
563
563
  - **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.
564
- - **`dsh-llm-fallbacks` is a registry `dependencies` entry with type-only imports** — declared `^0.2.0` (caret range admits 0.2.1+; the probe shape-assertion test is the executable drift gate) and `--external` in the build, so `dist/` carries no bundled runtime import (only three string literals naming the package — the probe's loader-entry match and the two decoration log lines; the advisory logs say `fallbacks`); activation is a SEPARATE explicit install (two-command contract), never transitive. The library-form dependency exists so future value imports resolve without a manifest change; `--external` remains the guard.
564
+ - **`dsh-llm-fallbacks` is a registry `dependencies` entry with type-only imports** — caret range lives in `package.json` (the probe shape-assertion test is the executable drift gate) and `--external` in the build, so `dist/` carries no bundled runtime import (only three string literals naming the package — the probe's loader-entry match and the two decoration log lines; the advisory logs say `fallbacks`); activation is a SEPARATE explicit install (two-command contract), never transitive. The library-form dependency exists so future value imports resolve without a manifest change; `--external` remains the guard.
565
565
  - **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).
566
566
  - **Decoration is a minimal per-child section, not the N-B1 systemPrompt adoption** — `mstar:role-persona` is one agent-scoped section on the child's context; no harness-rules sections, no PromptContext, no variables. N-B1 (roadmap §10.4) may absorb or replace this channel later without changing the observable (AC-3).
567
567
  - **Persona injection is fallbacks-independent** — `dsh-llm-fallbacks` only routes LLM failures; it is never required for decoration. Unmounted → the same persona lands from the mstar Config through the same channel with one debug log (AC-4). If `ctx.get('agents')` is absent in a composition (no dsh-agent), decoration is skipped with one debug log.
package/README.zh.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [English](README.md) | 中文
4
4
 
5
- ![dsh](https://img.shields.io/badge/dsh-0.1.0--rc.8-4B32C3.svg)
5
+ ![dsh](https://img.shields.io/badge/dsh-0.1.1--rc.1-4B32C3.svg)
6
6
 
7
7
  让 [Morning Star](https://github.com/btspoony/mstar-harness) 成为一等公民的 dsh(DeepSeek Harness)宿主——一个 cordis 函数插件,将 mstar engine 进程内挂载,实现 engine `HostAdapter`(`host: 'dsh'`),守护 `{HARNESS_DIR}/status.json` 写入(校验 + 咨询;hard 下按修复逃生放行),在 `Enforcement: hard` 开启时阻止被禁止的 subagent 派发,对挂载技能根下的 `SKILL.md` 写入执行技能撰写 lint,通过 dsh skill-filesystem 提供者挂载 mstar `skills/` 镜像(单一规范挂载),并向每个组合后的 agent 步骤追加一条持久化的 `mstar-engine-status` catalog 行。随 dsh Loader 应用启动;一切均通过 seam 的拒绝/咨询通道行使职责,从不改动工具本身。
8
8
 
@@ -334,7 +334,7 @@ catalog 行在委托之后追加到组合步骤消息的**末尾**——请求
334
334
  - **入口是 `src/gates/*` 之上的模块索引**——拆分已交付:`src/index.ts`(371 行)从各 gate 模块(`_shared` / `status` / `skill-lint` / `seams` / `dispatch` / `catalog` / `tools` / `adapter`)原样 re-export 冻结的 47 名导出面(28 值导出 + 19 type-only 名;`Config` 计一次),并保留插件 manifest、单一 cordis augmentation 点、命令注册与 `apply()` 启动接线。导出面由 `tests/export-surface.spec.ts` 冻结——运行时值导出集 + `typecheck:tests`(`bunx tsc --noEmit -p tests/tsconfig.json`)下的值命名空间恒等与逐名类型探测。
335
335
  - **engine dsh 行待上游化**——engine `host.ts` 的 dsh 改动(`DetectResult`、`ToolSignal`、`resolveSkillRoot`)位于 mstar-workflow engine 镜像,计划经用户授权的上游 PR 合入 mstar-harness;`mstar-host` 技能镜像(§ Detect / § Resolve loaded skill root / `references/dsh.md`)随之一并更新。
336
336
  - **迭代 stepper:Step 1 为 compass 驱动,Step 5 为 schema 驱动**——zone dashboard 的 Step 1(iteration-start)在 steering compass `status: active`(Phase 1 进行中)时为当前步(无 gate 判定 → 无 PASS/FAIL 徽标);Step 5(merge-ready)是 engine 闸门永不点亮为当前的 schema 常量(transition 只覆盖 Phase 2→3→4,merge-ready 从不是 gate transition);仅当 Step 4 为当前步时作为 `next` 渲染,其余为 idle——已记录于迭代 guide,非缺陷。完整面板限制清单见 Web 客户端插件一节。
337
- - **`dsh-llm-fallbacks` 为 registry `dependencies` 条目且仅类型导入**——声明 `^0.2.0`(caret 范围允许 0.2.1+;探测形状断言测试是可执行的漂移闸门)并在构建中 `--external`,因此 `dist/` 无打包运行时导入(仅 3 处命名该包的字符串字面量——探测的 loader 条目匹配与两条装饰日志;建议日志写作 `fallbacks`);激活是**单独显式安装**(双命令契约),绝不传递。库形态依赖存在是为未来值导入无需改 manifest 即可解析;`--external` 仍是护栏。
337
+ - **`dsh-llm-fallbacks` 为 registry `dependencies` 条目且仅类型导入**——caret 范围写在 `package.json`(探测形状断言测试是可执行的漂移闸门)并在构建中 `--external`,因此 `dist/` 无打包运行时导入(仅 3 处命名该包的字符串字面量——探测的 loader 条目匹配与两条装饰日志;建议日志写作 `fallbacks`);激活是**单独显式安装**(双命令契约),绝不传递。库形态依赖存在是为未来值导入无需改 manifest 即可解析;`--external` 仍是护栏。
338
338
  - **本批次未交付角色→模型覆盖**——把角色路由到 fallbacks `model`(或经 fallbacks 规则路由 persona)需要改写启动请求上的子会话 `agentOptions`,但启动请求选项由调用方控制(tool-subagent 自己的 Config;调用参数仅为 `description`/`prompt`/`run_in_background`,且深度冻结)。等待上游 `fallbacks-explicit-role-tool` 或 N-B1 systemPrompt 采纳(roadmap §10.4)。
339
339
  - **装饰是最小化的每子会话段,而非 N-B1 systemPrompt 采纳**——`mstar:role-persona` 是子上下文上的一个 agent 作用域段;无 harness 规则段、无 PromptContext、无变量。N-B1(roadmap §10.4)日后可吸收或替换该通道而不改变可观察行为(AC-3)。
340
340
  - **persona 注入与 fallbacks 无关**——`dsh-llm-fallbacks` 只路由 LLM 失败;装饰从不依赖它。未挂载 → 同一 persona 经同一通道来自 mstar Config,仅多一条 debug 日志(AC-4)。若组合中缺 `ctx.get('agents')`(无 dsh-agent),装饰以一条 debug 日志跳过。
@@ -35,8 +35,10 @@ export interface Config {
35
35
  * as the anti-recursion binding: an Assignment whose `Execute as` equals this
36
36
  * role is a self-dispatch (critical violation — leaf executors must not
37
37
  * re-invoke their own role). dsh exposes no agent role on the tool-execution
38
- * context, so the deployment declares it. Absent → the anti-recursion
39
- * precheck is skipped (an empty binding is not self-recursion).
38
+ * context, so the deployment declares it. Absent → the empty binding FAILS
39
+ * CLOSED: every Assignment-shaped dispatch emits
40
+ * `dispatch.anti-recursion.empty-binding` (critical) until the binding is
41
+ * set.
40
42
  */
41
43
  dispatchBinding?: string;
42
44
  /**
package/dist/index.js CHANGED
@@ -1106,7 +1106,7 @@ var require_lib2 = __commonJS((exports, module) => {
1106
1106
  });
1107
1107
 
1108
1108
  // src/index.ts
1109
- import { existsSync as existsSync20, readFileSync as readFileSync18, readdirSync as readdirSync14 } from "node:fs";
1109
+ import { existsSync as existsSync21, readFileSync as readFileSync18, readdirSync as readdirSync14 } from "node:fs";
1110
1110
  import { join as join23 } from "node:path";
1111
1111
  import { fileURLToPath as fileURLToPath3 } from "node:url";
1112
1112
  import {
@@ -1122,23 +1122,23 @@ import { basename, dirname, join, resolve } from "node:path";
1122
1122
  import { fileURLToPath } from "node:url";
1123
1123
  import { readFileSync as readFileSync2, statSync } from "node:fs";
1124
1124
  import { dirname as dirname2, isAbsolute, join as join2, relative, resolve as resolve2 } from "node:path";
1125
- import { mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync3, statSync as statSync2 } from "node:fs";
1125
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync3, realpathSync, statSync as statSync2 } from "node:fs";
1126
1126
  import { execFileSync } from "node:child_process";
1127
1127
  import { basename as basename2, dirname as dirname3, isAbsolute as isAbsolute2, join as join3, relative as relative2, resolve as resolve3 } from "node:path";
1128
- import { existsSync as existsSync2, readFileSync as readFileSync4, readdirSync as readdirSync2, realpathSync } from "node:fs";
1128
+ import { existsSync as existsSync3, readFileSync as readFileSync4, readdirSync as readdirSync2, realpathSync as realpathSync2 } from "node:fs";
1129
1129
  import { dirname as dirname5, join as join6, resolve as resolve5, sep } from "node:path";
1130
1130
  import { dirname as dirname4, isAbsolute as isAbsolute3, join as join4, resolve as resolve4 } from "node:path";
1131
1131
  import { AsyncLocalStorage } from "node:async_hooks";
1132
1132
  import { execFileSync as execFileSync2 } from "node:child_process";
1133
- import { existsSync as existsSync3 } from "node:fs";
1133
+ import { existsSync as existsSync4 } from "node:fs";
1134
1134
  import { isAbsolute as isAbsolute4, resolve as resolve6 } from "node:path";
1135
1135
  import { execFileSync as execFileSync3 } from "node:child_process";
1136
- import { mkdirSync as mkdirSync5, readdirSync as readdirSync3, readFileSync as readFileSync5, realpathSync as realpathSync2, statSync as statSync4, writeFileSync as writeFileSync3 } from "node:fs";
1136
+ import { mkdirSync as mkdirSync5, readdirSync as readdirSync3, readFileSync as readFileSync5, realpathSync as realpathSync3, statSync as statSync4, writeFileSync as writeFileSync3 } from "node:fs";
1137
1137
  import { basename as basename3, dirname as dirname6, isAbsolute as isAbsolute5, join as join7, resolve as resolve7 } from "node:path";
1138
- import { existsSync as existsSync4, readdirSync as readdirSync4, readFileSync as readFileSync6 } from "node:fs";
1139
- import { existsSync as existsSync6, readdirSync as readdirSync8, readFileSync as readFileSync10 } from "node:fs";
1138
+ import { existsSync as existsSync5, readdirSync as readdirSync4, readFileSync as readFileSync6 } from "node:fs";
1139
+ import { existsSync as existsSync7, readdirSync as readdirSync8, readFileSync as readFileSync10 } from "node:fs";
1140
1140
  import { basename as basename4, isAbsolute as isAbsolute7, join as join12, relative as relative4, resolve as resolve10, sep as sep3 } from "node:path";
1141
- import { existsSync as existsSync7 } from "node:fs";
1141
+ import { existsSync as existsSync8 } from "node:fs";
1142
1142
  import { join as join13 } from "node:path";
1143
1143
  var SEVERITY_ORDER = ["critical", "high", "medium", "low", "nit"];
1144
1144
  function applyEnforcement(gate, opts) {
@@ -1681,10 +1681,8 @@ function composeDispatchGate(text, opts = {}) {
1681
1681
  const violations = [];
1682
1682
  const writable = opts.writable !== false;
1683
1683
  violations.push(...validateAssignmentFields(text, { writable }).violations);
1684
- const agent = (opts.agent ?? "").trim();
1685
- if (agent !== "") {
1686
- violations.push(...antiRecursionPrecheck(agent, parseAssignmentFields(text).executeAs ?? "").violations);
1687
- }
1684
+ const agent = opts.agent ?? "";
1685
+ violations.push(...antiRecursionPrecheck(agent, parseAssignmentFields(text).executeAs ?? "").violations);
1688
1686
  if (writable) {
1689
1687
  const forms = parseAssignmentBranchForms(text);
1690
1688
  const branch = forms.createForm?.name ?? forms.workingBranch ?? forms.directOn?.branch ?? process.env.MSTAR_WORKING_BRANCH;
@@ -1703,7 +1701,15 @@ function composeDispatchGate(text, opts = {}) {
1703
1701
  function antiRecursionPrecheck(subagentType, executeAs) {
1704
1702
  const binding = subagentType.trim().toLowerCase();
1705
1703
  const role = executeAs.trim().toLowerCase();
1706
- if (binding !== "" && binding === role) {
1704
+ if (binding === "") {
1705
+ return {
1706
+ ok: false,
1707
+ violations: [
1708
+ violation2("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")
1709
+ ]
1710
+ };
1711
+ }
1712
+ if (binding === role) {
1707
1713
  return {
1708
1714
  ok: false,
1709
1715
  violations: [
@@ -2041,7 +2047,7 @@ function validateStatusV2(docOrPath, opts = {}) {
2041
2047
  if (harnessDir !== undefined && Array.isArray(doc.workflows)) {
2042
2048
  let realHarnessDir = null;
2043
2049
  try {
2044
- realHarnessDir = realpathSync(harnessDir);
2050
+ realHarnessDir = realpathSync2(harnessDir);
2045
2051
  } catch {}
2046
2052
  for (const entry of doc.workflows) {
2047
2053
  if (!isPlainObject3(entry) || typeof entry.dir !== "string")
@@ -2051,7 +2057,7 @@ function validateStatusV2(docOrPath, opts = {}) {
2051
2057
  const label = typeof entry.id === "string" ? entry.id : relSnapshot;
2052
2058
  let physical;
2053
2059
  try {
2054
- physical = realpathSync(snapshotPath);
2060
+ physical = realpathSync2(snapshotPath);
2055
2061
  } catch {
2056
2062
  violations.push(violation4("high", "status.workflow.snapshot-missing", `workflows[] lists ${JSON.stringify(label)} but its snapshot does not exist at ${JSON.stringify(relSnapshot)} — the root holds active lifecycles only; unregister the id when its snapshot is removed`));
2057
2063
  continue;
@@ -2083,7 +2089,7 @@ function validateStatusV2(docOrPath, opts = {}) {
2083
2089
  var validateStatus = validateStatusV2;
2084
2090
  function resolveCompassEnforcement(harnessDir) {
2085
2091
  const iterationsDir = resolveIterationDir(harnessDir);
2086
- if (!existsSync2(iterationsDir))
2092
+ if (!existsSync3(iterationsDir))
2087
2093
  return { hard: false, source: "none" };
2088
2094
  let entries;
2089
2095
  try {
@@ -2095,7 +2101,7 @@ function resolveCompassEnforcement(harnessDir) {
2095
2101
  if (!entry.isDirectory())
2096
2102
  continue;
2097
2103
  const compassPath = join6(iterationsDir, entry.name, "delivery-compass.md");
2098
- if (!existsSync2(compassPath))
2104
+ if (!existsSync3(compassPath))
2099
2105
  continue;
2100
2106
  let content;
2101
2107
  try {
@@ -2182,7 +2188,7 @@ function l1PreDispatchCheck(input, opts = {}) {
2182
2188
  if (controlWorktreePath !== "" && leaseWorktreePath !== "" && resolve6(controlWorktreePath) === resolve6(leaseWorktreePath)) {
2183
2189
  violations.push(violation5("critical", "worktree.l1.lease-equals-control", `execution_lease.worktree_path "${leaseWorktreePath}" equals metadata.control_worktree_path — the feature worktree MUST differ from the control worktree (L1 isolation; product edits never land in the control checkout)`, "use a distinct feature worktree for the plan (git worktree add <path> <branch>) and update the lease"));
2184
2190
  }
2185
- if (leaseWorktreePath !== "" && !existsSync3(leaseWorktreePath)) {
2191
+ if (leaseWorktreePath !== "" && !existsSync4(leaseWorktreePath)) {
2186
2192
  violations.push(violation5("high", "worktree.l1.feature-missing", `feature worktree directory "${leaseWorktreePath}" does not exist for plan "${planId}"`, `create it before dispatch: git worktree add ${leaseWorktreePath} <working-branch>`));
2187
2193
  } else if (leaseWorktreePath !== "" && leaseWorkingBranch !== "") {
2188
2194
  const probe = probeBranch(leaseWorktreePath, opts);
@@ -2216,7 +2222,7 @@ function l2PreDispatchCheck(input, opts = {}) {
2216
2222
  return;
2217
2223
  }
2218
2224
  seenPaths.add(normalized);
2219
- if (!existsSync3(track.worktreePath)) {
2225
+ if (!existsSync4(track.worktreePath)) {
2220
2226
  violations.push(violation5("high", "worktree.l2.track-missing", `track worktree directory "${track.worktreePath}" does not exist`, `create it before dispatch: git worktree add ${track.worktreePath} ${track.workingBranch}`));
2221
2227
  return;
2222
2228
  }
@@ -2304,8 +2310,8 @@ function isLinkedWorktree(root) {
2304
2310
  if (gitDir.includes("/.git/worktrees/") || gitDir.includes("/worktrees/"))
2305
2311
  return true;
2306
2312
  try {
2307
- const gdParent = realpathSync2(dirname6(gitDir));
2308
- const cmAbs = realpathSync2(common);
2313
+ const gdParent = realpathSync3(dirname6(gitDir));
2314
+ const cmAbs = realpathSync3(common);
2309
2315
  return join7(gdParent, basename3(gitDir)) !== cmAbs && gitDir !== cmAbs;
2310
2316
  } catch {
2311
2317
  return false;
@@ -2323,10 +2329,10 @@ function sddWorkspace(planId, opts = {}) {
2323
2329
  if (!isDirectory2(controlRoot)) {
2324
2330
  throw new SddScriptError(`mstar sdd workspace: CONTROL_ROOT / MSTAR_CONTROL_ROOT is not a directory: ${controlRoot}`, 1);
2325
2331
  }
2326
- root = realpathSync2(controlRoot);
2332
+ root = realpathSync3(controlRoot);
2327
2333
  } else {
2328
2334
  const topLevel = gitOut(cwd, ["rev-parse", "--show-toplevel"]);
2329
- root = realpathSync2(topLevel ?? cwd);
2335
+ root = realpathSync3(topLevel ?? cwd);
2330
2336
  }
2331
2337
  if (!controlRoot && isLinkedWorktree(root)) {
2332
2338
  throw new SddScriptError(`mstar sdd workspace: linked worktree at ${root} has no {HARNESS_DIR}/status.json (default gitignore).
@@ -2360,7 +2366,7 @@ function sddWorkspace(planId, opts = {}) {
2360
2366
  mkdirSync5(sddDir, { recursive: true });
2361
2367
  writeFileSync3(join7(sddDir, ".gitignore"), `*
2362
2368
  `);
2363
- return realpathSync2(sddDir);
2369
+ return realpathSync3(sddDir);
2364
2370
  }
2365
2371
  function taskBrief(planFile, taskN, outFile, opts = {}) {
2366
2372
  if (!planFile || !Number.isInteger(taskN) || taskN < 1) {
@@ -3600,7 +3606,7 @@ function referenceExists(repoRoot, docText) {
3600
3606
  for (const { ref, isSymbol, module } of refs) {
3601
3607
  if (!isSymbol || module === undefined) {
3602
3608
  const candidate = ref.replace(LINE_SUFFIX_RE, "").replace(ANCHOR_RE, "");
3603
- if (existsSync6(resolve10(repoRoot, candidate))) {
3609
+ if (existsSync7(resolve10(repoRoot, candidate))) {
3604
3610
  checked++;
3605
3611
  } else {
3606
3612
  violations.push(violation10("medium", "compound.reference.missing-file", `referenced path \`${ref}\` does not exist under ${repoRoot} (compound-refresh Phase 2: referenced code still exists?)`, "update the doc to reference an existing path, or delete the stale reference"));
@@ -3648,7 +3654,7 @@ function normalizeIndexRef(cell) {
3648
3654
  function assertIndexRows(knowledgeDir) {
3649
3655
  const violations = [];
3650
3656
  const readmePath = join12(knowledgeDir, "README.md");
3651
- if (!existsSync6(readmePath)) {
3657
+ if (!existsSync7(readmePath)) {
3652
3658
  violations.push(violation10("medium", "compound.index.missing-readme", `missing ${readmePath} — the knowledge index is required (mstar-compound Phase 6: every doc gets a README.md row)`, "create knowledge/README.md with a Document / Source Plan / Description / Status table"));
3653
3659
  return { ok: false, violations };
3654
3660
  }
@@ -3835,7 +3841,7 @@ function validateRoleMapping(rolesDir, options = {}) {
3835
3841
  const violations = [];
3836
3842
  const referenceById = new Map(mapping.map((m) => [m.agentId, m.reference]));
3837
3843
  for (const { agentId, reference } of mapping) {
3838
- if (!existsSync7(join13(rolesDir, reference))) {
3844
+ if (!existsSync8(join13(rolesDir, reference))) {
3839
3845
  violations.push(violation12("medium", "roles.mapping.reference.missing", `role "${agentId}" maps to ${reference} which does not exist under ${rolesDir} (mstar-roles § Role Reference Mapping)`, `create ${join13(rolesDir, reference)} or fix the mapping row`));
3840
3846
  }
3841
3847
  }
@@ -4046,7 +4052,7 @@ class DshMstar extends Service {
4046
4052
 
4047
4053
  // src/gates/_shared.ts
4048
4054
  var import_schemastery = __toESM(require_lib2(), 1);
4049
- import { existsSync as existsSync5 } from "node:fs";
4055
+ import { existsSync as existsSync6 } from "node:fs";
4050
4056
  import { resolve as resolve8 } from "node:path";
4051
4057
  import { fileURLToPath as fileURLToPath2 } from "node:url";
4052
4058
  var STATUS_FILE = "status.json";
@@ -4101,7 +4107,7 @@ function resolvePackagedSkillsDir(fileUrl) {
4101
4107
  for (const rel of ["../harness-skills", "../../harness-skills"]) {
4102
4108
  try {
4103
4109
  const dir = fileURLToPath2(new URL(rel, fileUrl));
4104
- if (existsSync5(dir))
4110
+ if (existsSync6(dir))
4105
4111
  return dir;
4106
4112
  } catch {}
4107
4113
  }
@@ -4120,7 +4126,7 @@ function resolvePackagedAgentsDir(fileUrl) {
4120
4126
  for (const rel of ["../harness-agents", "../../harness-agents"]) {
4121
4127
  try {
4122
4128
  const dir = fileURLToPath2(new URL(rel, fileUrl));
4123
- if (existsSync5(dir))
4129
+ if (existsSync6(dir))
4124
4130
  return dir;
4125
4131
  } catch {}
4126
4132
  }
@@ -4188,20 +4194,20 @@ function skillLocalConfig(config) {
4188
4194
  }
4189
4195
 
4190
4196
  // src/gates/catalog.ts
4191
- import { existsSync as existsSync12, readFileSync as readFileSync9, readdirSync as readdirSync7 } from "node:fs";
4197
+ import { existsSync as existsSync13, readFileSync as readFileSync9, readdirSync as readdirSync7 } from "node:fs";
4192
4198
  import { join as join11 } from "node:path";
4193
4199
  import { createUserMessage } from "@deepseek-ai/dsh-llm";
4194
4200
 
4195
4201
  // src/gates/agent-flow.ts
4196
- import { appendFileSync, closeSync, existsSync as existsSync11, mkdirSync as mkdirSync3, openSync, readFileSync as readFileSync7, readSync, renameSync as renameSync2, rmdirSync, statSync as statSync5, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "node:fs";
4202
+ import { appendFileSync, closeSync, existsSync as existsSync12, mkdirSync as mkdirSync3, openSync, readFileSync as readFileSync7, readSync, renameSync as renameSync2, rmdirSync, statSync as statSync5, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "node:fs";
4197
4203
  import { join as join10 } from "node:path";
4198
4204
 
4199
4205
  // src/gates/dispatch.ts
4200
- import { existsSync as existsSync10 } from "node:fs";
4206
+ import { existsSync as existsSync11 } from "node:fs";
4201
4207
  import { basename as basename5, join as join8, resolve as resolve11 } from "node:path";
4202
4208
 
4203
4209
  // src/gates/workflow-selection.ts
4204
- import { existsSync as existsSync8, readdirSync as readdirSync5, statSync as statSync3 } from "node:fs";
4210
+ import { existsSync as existsSync9, readdirSync as readdirSync5, statSync as statSync3 } from "node:fs";
4205
4211
  import { join as join5, relative as relative3 } from "node:path";
4206
4212
  var TERMINAL_STATUS_CACHE_MAX = 64;
4207
4213
  var terminalStatusCache = new Map;
@@ -4235,7 +4241,7 @@ function terminalStatusOf(snapshotPath) {
4235
4241
  }
4236
4242
  function resolveActiveWorkflow(harnessDir) {
4237
4243
  const statusPath = join5(harnessDir, STATUS_FILE);
4238
- if (!existsSync8(statusPath)) {
4244
+ if (!existsSync9(statusPath)) {
4239
4245
  return {
4240
4246
  kind: "error",
4241
4247
  code: "status.missing",
@@ -4293,7 +4299,7 @@ function resolveReadWorkflow(harnessDir) {
4293
4299
  if (active.kind === "error" && active.code !== "workflow.selection.no-active")
4294
4300
  return active;
4295
4301
  const workflowsDir = resolveWorkflowDir(harnessDir, { harnessDir });
4296
- if (!existsSync8(workflowsDir)) {
4302
+ if (!existsSync9(workflowsDir)) {
4297
4303
  return {
4298
4304
  kind: "error",
4299
4305
  code: "workflow.selection.no-snapshot",
@@ -4315,7 +4321,7 @@ function resolveReadWorkflow(harnessDir) {
4315
4321
  if (!entry.isDirectory())
4316
4322
  continue;
4317
4323
  const snapshotPath = join5(workflowsDir, entry.name, WORKFLOW_SNAPSHOT_FILE);
4318
- if (!existsSync8(snapshotPath))
4324
+ if (!existsSync9(snapshotPath))
4319
4325
  continue;
4320
4326
  const verdict = terminalStatusOf(snapshotPath);
4321
4327
  if (verdict === undefined || !verdict.terminal)
@@ -4518,7 +4524,7 @@ function leaseGateViolations(harnessDir, exec, writable, prompt) {
4518
4524
  return [leaseViolation("lease.dispatch.unverifiable", `${join8(harnessDir, STATUS_FILE)}: ${selection.kind === "error" ? selection.message : "no active workflow"} — the plan's execution_lease state is unverifiable; STOP before writable dispatch`, "create a valid v2 status.json registering an active workflow (first implement dispatch requires a plan row + lease)")];
4519
4525
  }
4520
4526
  const snapshotPath = join8(harnessDir, selection.dir, WORKFLOW_SNAPSHOT_FILE);
4521
- if (!existsSync10(snapshotPath)) {
4527
+ if (!existsSync11(snapshotPath)) {
4522
4528
  if (!sdd)
4523
4529
  return [];
4524
4530
  return [leaseViolation("lease.dispatch.unverifiable", `${snapshotPath} is missing — the plan's execution_lease state is unverifiable; STOP before writable dispatch`, "create a valid workflow snapshot registering the plan row (first implement dispatch requires a plan row)")];
@@ -4592,7 +4598,7 @@ function worktreeL2Violations(header) {
4592
4598
  return violations;
4593
4599
  }
4594
4600
  function activeSnapshotRows(harnessDir) {
4595
- if (!existsSync10(join8(harnessDir, STATUS_FILE)))
4601
+ if (!existsSync11(join8(harnessDir, STATUS_FILE)))
4596
4602
  return { rows: null, controlWorktreePath: undefined, unreadable: false };
4597
4603
  const selection = resolveActiveWorkflow(harnessDir);
4598
4604
  if (selection.kind !== "active") {
@@ -4603,7 +4609,7 @@ function activeSnapshotRows(harnessDir) {
4603
4609
  };
4604
4610
  }
4605
4611
  const snapshotPath = join8(harnessDir, selection.dir, WORKFLOW_SNAPSHOT_FILE);
4606
- if (!existsSync10(snapshotPath))
4612
+ if (!existsSync11(snapshotPath))
4607
4613
  return { rows: null, controlWorktreePath: undefined, unreadable: true };
4608
4614
  let doc;
4609
4615
  try {
@@ -5333,7 +5339,7 @@ function ledgerContent(file, n) {
5333
5339
  }
5334
5340
  function readAgentFlow(workflowDir, limit) {
5335
5341
  const file = join10(workflowDir, AGENT_FLOW_FILE);
5336
- if (!existsSync11(file)) {
5342
+ if (!existsSync12(file)) {
5337
5343
  return { events: [], summary: [] };
5338
5344
  }
5339
5345
  const n = limit === undefined ? AGENT_FLOW_DEFAULT_LIMIT : Math.max(0, Math.floor(limit));
@@ -5588,7 +5594,7 @@ function harnessStateSource(harnessDir) {
5588
5594
  if (harnessDir === null)
5589
5595
  return null;
5590
5596
  const statusPath = join11(harnessDir, STATUS_FILE);
5591
- if (!existsSync12(statusPath))
5597
+ if (!existsSync13(statusPath))
5592
5598
  return null;
5593
5599
  try {
5594
5600
  const selection = resolveReadWorkflow(harnessDir);
@@ -5609,7 +5615,7 @@ function harnessStateSource(harnessDir) {
5609
5615
  }
5610
5616
  const snapshotPath = join11(harnessDir, selection.dir, WORKFLOW_SNAPSHOT_FILE);
5611
5617
  let snapshot;
5612
- if (!existsSync12(snapshotPath)) {
5618
+ if (!existsSync13(snapshotPath)) {
5613
5619
  return selectionErrorState({
5614
5620
  kind: "error",
5615
5621
  code: "workflow.selection.snapshot-unreadable",
@@ -5701,7 +5707,7 @@ function selectionErrorState(selection, rollup, harnessDir, compass) {
5701
5707
  function projectRollupSource(harnessDir, residuals) {
5702
5708
  const milestones = [];
5703
5709
  const projectsDir = resolveProjectDir(harnessDir, { harnessDir });
5704
- if (existsSync12(projectsDir)) {
5710
+ if (existsSync13(projectsDir)) {
5705
5711
  let entries;
5706
5712
  try {
5707
5713
  entries = readdirSync7(projectsDir, { withFileTypes: true });
@@ -5712,7 +5718,7 @@ function projectRollupSource(harnessDir, residuals) {
5712
5718
  if (!entry.isDirectory())
5713
5719
  continue;
5714
5720
  const roadmapPath = join11(projectsDir, entry.name, PROJECT_ROADMAP_FILE);
5715
- if (!existsSync12(roadmapPath))
5721
+ if (!existsSync13(roadmapPath))
5716
5722
  continue;
5717
5723
  try {
5718
5724
  const doc = parseCompassFrontmatterText(readFileSync9(roadmapPath, "utf8"), roadmapPath);
@@ -5777,7 +5783,7 @@ function residualRollup(harnessDir) {
5777
5783
  }
5778
5784
  function projectRegisters(harnessDir) {
5779
5785
  const projectsDir = resolveProjectDir(harnessDir, { harnessDir });
5780
- if (!existsSync12(projectsDir))
5786
+ if (!existsSync13(projectsDir))
5781
5787
  return [];
5782
5788
  let entries;
5783
5789
  try {
@@ -5790,7 +5796,7 @@ function projectRegisters(harnessDir) {
5790
5796
  if (!entry.isDirectory())
5791
5797
  continue;
5792
5798
  const registerPath = join11(projectsDir, entry.name, PROJECT_REGISTER_FILE);
5793
- if (!existsSync12(registerPath))
5799
+ if (!existsSync13(registerPath))
5794
5800
  continue;
5795
5801
  try {
5796
5802
  registers.push({ projectId: entry.name, doc: readJson(registerPath) });
@@ -5802,7 +5808,7 @@ function projectRegisters(harnessDir) {
5802
5808
  }
5803
5809
  function knowledgeDigest(harnessDir) {
5804
5810
  const indexPath = join11(harnessDir, "knowledge", "README.md");
5805
- if (!existsSync12(indexPath))
5811
+ if (!existsSync13(indexPath))
5806
5812
  return null;
5807
5813
  try {
5808
5814
  const categories = new Set;
@@ -5847,7 +5853,7 @@ function compassDirection(compassPath) {
5847
5853
  }
5848
5854
  function steeringCompassPath(harnessDir) {
5849
5855
  const iterationsDir = resolveIterationDir(harnessDir);
5850
- if (!existsSync12(iterationsDir))
5856
+ if (!existsSync13(iterationsDir))
5851
5857
  return;
5852
5858
  let entries;
5853
5859
  try {
@@ -5859,7 +5865,7 @@ function steeringCompassPath(harnessDir) {
5859
5865
  if (!entry.isDirectory())
5860
5866
  continue;
5861
5867
  const compassPath = join11(iterationsDir, entry.name, "delivery-compass.md");
5862
- if (!existsSync12(compassPath))
5868
+ if (!existsSync13(compassPath))
5863
5869
  continue;
5864
5870
  let content;
5865
5871
  try {
@@ -5878,7 +5884,7 @@ function iterationGateSource(harnessDir) {
5878
5884
  if (harnessDir === null)
5879
5885
  return;
5880
5886
  const statusPath = join11(harnessDir, STATUS_FILE);
5881
- if (!existsSync12(statusPath))
5887
+ if (!existsSync13(statusPath))
5882
5888
  return;
5883
5889
  const compass = steeringCompassPath(harnessDir);
5884
5890
  if (compass === undefined)
@@ -5940,7 +5946,7 @@ function agentDigestKey(agent, cwd) {
5940
5946
  }
5941
5947
 
5942
5948
  // src/gates/status.ts
5943
- import { existsSync as existsSync13, readdirSync as readdirSync9 } from "node:fs";
5949
+ import { existsSync as existsSync14, readdirSync as readdirSync9 } from "node:fs";
5944
5950
  import { basename as basename7, join as join14, relative as relative5, resolve as resolve13 } from "node:path";
5945
5951
  var LOGGER_NAME = "mstar/status-gate";
5946
5952
  function harnessDocKindOfTarget(harnessDir, targetPath) {
@@ -6023,7 +6029,7 @@ function projectRegisterDocs(harnessDir) {
6023
6029
  if (!entry.isDirectory())
6024
6030
  continue;
6025
6031
  const registerPath = join14(projectsDir, entry.name, PROJECT_REGISTER_FILE);
6026
- if (!existsSync13(registerPath))
6032
+ if (!existsSync14(registerPath))
6027
6033
  continue;
6028
6034
  try {
6029
6035
  registers.push({ projectId: entry.name, doc: readJson(registerPath) });
@@ -6092,7 +6098,7 @@ async function editIntentListener(ctx, resolver, config, adapter, target, actor,
6092
6098
  }
6093
6099
 
6094
6100
  // src/gates/skill-lint.ts
6095
- import { existsSync as existsSync14, readFileSync as readFileSync11 } from "node:fs";
6101
+ import { existsSync as existsSync15, readFileSync as readFileSync11 } from "node:fs";
6096
6102
  import { basename as basename8, dirname as dirname7, resolve as resolve14, sep as sep2 } from "node:path";
6097
6103
  var SKILL_LINT_LOGGER = "mstar/skill-lint";
6098
6104
 
@@ -6173,7 +6179,7 @@ function gateSkillIntent(ctx, harnessDir, config, target) {
6173
6179
  if (!isSkillTarget(roots, target))
6174
6180
  return;
6175
6181
  const skillPath = resolve14(target.displayPath);
6176
- if (!existsSync14(skillPath))
6182
+ if (!existsSync15(skillPath))
6177
6183
  return;
6178
6184
  let doc;
6179
6185
  try {
@@ -6224,7 +6230,7 @@ async function skillWriteIntentListener(ctx, resolver, config, target, actor, ne
6224
6230
  }
6225
6231
 
6226
6232
  // src/gates/seams.ts
6227
- import { existsSync as existsSync15, readFileSync as readFileSync13, readdirSync as readdirSync11 } from "node:fs";
6233
+ import { existsSync as existsSync16, readFileSync as readFileSync13, readdirSync as readdirSync11 } from "node:fs";
6228
6234
  import { basename as basename9, dirname as dirname9, join as join16, resolve as resolve15, sep as sep4 } from "node:path";
6229
6235
  var SEAM_LOGGERS = {
6230
6236
  "design-md": "mstar/design-md-lint",
@@ -6300,7 +6306,7 @@ function validateDesignDoc(doc, path) {
6300
6306
  const violations = [...validateDesignTokenFrontmatter(doc).violations];
6301
6307
  const isDark = basename9(path) === "DESIGN.dark.md";
6302
6308
  const sibling = join16(dirname9(path), isDark ? "DESIGN.md" : "DESIGN.dark.md");
6303
- if (existsSync15(sibling)) {
6309
+ if (existsSync16(sibling)) {
6304
6310
  const light = isDark ? readFileSync13(sibling, "utf8") : doc;
6305
6311
  const dark = isDark ? doc : readFileSync13(sibling, "utf8");
6306
6312
  violations.push(...assertLightDarkParity(light, dark).violations);
@@ -6327,7 +6333,7 @@ function validateRolesState(rolesDir, skillsRoot = dirname9(rolesDir)) {
6327
6333
  if (!entry.isDirectory() || !entry.name.startsWith("mstar-"))
6328
6334
  continue;
6329
6335
  const skillFile = join16(skillsRoot, entry.name, "SKILL.md");
6330
- if (!existsSync15(skillFile))
6336
+ if (!existsSync16(skillFile))
6331
6337
  continue;
6332
6338
  try {
6333
6339
  skillTexts[entry.name] = readFileSync13(skillFile, "utf8");
@@ -6361,7 +6367,7 @@ function gateSeamIntent(ctx, harnessDir, config, seam, target) {
6361
6367
  if (!isSeamTarget(seam, harnessDir, target))
6362
6368
  return;
6363
6369
  const path = resolve15(target.displayPath);
6364
- if (!existsSync15(path))
6370
+ if (!existsSync16(path))
6365
6371
  return;
6366
6372
  let doc;
6367
6373
  try {
@@ -6431,7 +6437,7 @@ function lintRolesWrite(doc, options) {
6431
6437
  }
6432
6438
 
6433
6439
  // src/gates/tools.ts
6434
- import { existsSync as existsSync16, readFileSync as readFileSync14 } from "node:fs";
6440
+ import { existsSync as existsSync17, readFileSync as readFileSync14 } from "node:fs";
6435
6441
  import { join as join17, resolve as resolve16 } from "node:path";
6436
6442
  import { defineTool } from "@deepseek-ai/dsh-tools";
6437
6443
  var ITERATION_VIOLATION_SCHEMA = {
@@ -6597,9 +6603,9 @@ function registerSddIterationTools(ctx, resolver) {
6597
6603
  presentResult: (_args, _result) => ({ card: "generic", title: "Iteration gate evaluation" }),
6598
6604
  isConcurrencySafe: () => false,
6599
6605
  async execute(args) {
6600
- if (!existsSync16(args.snapshot_path))
6606
+ if (!existsSync17(args.snapshot_path))
6601
6607
  throw new Error(`workflow snapshot not found: ${args.snapshot_path}`);
6602
- if (!existsSync16(args.compass_path))
6608
+ if (!existsSync17(args.compass_path))
6603
6609
  throw new Error(`compass file not found: ${args.compass_path}`);
6604
6610
  const snapshotDoc = readJson(args.snapshot_path);
6605
6611
  const compassDoc = parseCompassFrontmatter(args.compass_path);
@@ -6654,7 +6660,7 @@ function registerSeamTools(ctx, resolver) {
6654
6660
  async execute(args) {
6655
6661
  const abs = resolve16(args.dir);
6656
6662
  const lightPath = join17(abs, "DESIGN.md");
6657
- if (!existsSync16(lightPath))
6663
+ if (!existsSync17(lightPath))
6658
6664
  throw new Error(`design file not found: ${lightPath}`);
6659
6665
  const light = readFileSync14(lightPath, "utf8");
6660
6666
  const result = validateDesignDoc(light, lightPath);
@@ -6707,7 +6713,7 @@ function registerSeamTools(ctx, resolver) {
6707
6713
  isConcurrencySafe: () => false,
6708
6714
  async execute(args) {
6709
6715
  const abs = resolve16(args.plan_path);
6710
- if (!existsSync16(abs))
6716
+ if (!existsSync17(abs))
6711
6717
  throw new Error(`plan file not found: ${abs}`);
6712
6718
  const text = readFileSync14(abs, "utf8");
6713
6719
  const result = validateAuditDoc(text, abs);
@@ -6747,7 +6753,7 @@ function registerSeamTools(ctx, resolver) {
6747
6753
  isConcurrencySafe: () => false,
6748
6754
  async execute(args, exec) {
6749
6755
  const abs = resolve16(args.doc_path);
6750
- if (!existsSync16(abs))
6756
+ if (!existsSync17(abs))
6751
6757
  throw new Error(`knowledge doc not found: ${abs}`);
6752
6758
  const text = readFileSync14(abs, "utf8");
6753
6759
  const base = validateCompoundDoc(text, abs, args.repo_root !== undefined ? null : resolver.forAgent(exec.agent));
@@ -6792,7 +6798,7 @@ function registerSeamTools(ctx, resolver) {
6792
6798
  isConcurrencySafe: () => false,
6793
6799
  async execute(args) {
6794
6800
  const rolesDir = resolve16(args.roles_dir);
6795
- if (!existsSync16(join17(rolesDir, "SKILL.md")))
6801
+ if (!existsSync17(join17(rolesDir, "SKILL.md")))
6796
6802
  throw new Error(`roles dir not found: ${rolesDir}`);
6797
6803
  const result = validateRolesState(rolesDir, args.skills_root !== undefined ? resolve16(args.skills_root) : undefined);
6798
6804
  return { ok: result.ok, violations: result.violations.map(iterationViolationView) };
@@ -6802,7 +6808,7 @@ function registerSeamTools(ctx, resolver) {
6802
6808
  }
6803
6809
 
6804
6810
  // src/gates/adapter.ts
6805
- import { existsSync as existsSync17 } from "node:fs";
6811
+ import { existsSync as existsSync18 } from "node:fs";
6806
6812
  import { join as join18 } from "node:path";
6807
6813
  import { Service as Service2 } from "@deepseek-ai/cordis";
6808
6814
  var HOST_LOGGER = "mstar/host-adapter";
@@ -6839,7 +6845,7 @@ class DshHostAdapter extends Service2 {
6839
6845
  this.logSink(level, msg);
6840
6846
  }
6841
6847
  statusGate(path, kind, harnessDir) {
6842
- if (!existsSync17(path))
6848
+ if (!existsSync18(path))
6843
6849
  return { ok: true, violations: [] };
6844
6850
  return validateStatusDoc(path, kind, harnessDir);
6845
6851
  }
@@ -7451,7 +7457,7 @@ function registerWorkflowLedger(ctx, resolver, workflowAskCache) {
7451
7457
  }
7452
7458
 
7453
7459
  // src/gates/goal-bridge.ts
7454
- import { existsSync as existsSync18, readFileSync as readFileSync17, readdirSync as readdirSync13 } from "node:fs";
7460
+ import { existsSync as existsSync19, readFileSync as readFileSync17, readdirSync as readdirSync13 } from "node:fs";
7455
7461
  import { join as join21 } from "node:path";
7456
7462
  var GOAL_BRIDGE_LOGGER = "mstar/goal-bridge";
7457
7463
  var DEFAULT_MAX_GOAL_ROUNDS = 256;
@@ -7480,7 +7486,7 @@ function projectRegisterPointer(harnessDir) {
7480
7486
  if (!entry.isDirectory())
7481
7487
  continue;
7482
7488
  const registerPath = join21(projectsDir, entry.name, PROJECT_REGISTER_FILE);
7483
- if (existsSync18(registerPath))
7489
+ if (existsSync19(registerPath))
7484
7490
  return registerPath;
7485
7491
  }
7486
7492
  } catch {}
@@ -7495,7 +7501,7 @@ function iterationGoalObjective(iterationId) {
7495
7501
  }
7496
7502
  function steeringCompass(harnessDir) {
7497
7503
  const iterationsDir = resolveIterationDir(harnessDir);
7498
- if (!existsSync18(iterationsDir))
7504
+ if (!existsSync19(iterationsDir))
7499
7505
  return;
7500
7506
  let entries;
7501
7507
  try {
@@ -7507,7 +7513,7 @@ function steeringCompass(harnessDir) {
7507
7513
  if (!entry.isDirectory())
7508
7514
  continue;
7509
7515
  const compassPath = join21(iterationsDir, entry.name, "delivery-compass.md");
7510
- if (!existsSync18(compassPath))
7516
+ if (!existsSync19(compassPath))
7511
7517
  continue;
7512
7518
  let content;
7513
7519
  try {
@@ -7687,7 +7693,7 @@ function registerGoalBridge(ctx, resolver, config) {
7687
7693
  }
7688
7694
 
7689
7695
  // src/gates/plan-mode-bridge.ts
7690
- import { existsSync as existsSync19 } from "node:fs";
7696
+ import { existsSync as existsSync20 } from "node:fs";
7691
7697
  import { join as join22 } from "node:path";
7692
7698
  var PLAN_MODE_BRIDGE_LOGGER = "mstar/plan-mode-bridge";
7693
7699
  var PLAN_STATUS_TODO = "Todo";
@@ -7705,7 +7711,7 @@ function errorMessage5(error) {
7705
7711
  }
7706
7712
  function hasPrepareWindow(harnessDir) {
7707
7713
  const statusPath = join22(harnessDir, STATUS_FILE);
7708
- if (!existsSync19(statusPath))
7714
+ if (!existsSync20(statusPath))
7709
7715
  return false;
7710
7716
  const selection = resolveReadWorkflow(harnessDir);
7711
7717
  if (selection.kind === "error")
@@ -8164,7 +8170,7 @@ var inject = ["loader"];
8164
8170
  function packagedCommandsDir() {
8165
8171
  try {
8166
8172
  const dir = fileURLToPath3(new URL("../harness-commands", import.meta.url));
8167
- return existsSync20(dir) ? dir : undefined;
8173
+ return existsSync21(dir) ? dir : undefined;
8168
8174
  } catch {
8169
8175
  return;
8170
8176
  }
@@ -8364,7 +8370,7 @@ function apply(ctx, config) {
8364
8370
  }
8365
8371
  const effectiveHard = config.enforcement === "hard" || bootHarnessDir !== null && resolveRepoEnforcement(bootHarnessDir).hard;
8366
8372
  if (effectiveHard && (config.dispatchBinding ?? "").trim() === "") {
8367
- ctx.logger(DISPATCH_LOGGER).warn("Enforcement: hard is active but dispatchBinding is unset — the anti-recursion precheck is skipped (an Assignment whose Execute as equals the dispatching agent cannot be detected)");
8373
+ ctx.logger(DISPATCH_LOGGER).warn("Enforcement: hard is active but dispatchBinding is unset — the empty host binding fails closed: every Assignment-shaped dispatch emits dispatch.anti-recursion.empty-binding (critical) until dispatchBinding is set");
8368
8374
  }
8369
8375
  if (effectiveHard && config.dispatchTools === undefined) {
8370
8376
  ctx.logger(DISPATCH_LOGGER).warn('Enforcement: hard is active but dispatchTools is unset — the dispatch gate matches the default tool names "subagent" and "subagent_fork"; a deployment renaming either dsh subagent tool (toolName) without declaring dispatchTools silently disables the gate');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mstar-harness/dsh",
3
- "version": "3.1.1",
3
+ "version": "3.1.2",
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",
@@ -64,23 +64,23 @@
64
64
  "bun": ">=1.2.17"
65
65
  },
66
66
  "dependencies": {
67
- "dsh-llm-fallbacks": "^0.2.0"
67
+ "dsh-llm-fallbacks": "^0.3.0"
68
68
  },
69
69
  "peerDependencies": {
70
70
  "@deepseek-ai/cordis": "^4.0.1",
71
- "@deepseek-ai/dsh-agent": "^0.1.0-rc.8",
72
- "@deepseek-ai/dsh-client-locale": "^0.1.0-rc.8",
73
- "@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.8",
74
- "@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.8",
75
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.8",
76
- "@deepseek-ai/dsh-commands": "^0.1.0-rc.8",
77
- "@deepseek-ai/dsh-fs": "^0.1.0-rc.8",
78
- "@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
79
- "@deepseek-ai/dsh-jobs": "^0.1.0-rc.8",
80
- "@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
81
- "@deepseek-ai/dsh-skill": "^0.1.0-rc.8",
82
- "@deepseek-ai/dsh-skill-filesystem": "^0.1.0-rc.8",
83
- "@deepseek-ai/dsh-tools": "^0.1.0-rc.8"
71
+ "@deepseek-ai/dsh-agent": "^0.1.1-rc.1",
72
+ "@deepseek-ai/dsh-client-locale": "^0.1.1-rc.1",
73
+ "@deepseek-ai/dsh-client-runtime": "^0.1.1-rc.1",
74
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.1.1-rc.1",
75
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.1-rc.1",
76
+ "@deepseek-ai/dsh-commands": "^0.1.1-rc.1",
77
+ "@deepseek-ai/dsh-fs": "^0.1.1-rc.1",
78
+ "@deepseek-ai/dsh-invariants": "^0.1.1-rc.1",
79
+ "@deepseek-ai/dsh-jobs": "^0.1.1-rc.1",
80
+ "@deepseek-ai/dsh-llm": "^0.1.1-rc.1",
81
+ "@deepseek-ai/dsh-skill": "^0.1.1-rc.1",
82
+ "@deepseek-ai/dsh-skill-filesystem": "^0.1.1-rc.1",
83
+ "@deepseek-ai/dsh-tools": "^0.1.1-rc.1"
84
84
  },
85
85
  "publishConfig": {
86
86
  "access": "public"