@mstar-harness/dsh 3.1.0 → 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 +2 -2
- package/README.zh.md +2 -2
- package/dist/gates/_shared.d.ts +4 -2
- package/dist/gates/system-prompt.d.ts +7 -4
- package/dist/index.js +95 -92
- package/dist/types.d.ts +14 -0
- package/package.json +15 -15
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
English | [中文](README.zh.md)
|
|
4
4
|
|
|
5
|
-

|
|
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** —
|
|
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
|
-

|
|
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`
|
|
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 日志跳过。
|
package/dist/gates/_shared.d.ts
CHANGED
|
@@ -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
|
|
39
|
-
*
|
|
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
|
/**
|
|
@@ -37,10 +37,13 @@
|
|
|
37
37
|
* re-registration, in zero-config and explicit-config deployments alike.
|
|
38
38
|
* - The context provider reuses the catalog's unified machine-summary
|
|
39
39
|
* source (`buildCatalogSources` — the SAME builder the engine-status
|
|
40
|
-
* pre-step catalog row uses) and projects
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
40
|
+
* pre-step catalog row uses) and projects the SLIM digest (plan
|
|
41
|
+
* `20260820-dsh-engine-status-slim` Task 2): the version watermark
|
|
42
|
+
* ALWAYS, plus ONE `workflow … | plans: …` line only when the active set
|
|
43
|
+
* selects a lifecycle (`state.selection.kind === 'active'`). Harness dir
|
|
44
|
+
* and enforcement live in `mstar:harness-rules`; residuals / leases /
|
|
45
|
+
* direction / iteration-gate detail stay exclusive to the pre-step row.
|
|
46
|
+
* v3 (plan `20260819-workflow-dsh-viz` Task 3): the
|
|
44
47
|
* digest reads ONLY the catalog row (`state` — itself aggregated from the
|
|
45
48
|
* SELECTED workflow snapshot + project registers) — no direct
|
|
46
49
|
* status.json / snapshot file reads to change. The build is TTL-memoized
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1139
|
-
import { existsSync as
|
|
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
|
|
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 =
|
|
1685
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
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 (!
|
|
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 (!
|
|
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 !== "" && !
|
|
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 (!
|
|
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 =
|
|
2308
|
-
const cmAbs =
|
|
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 =
|
|
2332
|
+
root = realpathSync3(controlRoot);
|
|
2327
2333
|
} else {
|
|
2328
2334
|
const topLevel = gitOut(cwd, ["rev-parse", "--show-toplevel"]);
|
|
2329
|
-
root =
|
|
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
|
|
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 (
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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 (
|
|
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 (
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
5618
|
+
if (!existsSync13(snapshotPath)) {
|
|
5613
5619
|
return selectionErrorState({
|
|
5614
5620
|
kind: "error",
|
|
5615
5621
|
code: "workflow.selection.snapshot-unreadable",
|
|
@@ -5656,6 +5662,8 @@ function harnessStateSource(harnessDir) {
|
|
|
5656
5662
|
const executionPolicy = asRecord(snapshot.execution_policy);
|
|
5657
5663
|
return {
|
|
5658
5664
|
selection,
|
|
5665
|
+
workflowType: str(snapshot.type),
|
|
5666
|
+
workflowStatus: str(snapshot.status),
|
|
5659
5667
|
plans,
|
|
5660
5668
|
residuals,
|
|
5661
5669
|
residualFindings,
|
|
@@ -5678,6 +5686,8 @@ function harnessStateSource(harnessDir) {
|
|
|
5678
5686
|
function selectionErrorState(selection, rollup, harnessDir, compass) {
|
|
5679
5687
|
return {
|
|
5680
5688
|
selection,
|
|
5689
|
+
workflowType: null,
|
|
5690
|
+
workflowStatus: null,
|
|
5681
5691
|
plans: [],
|
|
5682
5692
|
residuals: [],
|
|
5683
5693
|
residualFindings: null,
|
|
@@ -5697,7 +5707,7 @@ function selectionErrorState(selection, rollup, harnessDir, compass) {
|
|
|
5697
5707
|
function projectRollupSource(harnessDir, residuals) {
|
|
5698
5708
|
const milestones = [];
|
|
5699
5709
|
const projectsDir = resolveProjectDir(harnessDir, { harnessDir });
|
|
5700
|
-
if (
|
|
5710
|
+
if (existsSync13(projectsDir)) {
|
|
5701
5711
|
let entries;
|
|
5702
5712
|
try {
|
|
5703
5713
|
entries = readdirSync7(projectsDir, { withFileTypes: true });
|
|
@@ -5708,7 +5718,7 @@ function projectRollupSource(harnessDir, residuals) {
|
|
|
5708
5718
|
if (!entry.isDirectory())
|
|
5709
5719
|
continue;
|
|
5710
5720
|
const roadmapPath = join11(projectsDir, entry.name, PROJECT_ROADMAP_FILE);
|
|
5711
|
-
if (!
|
|
5721
|
+
if (!existsSync13(roadmapPath))
|
|
5712
5722
|
continue;
|
|
5713
5723
|
try {
|
|
5714
5724
|
const doc = parseCompassFrontmatterText(readFileSync9(roadmapPath, "utf8"), roadmapPath);
|
|
@@ -5773,7 +5783,7 @@ function residualRollup(harnessDir) {
|
|
|
5773
5783
|
}
|
|
5774
5784
|
function projectRegisters(harnessDir) {
|
|
5775
5785
|
const projectsDir = resolveProjectDir(harnessDir, { harnessDir });
|
|
5776
|
-
if (!
|
|
5786
|
+
if (!existsSync13(projectsDir))
|
|
5777
5787
|
return [];
|
|
5778
5788
|
let entries;
|
|
5779
5789
|
try {
|
|
@@ -5786,7 +5796,7 @@ function projectRegisters(harnessDir) {
|
|
|
5786
5796
|
if (!entry.isDirectory())
|
|
5787
5797
|
continue;
|
|
5788
5798
|
const registerPath = join11(projectsDir, entry.name, PROJECT_REGISTER_FILE);
|
|
5789
|
-
if (!
|
|
5799
|
+
if (!existsSync13(registerPath))
|
|
5790
5800
|
continue;
|
|
5791
5801
|
try {
|
|
5792
5802
|
registers.push({ projectId: entry.name, doc: readJson(registerPath) });
|
|
@@ -5798,7 +5808,7 @@ function projectRegisters(harnessDir) {
|
|
|
5798
5808
|
}
|
|
5799
5809
|
function knowledgeDigest(harnessDir) {
|
|
5800
5810
|
const indexPath = join11(harnessDir, "knowledge", "README.md");
|
|
5801
|
-
if (!
|
|
5811
|
+
if (!existsSync13(indexPath))
|
|
5802
5812
|
return null;
|
|
5803
5813
|
try {
|
|
5804
5814
|
const categories = new Set;
|
|
@@ -5843,7 +5853,7 @@ function compassDirection(compassPath) {
|
|
|
5843
5853
|
}
|
|
5844
5854
|
function steeringCompassPath(harnessDir) {
|
|
5845
5855
|
const iterationsDir = resolveIterationDir(harnessDir);
|
|
5846
|
-
if (!
|
|
5856
|
+
if (!existsSync13(iterationsDir))
|
|
5847
5857
|
return;
|
|
5848
5858
|
let entries;
|
|
5849
5859
|
try {
|
|
@@ -5855,7 +5865,7 @@ function steeringCompassPath(harnessDir) {
|
|
|
5855
5865
|
if (!entry.isDirectory())
|
|
5856
5866
|
continue;
|
|
5857
5867
|
const compassPath = join11(iterationsDir, entry.name, "delivery-compass.md");
|
|
5858
|
-
if (!
|
|
5868
|
+
if (!existsSync13(compassPath))
|
|
5859
5869
|
continue;
|
|
5860
5870
|
let content;
|
|
5861
5871
|
try {
|
|
@@ -5874,7 +5884,7 @@ function iterationGateSource(harnessDir) {
|
|
|
5874
5884
|
if (harnessDir === null)
|
|
5875
5885
|
return;
|
|
5876
5886
|
const statusPath = join11(harnessDir, STATUS_FILE);
|
|
5877
|
-
if (!
|
|
5887
|
+
if (!existsSync13(statusPath))
|
|
5878
5888
|
return;
|
|
5879
5889
|
const compass = steeringCompassPath(harnessDir);
|
|
5880
5890
|
if (compass === undefined)
|
|
@@ -5936,7 +5946,7 @@ function agentDigestKey(agent, cwd) {
|
|
|
5936
5946
|
}
|
|
5937
5947
|
|
|
5938
5948
|
// src/gates/status.ts
|
|
5939
|
-
import { existsSync as
|
|
5949
|
+
import { existsSync as existsSync14, readdirSync as readdirSync9 } from "node:fs";
|
|
5940
5950
|
import { basename as basename7, join as join14, relative as relative5, resolve as resolve13 } from "node:path";
|
|
5941
5951
|
var LOGGER_NAME = "mstar/status-gate";
|
|
5942
5952
|
function harnessDocKindOfTarget(harnessDir, targetPath) {
|
|
@@ -6019,7 +6029,7 @@ function projectRegisterDocs(harnessDir) {
|
|
|
6019
6029
|
if (!entry.isDirectory())
|
|
6020
6030
|
continue;
|
|
6021
6031
|
const registerPath = join14(projectsDir, entry.name, PROJECT_REGISTER_FILE);
|
|
6022
|
-
if (!
|
|
6032
|
+
if (!existsSync14(registerPath))
|
|
6023
6033
|
continue;
|
|
6024
6034
|
try {
|
|
6025
6035
|
registers.push({ projectId: entry.name, doc: readJson(registerPath) });
|
|
@@ -6088,7 +6098,7 @@ async function editIntentListener(ctx, resolver, config, adapter, target, actor,
|
|
|
6088
6098
|
}
|
|
6089
6099
|
|
|
6090
6100
|
// src/gates/skill-lint.ts
|
|
6091
|
-
import { existsSync as
|
|
6101
|
+
import { existsSync as existsSync15, readFileSync as readFileSync11 } from "node:fs";
|
|
6092
6102
|
import { basename as basename8, dirname as dirname7, resolve as resolve14, sep as sep2 } from "node:path";
|
|
6093
6103
|
var SKILL_LINT_LOGGER = "mstar/skill-lint";
|
|
6094
6104
|
|
|
@@ -6169,7 +6179,7 @@ function gateSkillIntent(ctx, harnessDir, config, target) {
|
|
|
6169
6179
|
if (!isSkillTarget(roots, target))
|
|
6170
6180
|
return;
|
|
6171
6181
|
const skillPath = resolve14(target.displayPath);
|
|
6172
|
-
if (!
|
|
6182
|
+
if (!existsSync15(skillPath))
|
|
6173
6183
|
return;
|
|
6174
6184
|
let doc;
|
|
6175
6185
|
try {
|
|
@@ -6220,7 +6230,7 @@ async function skillWriteIntentListener(ctx, resolver, config, target, actor, ne
|
|
|
6220
6230
|
}
|
|
6221
6231
|
|
|
6222
6232
|
// src/gates/seams.ts
|
|
6223
|
-
import { existsSync as
|
|
6233
|
+
import { existsSync as existsSync16, readFileSync as readFileSync13, readdirSync as readdirSync11 } from "node:fs";
|
|
6224
6234
|
import { basename as basename9, dirname as dirname9, join as join16, resolve as resolve15, sep as sep4 } from "node:path";
|
|
6225
6235
|
var SEAM_LOGGERS = {
|
|
6226
6236
|
"design-md": "mstar/design-md-lint",
|
|
@@ -6296,7 +6306,7 @@ function validateDesignDoc(doc, path) {
|
|
|
6296
6306
|
const violations = [...validateDesignTokenFrontmatter(doc).violations];
|
|
6297
6307
|
const isDark = basename9(path) === "DESIGN.dark.md";
|
|
6298
6308
|
const sibling = join16(dirname9(path), isDark ? "DESIGN.md" : "DESIGN.dark.md");
|
|
6299
|
-
if (
|
|
6309
|
+
if (existsSync16(sibling)) {
|
|
6300
6310
|
const light = isDark ? readFileSync13(sibling, "utf8") : doc;
|
|
6301
6311
|
const dark = isDark ? doc : readFileSync13(sibling, "utf8");
|
|
6302
6312
|
violations.push(...assertLightDarkParity(light, dark).violations);
|
|
@@ -6323,7 +6333,7 @@ function validateRolesState(rolesDir, skillsRoot = dirname9(rolesDir)) {
|
|
|
6323
6333
|
if (!entry.isDirectory() || !entry.name.startsWith("mstar-"))
|
|
6324
6334
|
continue;
|
|
6325
6335
|
const skillFile = join16(skillsRoot, entry.name, "SKILL.md");
|
|
6326
|
-
if (!
|
|
6336
|
+
if (!existsSync16(skillFile))
|
|
6327
6337
|
continue;
|
|
6328
6338
|
try {
|
|
6329
6339
|
skillTexts[entry.name] = readFileSync13(skillFile, "utf8");
|
|
@@ -6357,7 +6367,7 @@ function gateSeamIntent(ctx, harnessDir, config, seam, target) {
|
|
|
6357
6367
|
if (!isSeamTarget(seam, harnessDir, target))
|
|
6358
6368
|
return;
|
|
6359
6369
|
const path = resolve15(target.displayPath);
|
|
6360
|
-
if (!
|
|
6370
|
+
if (!existsSync16(path))
|
|
6361
6371
|
return;
|
|
6362
6372
|
let doc;
|
|
6363
6373
|
try {
|
|
@@ -6427,7 +6437,7 @@ function lintRolesWrite(doc, options) {
|
|
|
6427
6437
|
}
|
|
6428
6438
|
|
|
6429
6439
|
// src/gates/tools.ts
|
|
6430
|
-
import { existsSync as
|
|
6440
|
+
import { existsSync as existsSync17, readFileSync as readFileSync14 } from "node:fs";
|
|
6431
6441
|
import { join as join17, resolve as resolve16 } from "node:path";
|
|
6432
6442
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
6433
6443
|
var ITERATION_VIOLATION_SCHEMA = {
|
|
@@ -6593,9 +6603,9 @@ function registerSddIterationTools(ctx, resolver) {
|
|
|
6593
6603
|
presentResult: (_args, _result) => ({ card: "generic", title: "Iteration gate evaluation" }),
|
|
6594
6604
|
isConcurrencySafe: () => false,
|
|
6595
6605
|
async execute(args) {
|
|
6596
|
-
if (!
|
|
6606
|
+
if (!existsSync17(args.snapshot_path))
|
|
6597
6607
|
throw new Error(`workflow snapshot not found: ${args.snapshot_path}`);
|
|
6598
|
-
if (!
|
|
6608
|
+
if (!existsSync17(args.compass_path))
|
|
6599
6609
|
throw new Error(`compass file not found: ${args.compass_path}`);
|
|
6600
6610
|
const snapshotDoc = readJson(args.snapshot_path);
|
|
6601
6611
|
const compassDoc = parseCompassFrontmatter(args.compass_path);
|
|
@@ -6650,7 +6660,7 @@ function registerSeamTools(ctx, resolver) {
|
|
|
6650
6660
|
async execute(args) {
|
|
6651
6661
|
const abs = resolve16(args.dir);
|
|
6652
6662
|
const lightPath = join17(abs, "DESIGN.md");
|
|
6653
|
-
if (!
|
|
6663
|
+
if (!existsSync17(lightPath))
|
|
6654
6664
|
throw new Error(`design file not found: ${lightPath}`);
|
|
6655
6665
|
const light = readFileSync14(lightPath, "utf8");
|
|
6656
6666
|
const result = validateDesignDoc(light, lightPath);
|
|
@@ -6703,7 +6713,7 @@ function registerSeamTools(ctx, resolver) {
|
|
|
6703
6713
|
isConcurrencySafe: () => false,
|
|
6704
6714
|
async execute(args) {
|
|
6705
6715
|
const abs = resolve16(args.plan_path);
|
|
6706
|
-
if (!
|
|
6716
|
+
if (!existsSync17(abs))
|
|
6707
6717
|
throw new Error(`plan file not found: ${abs}`);
|
|
6708
6718
|
const text = readFileSync14(abs, "utf8");
|
|
6709
6719
|
const result = validateAuditDoc(text, abs);
|
|
@@ -6743,7 +6753,7 @@ function registerSeamTools(ctx, resolver) {
|
|
|
6743
6753
|
isConcurrencySafe: () => false,
|
|
6744
6754
|
async execute(args, exec) {
|
|
6745
6755
|
const abs = resolve16(args.doc_path);
|
|
6746
|
-
if (!
|
|
6756
|
+
if (!existsSync17(abs))
|
|
6747
6757
|
throw new Error(`knowledge doc not found: ${abs}`);
|
|
6748
6758
|
const text = readFileSync14(abs, "utf8");
|
|
6749
6759
|
const base = validateCompoundDoc(text, abs, args.repo_root !== undefined ? null : resolver.forAgent(exec.agent));
|
|
@@ -6788,7 +6798,7 @@ function registerSeamTools(ctx, resolver) {
|
|
|
6788
6798
|
isConcurrencySafe: () => false,
|
|
6789
6799
|
async execute(args) {
|
|
6790
6800
|
const rolesDir = resolve16(args.roles_dir);
|
|
6791
|
-
if (!
|
|
6801
|
+
if (!existsSync17(join17(rolesDir, "SKILL.md")))
|
|
6792
6802
|
throw new Error(`roles dir not found: ${rolesDir}`);
|
|
6793
6803
|
const result = validateRolesState(rolesDir, args.skills_root !== undefined ? resolve16(args.skills_root) : undefined);
|
|
6794
6804
|
return { ok: result.ok, violations: result.violations.map(iterationViolationView) };
|
|
@@ -6798,7 +6808,7 @@ function registerSeamTools(ctx, resolver) {
|
|
|
6798
6808
|
}
|
|
6799
6809
|
|
|
6800
6810
|
// src/gates/adapter.ts
|
|
6801
|
-
import { existsSync as
|
|
6811
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
6802
6812
|
import { join as join18 } from "node:path";
|
|
6803
6813
|
import { Service as Service2 } from "@deepseek-ai/cordis";
|
|
6804
6814
|
var HOST_LOGGER = "mstar/host-adapter";
|
|
@@ -6835,7 +6845,7 @@ class DshHostAdapter extends Service2 {
|
|
|
6835
6845
|
this.logSink(level, msg);
|
|
6836
6846
|
}
|
|
6837
6847
|
statusGate(path, kind, harnessDir) {
|
|
6838
|
-
if (!
|
|
6848
|
+
if (!existsSync18(path))
|
|
6839
6849
|
return { ok: true, violations: [] };
|
|
6840
6850
|
return validateStatusDoc(path, kind, harnessDir);
|
|
6841
6851
|
}
|
|
@@ -7447,7 +7457,7 @@ function registerWorkflowLedger(ctx, resolver, workflowAskCache) {
|
|
|
7447
7457
|
}
|
|
7448
7458
|
|
|
7449
7459
|
// src/gates/goal-bridge.ts
|
|
7450
|
-
import { existsSync as
|
|
7460
|
+
import { existsSync as existsSync19, readFileSync as readFileSync17, readdirSync as readdirSync13 } from "node:fs";
|
|
7451
7461
|
import { join as join21 } from "node:path";
|
|
7452
7462
|
var GOAL_BRIDGE_LOGGER = "mstar/goal-bridge";
|
|
7453
7463
|
var DEFAULT_MAX_GOAL_ROUNDS = 256;
|
|
@@ -7476,7 +7486,7 @@ function projectRegisterPointer(harnessDir) {
|
|
|
7476
7486
|
if (!entry.isDirectory())
|
|
7477
7487
|
continue;
|
|
7478
7488
|
const registerPath = join21(projectsDir, entry.name, PROJECT_REGISTER_FILE);
|
|
7479
|
-
if (
|
|
7489
|
+
if (existsSync19(registerPath))
|
|
7480
7490
|
return registerPath;
|
|
7481
7491
|
}
|
|
7482
7492
|
} catch {}
|
|
@@ -7491,7 +7501,7 @@ function iterationGoalObjective(iterationId) {
|
|
|
7491
7501
|
}
|
|
7492
7502
|
function steeringCompass(harnessDir) {
|
|
7493
7503
|
const iterationsDir = resolveIterationDir(harnessDir);
|
|
7494
|
-
if (!
|
|
7504
|
+
if (!existsSync19(iterationsDir))
|
|
7495
7505
|
return;
|
|
7496
7506
|
let entries;
|
|
7497
7507
|
try {
|
|
@@ -7503,7 +7513,7 @@ function steeringCompass(harnessDir) {
|
|
|
7503
7513
|
if (!entry.isDirectory())
|
|
7504
7514
|
continue;
|
|
7505
7515
|
const compassPath = join21(iterationsDir, entry.name, "delivery-compass.md");
|
|
7506
|
-
if (!
|
|
7516
|
+
if (!existsSync19(compassPath))
|
|
7507
7517
|
continue;
|
|
7508
7518
|
let content;
|
|
7509
7519
|
try {
|
|
@@ -7683,7 +7693,7 @@ function registerGoalBridge(ctx, resolver, config) {
|
|
|
7683
7693
|
}
|
|
7684
7694
|
|
|
7685
7695
|
// src/gates/plan-mode-bridge.ts
|
|
7686
|
-
import { existsSync as
|
|
7696
|
+
import { existsSync as existsSync20 } from "node:fs";
|
|
7687
7697
|
import { join as join22 } from "node:path";
|
|
7688
7698
|
var PLAN_MODE_BRIDGE_LOGGER = "mstar/plan-mode-bridge";
|
|
7689
7699
|
var PLAN_STATUS_TODO = "Todo";
|
|
@@ -7701,7 +7711,7 @@ function errorMessage5(error) {
|
|
|
7701
7711
|
}
|
|
7702
7712
|
function hasPrepareWindow(harnessDir) {
|
|
7703
7713
|
const statusPath = join22(harnessDir, STATUS_FILE);
|
|
7704
|
-
if (!
|
|
7714
|
+
if (!existsSync20(statusPath))
|
|
7705
7715
|
return false;
|
|
7706
7716
|
const selection = resolveReadWorkflow(harnessDir);
|
|
7707
7717
|
if (selection.kind === "error")
|
|
@@ -8123,7 +8133,6 @@ function engineStatusProvider(ctx, resolver, bootHarnessDir) {
|
|
|
8123
8133
|
};
|
|
8124
8134
|
}
|
|
8125
8135
|
var DIGEST_PLAN_CAP = 8;
|
|
8126
|
-
var DIGEST_LEASE_CAP = 4;
|
|
8127
8136
|
function joinCapped(items, cap, separator, render) {
|
|
8128
8137
|
const visible = items.slice(0, cap).map(render);
|
|
8129
8138
|
if (items.length > cap)
|
|
@@ -8131,23 +8140,17 @@ function joinCapped(items, cap, separator, render) {
|
|
|
8131
8140
|
return visible.join(separator);
|
|
8132
8141
|
}
|
|
8133
8142
|
function engineStatusSummary(source) {
|
|
8134
|
-
const lines = [
|
|
8135
|
-
`mstar engine status: v${source.version} | harness ${stripInterpolationHazard(source.harnessDir ?? "none")} | enforcement ${source.enforcement.hard ? "hard" : "soft"}`
|
|
8136
|
-
];
|
|
8137
|
-
const iteration = source.iteration;
|
|
8138
|
-
if (iteration !== undefined) {
|
|
8139
|
-
const gate2 = iteration.gate;
|
|
8140
|
-
const codes = gate2.violations.map((v) => v.code).join(", ");
|
|
8141
|
-
lines.push(`iteration ${stripInterpolationHazard(iteration.iterationId)}: gate ${gate2.ok ? "PASS" : `FAIL (${codes})`} | transition ${gate2.transition} | all plans done ${gate2.all_plans_done}`);
|
|
8142
|
-
}
|
|
8143
|
+
const lines = [`mstar engine status: v${source.version}`];
|
|
8143
8144
|
const state = source.state;
|
|
8144
8145
|
if (state !== null) {
|
|
8145
|
-
const
|
|
8146
|
-
|
|
8147
|
-
|
|
8148
|
-
|
|
8149
|
-
|
|
8150
|
-
|
|
8146
|
+
const selection = state.selection;
|
|
8147
|
+
if (selection.kind === "active") {
|
|
8148
|
+
const pending = state.plans.filter((p) => p.status !== "Done");
|
|
8149
|
+
const plans = pending.length === 0 ? "none" : joinCapped(pending, DIGEST_PLAN_CAP, " ", (p) => `${stripInterpolationHazard(p.id)}(${stripInterpolationHazard(p.status)})`);
|
|
8150
|
+
const workflowType = stripInterpolationHazard(state.workflowType ?? "unknown");
|
|
8151
|
+
const workflowStatus = stripInterpolationHazard(state.workflowStatus ?? "unknown");
|
|
8152
|
+
lines.push(`workflow ${stripInterpolationHazard(selection.workflowId)} (${workflowType}) ${workflowStatus} | plans: ${plans}`);
|
|
8153
|
+
}
|
|
8151
8154
|
}
|
|
8152
8155
|
return lines.join(`
|
|
8153
8156
|
`);
|
|
@@ -8167,7 +8170,7 @@ var inject = ["loader"];
|
|
|
8167
8170
|
function packagedCommandsDir() {
|
|
8168
8171
|
try {
|
|
8169
8172
|
const dir = fileURLToPath3(new URL("../harness-commands", import.meta.url));
|
|
8170
|
-
return
|
|
8173
|
+
return existsSync21(dir) ? dir : undefined;
|
|
8171
8174
|
} catch {
|
|
8172
8175
|
return;
|
|
8173
8176
|
}
|
|
@@ -8367,7 +8370,7 @@ function apply(ctx, config) {
|
|
|
8367
8370
|
}
|
|
8368
8371
|
const effectiveHard = config.enforcement === "hard" || bootHarnessDir !== null && resolveRepoEnforcement(bootHarnessDir).hard;
|
|
8369
8372
|
if (effectiveHard && (config.dispatchBinding ?? "").trim() === "") {
|
|
8370
|
-
ctx.logger(DISPATCH_LOGGER).warn("Enforcement: hard is active but dispatchBinding is unset — the
|
|
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");
|
|
8371
8374
|
}
|
|
8372
8375
|
if (effectiveHard && config.dispatchTools === undefined) {
|
|
8373
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/dist/types.d.ts
CHANGED
|
@@ -199,6 +199,20 @@ export interface MstarHarnessState {
|
|
|
199
199
|
* read).
|
|
200
200
|
*/
|
|
201
201
|
readonly selection: WorkflowSelectionView;
|
|
202
|
+
/**
|
|
203
|
+
* The selected workflow snapshot's top-level `type` (`plan` / `iteration`),
|
|
204
|
+
* projected loose (`string`) like `HarnessPlanView.status`. ALWAYS-present
|
|
205
|
+
* nullable scalar (lossless JSON, never omitted): missing/empty → `null`;
|
|
206
|
+
* `null` on selection error (no snapshot read). Added for the slim
|
|
207
|
+
* `mstar:engine-status` digest (plan `20260820-dsh-engine-status-slim`).
|
|
208
|
+
*/
|
|
209
|
+
readonly workflowType: string | null;
|
|
210
|
+
/**
|
|
211
|
+
* The selected workflow snapshot's top-level lifecycle `status`
|
|
212
|
+
* (`running` / `paused`; a terminal word for a stale-but-registered entry,
|
|
213
|
+
* decision D3). Same ALWAYS-present nullable discipline as `workflowType`.
|
|
214
|
+
*/
|
|
215
|
+
readonly workflowStatus: string | null;
|
|
202
216
|
/** Registered plan rows (`plan_id`/`id` + `status`), snapshot plans[] order. */
|
|
203
217
|
readonly plans: readonly HarnessPlanView[];
|
|
204
218
|
/** Open `residual_findings` counts by severity (non-zero only). */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mstar-harness/dsh",
|
|
3
|
-
"version": "3.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.
|
|
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.
|
|
72
|
-
"@deepseek-ai/dsh-client-locale": "^0.1.
|
|
73
|
-
"@deepseek-ai/dsh-client-runtime": "^0.1.
|
|
74
|
-
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.
|
|
75
|
-
"@deepseek-ai/dsh-client-ui-slots": "^0.1.
|
|
76
|
-
"@deepseek-ai/dsh-commands": "^0.1.
|
|
77
|
-
"@deepseek-ai/dsh-fs": "^0.1.
|
|
78
|
-
"@deepseek-ai/dsh-invariants": "^0.1.
|
|
79
|
-
"@deepseek-ai/dsh-jobs": "^0.1.
|
|
80
|
-
"@deepseek-ai/dsh-llm": "^0.1.
|
|
81
|
-
"@deepseek-ai/dsh-skill": "^0.1.
|
|
82
|
-
"@deepseek-ai/dsh-skill-filesystem": "^0.1.
|
|
83
|
-
"@deepseek-ai/dsh-tools": "^0.1.
|
|
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"
|