@jayyuen66/dsh-quality-gate 0.1.4 → 0.1.6
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/CHANGELOG.md +74 -23
- package/LICENSE +1 -1
- package/README.md +15 -15
- package/README.zh-CN.md +15 -15
- package/client.js +134 -394
- package/cordis.patch.yml +7 -2
- package/host.js +259 -138
- package/package.json +54 -41
package/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,63 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
3
|
格式遵循 [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),版本号遵循
|
|
4
|
-
[Semantic Versioning](https://semver.org/spec/v2.0.0.html)
|
|
4
|
+
[Semantic Versioning](https://semver.org/spec/v2.0.0.html)。最新版本在最上。这里的偏离是刻意的:
|
|
5
|
+
版本标题不写发布日期(各版发布时间以 npm registry 为准,`npm view <包名> time` 取得到),并按需
|
|
6
|
+
增设标准六类之外的小节(如 `Tests`、`Not changed, deliberately`)。
|
|
7
|
+
|
|
8
|
+
## 0.1.6
|
|
9
|
+
|
|
10
|
+
`apply_patch` 进入编辑台账与回合累加器的记账面;宿主开发基线抬到 `0.2.2-rc.1`,支持窗口从单条 `^0.2.1-rc.3` 改成两条宿主线的并集,`engines.node` 地板抬到 `>=24.9.0`;卡片组件改由 shared 的 `./client` 供。默认配置与检查白名单不变,需要留意的是 `engines.node` 抬高后 node 22 装不上本包。
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`apply_patch` 记账**:编辑台账与回合累加器按补丁的每个操作目标记账(含 move 的源与目的);失败结果否定同 callId 的全部行。补丁解析复用 shared 的官方 `dsh-fs-patch` 封装。
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- **shared 抬到 0.2.4**:catalog、生成物与 lockfile 随之同步;shared 新增的普通依赖 `@deepseek-ai/dsh-fs-patch` 由 shared 自己安装,本包不重复声明。
|
|
19
|
+
- **宿主开发基线**:`devDependencies` 的 dsh 家族抬到 `0.2.2-rc.1`,`@deepseek-ai/cordis` 抬到 `4.1.0`、cordis 插件组两枚抬到 `1.1.0`、`@deepseek-ai/schemastery` 抬到 `3.19.0`;根 catalog 与 overrides 的投影同步,`pnpm-lock.yaml` 随之重解。
|
|
20
|
+
- **支持窗口写成并集**:`engines.dsh` 与 `@deepseek-ai/dsh`、`@deepseek-ai/dsh-credentials` 两枚 peer 由 `^0.2.1-rc.3` 改为 `^0.2.1-rc.5 || ^0.2.2-alpha.3`;`@deepseek-ai/schemastery` 的 peer 由 `^3.18.6-alpha.1` 放宽为 `^3.18.5 || ^3.18.6-alpha.1`,与两条宿主线各自的实装版本对齐。caret 的预发布比较只吃自己那条元组,单线会把另一条线上的预发布判成不满足,而装载门(`app-boot` 带 `includePrerelease` 读 `engines.dsh`)没有这个差别——把窗口写窄只会让发布契约与安装器各说一套。`engines.node` 由 `>=22.12.0` 抬到 `>=24.9.0`,取值取自宿主自己的地板。
|
|
21
|
+
- **卡片组件改由 shared 的 `./client` 供**:`src/client-entry.ts` 的运行期值导入(`diffTouched` 与卡片组件)并入 `./client`,`package.json` 新增 `dsh.bundle.client.external`,`build-client.mjs` 的外置清单同步——组件由模块图供给,不再在每个卡片产物里内联一份;`cordis.patch.yml` 因此插入一条**不写 id** 的 `@jayyuen66/dsh-plugin-shared` 组件行(写 id 会在插件页多出一个可关闭的组件,关掉后设置卡整块不激活)。
|
|
22
|
+
- **CI 的 pnpm 与 node 取值**:`ci.yml` 与 `publish.yml` 的 workflow 级 `env.PNPM_VERSION` 固定为 `12.11.2`(与伞根 `packageManager` 同源,浮动主版本 `12` 会让门禁与发布在补丁版之间漂移);`node: [22, 24]` 加 macOS 排除改为单格 `node: [24.9]`,`node-version` 由 `24` 改为 `24.9`。
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- **注入回调不再丢掉 setup 屏障**(`host.ts`):`inject` 的回调返回类型由 `void` 放宽为 `void | Promise<void>`,把注册那一条 promise 交回 cordis——它把回调返回值当子 fiber 的 setup 屏障,返回 `void` 时页面策略那一条投影还没挂上就被判为装配完成。`test/host.test.ts` 补两条用例钉这一时序。
|
|
27
|
+
- **发布物体检在 Windows 上打不出包**(`pack-check.mjs`):`check:pack` 原先把系统临时目录的绝对路径带引号交给 ComSpec 的 `/c`,cmd 会把那对双引号连同参数一起交给 pnpm,pnpm 于是把 `"C:\Users\…\Temp\…"` 当相对路径拼到 cwd 上,报 `Failed to create directory D:\a\…\"C:\Users\…"`(os error 123)。目的地改为本包目录下一个自建的相对名(`.pack-tmp-<pid>-<rand>`):这一段名字既无空格也无引号,参数里没有需要保护的字符,两个平台同一种写法;`.gitignore` 补 `/.pack-tmp*/` 兜住进程被强杀时的残骸。同一条路径此前只在 Windows 格暴露——那一格上一版是 1.1 秒零输出的假绿。
|
|
28
|
+
- **Windows 格首次真跑出来的八条红**(`test/gate.test.ts`、`test/platform/guards.platform.test.ts`、`test/property/gate-invariants.property.test.ts`):`EditAccumulator.note()` 会 `path.normalize` 入参,Windows 上写死的 `/w/a.ts`、`src/a.ts` 归一成 `\` 形,而期望值是 POSIX 字形——断言因此只在半边平台成立。夹具改由 `path.join` 按当前平台构造,两侧同源后 normalize 成为恒等变换,被判的三件事(去重、淘汰顺序、扩展名过滤)没有变弱。`detectGate` 的 `root` 字面量不经归一,未动。
|
|
29
|
+
- **台账的同 callId 回填不再只否定最后一行**(`lib/edit-ledger.ts`):原先按 callId 找最近一条标记失败,普通编辑器一次调用只产一行、语义不变;`apply_patch` 一次调用可产多行,只否定最后一行会让其余路径在失败后仍被当成成功编辑,改为否定同 callId 的全部行。分流的 `pending` / `noCallId` 两张表合成一张 `records`,无 callId 的异常形状仍保守直记防漏。
|
|
30
|
+
- **回合累加器的编辑面补齐**(`lib/accumulator.ts`):`EDIT_TOOLS` 加入 `apply_patch`,多文件补丁的路径由 `editPathsOf` 逐枚展开,`note` 仍按单个路径去重。
|
|
31
|
+
|
|
32
|
+
### Docs
|
|
33
|
+
|
|
34
|
+
- dsh 徽章由 `^0.2.1-rc.3` 抬到 `^0.2.1-rc.5`,与 `engines.dsh` 首条同值;`cordis.patch.yml` 的注册行由「唯一一行」改写为「两条条目」并说明 shared 那行为何不写 id(`test/profile-bundle.ts` 里同类表述同步);开发面命令注释的 typecheck 由「三个 tsconfig」改为四个,与 `package.json` 的 `typecheck` 实跑数一致。
|
|
35
|
+
|
|
36
|
+
## 0.1.5
|
|
37
|
+
|
|
38
|
+
一轮跨面审查修复:凭据 URL 判据重建、设置卡改逐字段校验、门禁探测与反馈投递收口;
|
|
39
|
+
注入配额语义改为按会话跨回合累计。
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
|
|
43
|
+
- **凭据 URL 判据重建**(新增 `lib/credential-url.ts`,`lib/gateway-target.ts` 改为转导出):IPv4-mapped 回环地址按末 32 位数值判(`::ffff:7fa:0` 不再误判回环);`resolveGatewayUrl` 对带 userinfo/query/hash 的基址回空串(fail-closed)。
|
|
44
|
+
- **设置卡逐字段校验**(`src/client-entry.ts`):`projectCardValues` 改逐字段解析,坏 URL 不再清空整卡;网关地址经 `isCredentialSafeUrl` refine,保存前拦 `invalidGatewayUrl`;凭据引用名按 REF_PATTERN 复判 `invalidKeyRef`;数字字段弃用宿主 zod 缺失的 `.validate`,改 `inBounds` 边界自判(宿主冻结 zod 4.4.3 下不再崩)。
|
|
45
|
+
- **台账读口**(`lib/edit-ledger-reader.ts`):publish/effect 收进 try,失败先撤读口、再释放投影、最后报原始错;重复 dispose 幂等。
|
|
46
|
+
- **台账游标与卸载**(`host.ts`):游标只推进到台账实际覆盖末端(`EDIT_LEDGER_MAX` 截断不再永久跳号);异步读窗后复查卸载旗,dispose 后不再新建 flight。
|
|
47
|
+
- **门禁探测**(`lib/gate-detect.ts`):workspace 根缺 check 脚本不再降级发 tsc(solution-style 恒绿);同 root 去重;清单加 `.ruff.toml`。
|
|
48
|
+
- **网关反馈**(`lib/gateway-feedback.ts`):基址做 http(s)/query/userinfo 校验并复用凭据判据;非法 serviceId 回落 default;空正文不投递;错误正文按 content-type 才入日志。
|
|
49
|
+
- **反馈内容**(`lib/feedback-content.ts`):command 非数组容错、空 failure 不入库、正文追加 prompt-injection 隔离标注;`renderTemplate` 占位符放宽到 `{[^{}]+}`。
|
|
50
|
+
- **散修**:`lib/accumulator.ts` 路径 trim+normalize 归一;`lib/edit-ledger.ts` `strictObject`→`object`、failEntry 幂等、空 callId 不标失败;`pack-check.mjs` 改用官方 `builtinModules`、模板插值排除、产物缺席/无入口显形、win32 走 ComSpec;`build-host.mjs` 加空产物守卫。
|
|
51
|
+
|
|
52
|
+
### Changed
|
|
53
|
+
|
|
54
|
+
- **注入配额语义**:由「每回合每根」改为「每会话跨回合累计、干净通过重置」(`host.ts`、`cordis.patch.yml` 与卡片中英文案同步)。
|
|
55
|
+
- **发布链加固**(`.github/workflows/publish.yml`):分支守卫比完整 ref、token 形态校验、tarball 内 name/version 与预检逐字比对、查 registry 重复版本、直接发布那枚 tarball、发布后可见性轮询;CI 侧 actionlint 按 glob 逐文件豁免、codeql 对 fork PR 只对 analyze 步容错。
|
|
56
|
+
- **构建与元数据**:package.json 删 `prepack`、typecheck 四 program 串联;coverage 分母加 `src/ui-messages.ts`;oxlint `syncWrites` 改按文件 `syncIoScopes`;author 改对象形;`@deepseek-ai/schemastery` peer 放宽为 `^3.18.6-alpha.1`;catalog 的 shared 抬到 0.2.2;devDependencies 新增 `@deepseek-ai/dsh-bash-local` / `@deepseek-ai/dsh-subprocess-local` 与 fast-check 4.10.2。
|
|
57
|
+
|
|
58
|
+
### Added
|
|
59
|
+
|
|
60
|
+
- **测试三层**:`test/e2e/real-tsc.e2e.test.ts`(真 tsc 进程)、`test/platform/guards.platform.test.ts`、`test/property/gate-invariants.property.test.ts`(fast-check)、`test/client-zod-surface.test.ts`(禁 `.validate` 的门禁)。
|
|
5
61
|
|
|
6
62
|
## 0.1.4
|
|
7
63
|
|
|
@@ -17,7 +73,7 @@
|
|
|
17
73
|
|
|
18
74
|
## 0.1.3
|
|
19
75
|
|
|
20
|
-
这一版把判定面与构建/CI 面一起收口:ruff 不再对只写了 `pyproject.toml` 的工程发门禁,卡片保存期间的编辑不再被静默丢弃,记忆反馈的文案与来源一致,Windows
|
|
76
|
+
这一版把判定面与构建/CI 面一起收口:ruff 不再对只写了 `pyproject.toml` 的工程发门禁,卡片保存期间的编辑不再被静默丢弃,记忆反馈的文案与来源一致,Windows 与发布链的静默降级补上判据。构建/CI 那一批清掉的是只在 Windows 上才会红的判据、两个构建脚本里没有任何门看得见的静默降级,以及发布链路缺的一道检查。宿主线抬到 `0.2.1-rc.3`。
|
|
21
77
|
|
|
22
78
|
### Fixed
|
|
23
79
|
|
|
@@ -28,23 +84,12 @@
|
|
|
28
84
|
- **四个工作流里的判断面缺陷**:`ci.yml` 的 `find … -print -quit` 在零匹配时得到空串、多匹配时静默取任意一个,改为「恰好一个 tarball」并点名实际个数;`publish.yml` 的重复发布检查把包名与依赖名写死在 URL 里(改名后会查到 404 而当成首发放行),改为从 `package.json` 读,并在版本低于 registry `latest` 时告警(只告警:从旧分支发版会把 `latest` 回退,但半吊子的预发布序比较不该一票否决发布);静态 token 的形态校验把 `exit 1` 降级为告警,OIDC 主线不该被兜底凭据的形态卡死;`scorecard.yml` 的 job 级判据用 `github.ref_name` 比默认分支名,同名 tag 上会误判,改为比完整 ref。
|
|
29
85
|
- **`ci.yml` 的 `-ignore SC2016` 是全局豁免**:一行调用同时校验四份工作流,`codeql.yml` 与 `scorecard.yml` 没有 run 脚本体,却也继承了这条豁免——它们将来真写出的同类缺陷会被一起吞掉。改成按文件分两次调用。
|
|
30
86
|
- **`codeql.yml` 与 `scorecard.yml` 缺并发控制与权限声明**:前者补 `actions: read`(本仓是 public,官方只在私有仓要求它;显式声明是为了可见性变化时不静默缺权限),后者补按事件名分组的 `concurrency`,让四个入口不再互相覆盖同一份 analysis 结果。
|
|
31
|
-
|
|
32
87
|
- **`test/schema-coverage.ts` 的 host schema 解析器**:括号匹配不跳过字符串、模板与注释,`Schema.object(` 只取首个块,扩展名只收 `.ts`,包根靠 `testDir/..` 硬推且读盘无兜底。这些偏差都会让「宿主字段全集」偏少而门禁假绿。解析器改成字符串/注释感知、遍历全部 `Schema.object`、按前缀识别扩展名、向上找 `package.json`、读不到时判红;新增 `test/schema-coverage.test.ts` 钉住这一面。
|
|
33
88
|
- **记忆反馈的目标面与正文收口**:网关地址必须是合法的 `http(s)` URL(半截串、非 http(s) scheme 均按「未配置」处理),凭据只发 https 或回环地址;`x-tdai-service-id` 形状不合规时回落默认服务名;命令、根与错误文本在写入记忆前折成单行,避免一条反馈被拆成多段。
|
|
34
89
|
- **编辑累加器的容量契约对任意入参成立**:`maxFiles` 为 NaN、0、负数或小数时回落到默认值;同一文件被反复编辑会刷新到最近位置,避免最该检查的文件第一个被淘汰;host 与累加器共用同一份编辑工具名单。
|
|
35
90
|
- **消息模板插值只认自有键**:`{constructor}` / `{toString}` 这类原型链名字不再渲染成函数源码,未提供的名字留空串。
|
|
36
91
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
- **`ruff` 门禁的可发条件**(见 Fixed 第一条)是**判定规则**的变化:只有 `pyproject.toml` 的 Python 工程从此不再自动获得 ruff 门禁。两份 README 的命令表同步。
|
|
40
|
-
- **宿主线抬到 `0.2.1-rc.3`**:`engines.dsh`、peer/dev 依赖、overrides 与 README 徽标/正文同值。
|
|
41
|
-
- **构建编排统一进 shared 的 `lib/build-*`**:三个构建脚本只剩薄配置层,产物仍落 `dist/`;CI/CodeQL/Scorecard/Dependabot 与 Windows 矩阵补齐。
|
|
42
|
-
|
|
43
|
-
### Build and CI fixes
|
|
44
|
-
|
|
45
|
-
这一版同时清掉构建侧与 CI 侧的缺陷:一批只在 Windows 上才会红的判据、两个构建脚本里没有任何门看得见的静默降级,以及发布链路缺的一道检查。
|
|
46
|
-
|
|
47
|
-
#### Fixed
|
|
92
|
+
构建与 CI 面(同属本版):
|
|
48
93
|
|
|
49
94
|
- **`host.ts` 里三处用 `/` 拼子项的地方改成 `path.join`。** `findManifestRoot` 探测 `MANIFESTS`、`workspaceRootUpgrade` 探测 `pnpm-workspace.yaml`、`probeProjectRoot` 过滤 manifest 与判断 `tsconfig.json`,原先都用模板串拼出 `dir/manifest`。`dir` 是宿主给的真实路径,在 Windows 上这种混形让 `existsSync` 恒假——于是所有 manifest 都探测不到、项目根一路回落到父目录,`gate-detect` 的判据整片失效而命令照常退出 0。同一处 `declaredScriptsOf` 读 `package.json` 也改成 `path.join`。新增 `TSCONFIG_MANIFEST` 常量,避免同一个文件名在文件里出现两次。
|
|
50
95
|
- **`collapseStorePaths` 的折叠原先完全静默。** 正则只认 `/` 分隔的「内容寻址 store 段」(`node_modules/` 下那层带「名@版_哈希」的目录),既没有归一反斜杠,也没有折掉前缀里的 `../`。没命中时它原样放行,于是 `client.js` 会带着「版本 + 本机 store 哈希」进公共产物,而两道新鲜度门禁比的都是同一台机器上的内存构建与磁盘文件——没有任何门能看见。现在先归一分隔符,再把可选的前缀与 store 段整段折成一层,折完仍检出 store 段就抛错停止构建。
|
|
@@ -53,7 +98,19 @@
|
|
|
53
98
|
- **两处注释与实现不符。** 构建脚本的头注释说「react 及其子路径都要外部化」并给了「内联子路径会出现第二份 React」的理由——那个理由不成立,`react/jsx-runtime` 内联后自身仍 `require("react")`;`ci.yml` 里「门禁没跑过就别留下产物。」那行原先挂在上传覆盖率报告上方,与它描述的步骤错位。
|
|
54
99
|
- **`test/host-integrity.test.ts`、`test/host.test.ts`、`test/gateway-feedback.test.ts` 的 POSIX 假设。** 新增的 Windows 矩阵会让这些判据整片红:临时目录原先写成字面量 `/tmp/qg-shallow-…`,改成 `path.join(tmpdir(), …)`;匹配 tsc 命令的正则 `/bin\/tsc --noEmit$/u` 在 Windows 上拿到的渲染是 `'C:\…\bin\tsc' --noEmit`(带引号、反斜杠),改成 `/bin[\\/]tsc'? --noEmit$/u`;`deriveProjectKey` 的注入补上 `sep: "/"`,让它与字形的来源同源——路径字形与 `resolve` 不来自同一次调用时,`path.win32` 会把 `/tmp/x/proj` 解析成带盘符的串,字面量替换一次都不命中。
|
|
55
100
|
|
|
56
|
-
|
|
101
|
+
### Changed
|
|
102
|
+
|
|
103
|
+
- **宿主开发线抬到 `0.2.2-alpha.2`**:devDependencies 与 catalog/overrides 同值;`engines.dsh` 与
|
|
104
|
+
`@deepseek-ai/dsh*` peer 写支持窗口 `^0.2.1-rc.3`。
|
|
105
|
+
- **`ruff` 门禁的可发条件**(见 Fixed 第一条)是**判定规则**的变化:只有 `pyproject.toml` 的 Python 工程从此不再自动获得 ruff 门禁。两份 README 的命令表同步。
|
|
106
|
+
- **宿主线抬到 `0.2.1-rc.3`**:`engines.dsh`、peer/dev 依赖、overrides 与 README 徽标/正文同值。
|
|
107
|
+
- **构建编排统一进 shared 的 `lib/build-*`**:三个构建脚本只剩薄配置层,产物仍落 `dist/`;CI/CodeQL/Scorecard/Dependabot 与 Windows 矩阵补齐。
|
|
108
|
+
|
|
109
|
+
- **`publish.yml` 补回 `pnpm run check`。** 发布由 `workflow_dispatch` 手动触发,可以从任意分支派发;CI 在 `main` 上的绿不代表被派发的那个 ref 是绿的,而发布前少这一道检查等于把「发布物能装」与「发布物正确」当成一件事。现在它在 `pnpm install --frozen-lockfile` 之后、`发布` 之前跑。
|
|
110
|
+
- **`ci.yml` 的覆盖率上传取 `if-no-files-found: warn` 而不是 `error`。** 这一步在 `if: always()` 下跑,而 `coverage/` 由 vitest 产出——typecheck / lint / build 任一先红时目录根本不存在,此时判红会盖住真正的失败原因,还把后面的步骤一起中断。
|
|
111
|
+
- **`ci.yml` 里新增的 Windows 矩阵与多平台并发组的形状**:组键带 `event_name`,取消只给 `push` 与 `pull_request`;gate 的检查名带 `matrix.os`;attest 拆成独立 job 并补齐 `artifact-metadata: write`;install-probe 收窄到 `contents: read`;所有 `actions/checkout` 带 `persist-credentials: false`;第三方 action 与 `actionlint` 全部钉到 commit SHA / 校验和。
|
|
112
|
+
|
|
113
|
+
### Added
|
|
57
114
|
|
|
58
115
|
- **`tsconfig.build-scripts.json` 与 `typecheck` 里的一次额外 `tsc`。** 两个构建脚本头部的 `// @ts-check` 只在编辑器里生效:它们不在任何 `tsc` program 的 `include` 里,CI 因此对这一面没有任何门。新 tsconfig 继承共享基线、打开 `allowJs` / `checkJs`、`noEmit`,只收两个构建脚本;`typecheck` 追加 `&& tsc --noEmit -p tsconfig.build-scripts.json`。变异实测:往 `build-client.mjs` 塞一行 `probe.toFixed("nope")` 会红,删掉即绿。
|
|
59
116
|
- **`tsconfig.build-scripts.json` 补齐之后暴露并修掉的类型缺陷**:`isRecord` 一律改成类型谓词(`@returns {raw is Record<string, unknown>}`),`asRecord` 改成守卫分支而不是类型断言(断言会同时撞 `typescript(no-unsafe-type-assertion)` 与 `eslint(no-inline-comments)`);`invokedDirectly()` 先取 `process.argv.at(1)` 再判 `undefined`,不再把可能不存在的值直接交给 `realpathSync`;读 `package.json` 的 `name` 用 `const nameKey = "name"` 索引——点号写法被 `noPropertyAccessFromIndexSignature` 拒绝,索引写法又被 `dot-notation` 要求改成点号,两侧只能靠一次具名取值同时满足。
|
|
@@ -61,18 +118,12 @@
|
|
|
61
118
|
- **`test/dist-size.test.ts` 的预算改成按实测锚定**:`host.js` 70 500 B(实测 61 110 B)、`client.js` 23 500 B(实测 20 222 B),取实测上浮约 15% 再向上取整到 500 B。注释写明本仓没有 `build-types.mjs`,所以只量两项。
|
|
62
119
|
- **`package.json` 的 `keywords`**,以及两份 README 各新增一节「shared 的版本下限 / Shared version floor」。
|
|
63
120
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
- **`publish.yml` 补回 `pnpm run check`。** 发布由 `workflow_dispatch` 手动触发,可以从任意分支派发;CI 在 `main` 上的绿不代表被派发的那个 ref 是绿的,而发布前少这一道检查等于把「发布物能装」与「发布物正确」当成一件事。现在它在 `pnpm install --frozen-lockfile` 之后、`发布` 之前跑。
|
|
67
|
-
- **`ci.yml` 的覆盖率上传取 `if-no-files-found: warn` 而不是 `error`。** 这一步在 `if: always()` 下跑,而 `coverage/` 由 vitest 产出——typecheck / lint / build 任一先红时目录根本不存在,此时判红会盖住真正的失败原因,还把后面的步骤一起中断。
|
|
68
|
-
- **`ci.yml` 里新增的 Windows 矩阵与多平台并发组的形状**:组键带 `event_name`,取消只给 `push` 与 `pull_request`;gate 的检查名带 `matrix.os`;attest 拆成独立 job 并补齐 `artifact-metadata: write`;install-probe 收窄到 `contents: read`;所有 `actions/checkout` 带 `persist-credentials: false`;第三方 action 与 `actionlint` 全部钉到 commit SHA / 校验和。
|
|
69
|
-
|
|
70
|
-
#### Not changed, deliberately
|
|
121
|
+
### Not changed, deliberately
|
|
71
122
|
|
|
72
123
|
- **除 ruff 的可发条件外,四条命令的白名单与优先级、`not-run` 与 `code-failure` 的分界、注入配额、预算分摊、默认设置值与双语文案内容未变。**
|
|
73
124
|
- **覆盖率阈值仍由 `@jayyuen66/dsh-plugin-shared/config/vitest.base` 单源给出(100%)**,`coverageInclude` 仍只覆盖 host 半。
|
|
74
125
|
|
|
75
|
-
|
|
126
|
+
### Tests
|
|
76
127
|
|
|
77
128
|
- `pnpm run check` 通过:typecheck 三个 tsconfig、`oxlint` 0 warning / 0 error、两个 rolldown 构建、`vitest run --coverage` 四项 100%、`oxfmt --check`、`check:pack` 的 tarball 体检。
|
|
78
129
|
- 新增的门禁都做过变异实测:`tsconfig.build-scripts.json` 那条塞错误即红、删掉即绿;`collapseStorePaths` 的残留检查拿含 `.pnpm` 的行喂进去会抛,四种真实形态(无前缀 / `../` 前缀 / 反斜杠 / 普通相对段)折叠结果逐个断言。
|
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
<a href="https://www.npmjs.com/package/@jayyuen66/dsh-quality-gate"><img src="https://img.shields.io/npm/d18m/@jayyuen66/dsh-quality-gate?style=flat-square" alt="npm downloads (total)" /></a>
|
|
12
12
|
<a href="https://github.com/JayYuen666/dsh-quality-gate/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/JayYuen666/dsh-quality-gate/ci.yml?branch=main&style=flat-square&label=ci" alt="ci" /></a>
|
|
13
13
|
<img src="https://img.shields.io/badge/license-MIT-4c1?style=flat-square" alt="MIT" />
|
|
14
|
-
<img src="https://img.shields.io/badge/node-%3E%
|
|
15
|
-
<img src="https://img.shields.io/badge/dsh-%5E0.2.1--rc.
|
|
14
|
+
<img src="https://img.shields.io/badge/node-%3E%3D24.9-4c1?style=flat-square&logo=nodedotjs&logoColor=white" alt="node" />
|
|
15
|
+
<img src="https://img.shields.io/badge/dsh-%5E0.2.1--rc.5-658EFF?style=flat-square" alt="dsh" />
|
|
16
16
|
<a href="https://github.com/deepseek-ai/deepseek-harness"><img src="https://img.shields.io/badge/powered_by_dsh-4D6BFE?style=flat-square&logo=deepseek&logoColor=white" alt="powered by dsh" /></a>
|
|
17
17
|
</p>
|
|
18
18
|
|
|
@@ -61,8 +61,8 @@ dsh plugin --profile web add @jayyuen66/dsh-quality-gate
|
|
|
61
61
|
|
|
62
62
|
Remove with `dsh plugin --profile web remove @jayyuen66/dsh-quality-gate`. The package is on the public npm registry, so installation needs no credentials.
|
|
63
63
|
|
|
64
|
-
- **Requires Node.js `>=
|
|
65
|
-
- **Requires a host `dsh` on the `0.2.1-rc.3` line.** The requirement the host reads is `engines.dsh` (`^0.2.1-rc.3`), and it **decides alone**; pre-release versions participate in range comparison, and `0.3.0` and later need the explicit, risk-accepting grant `dsh plugin --profile <p> allow-version <pkg>@<ver> --dsh-version <exact> --accept-risk`.The support window rolls: this package is verified against
|
|
64
|
+
- **Requires Node.js `>= 24.9`.** The artifacts use newer runtime faces such as `AbortSignal.any`.
|
|
65
|
+
- **Requires a host `dsh` on the `0.2.1-rc.5` or `0.2.2-alpha.3` line.** The requirement the host reads is `engines.dsh` (`^0.2.1-rc.5 || ^0.2.2-alpha.3`), and it **decides alone**; pre-release versions participate in range comparison, and `0.3.0` and later need the explicit, risk-accepting grant `dsh plugin --profile <p> allow-version <pkg>@<ver> --dsh-version <exact> --accept-risk`. The support window rolls: this package is verified against every released host version inside the window at publish time and re-verified once the window moves, so the version number in this bullet is not a standing promise.
|
|
66
66
|
- **The runtime dependency `@jayyuen66/dsh-plugin-shared` must already be on the same registry**, otherwise the whole set 404s.
|
|
67
67
|
- **`typescript` is a real `dependencies` entry**: the fallback entry for the `tsc` gate — the project's own install wins, then the copy shipped with this package; no network action at runtime, never an `npx` auto-download.
|
|
68
68
|
- **`zod` is also a real `dependencies` entry**: the edit-ledger projection state and the settings card's value/draft faces use it for shape validation, versioned through the umbrella catalog. The client artifact keeps `require("zod")` externalized via the host's `PLATFORM_MODULES`.
|
|
@@ -91,7 +91,7 @@ If your profile pins `@jayyuen66/dsh-plugin-shared` with a pnpm `overrides` entr
|
|
|
91
91
|
|
|
92
92
|
## <img src="https://api.iconify.design/lucide/plug.svg?color=%23658EFF&height=22" height="22" width="22" align="absmiddle" alt="" /> Enabling it in dsh
|
|
93
93
|
|
|
94
|
-
- Bundle form: `dsh.bundle.patch` in `package.json` points at `cordis.patch.yml`,
|
|
94
|
+
- Bundle form: `dsh.bundle.patch` in `package.json` points at `cordis.patch.yml`, which carries **two entries**: `- name: "@jayyuen66/dsh-plugin-shared"` (the supplier of the card components, deliberately **without an `id`** so the plugin page does not list it as a switchable component) and `- id: quality-gate` with `name` set to the bare package name (the host scans `dsh.client` only on bare-name entries); `dsh plugin --profile web add/remove` maintains those lines.
|
|
95
95
|
- The host half hard-depends only on `settings` (`default.inject = ["settings"]`); precedence is card runtime value > the line's `config:` > schema built-in defaults.
|
|
96
96
|
- The client half (`dsh.client.platform = web`, `immediately: true`) injects a card into the `plugins.bundle.config` slot of the plugin page. Two identifiers, deliberately different sources: the slot key is the **bundle package name** `@jayyuen66/dsh-quality-gate`, while the settings namespace and `configForms.get()` use the **bare entry id** `quality-gate` — mix them up and the card never renders.
|
|
97
97
|
- Saving on the card is explicitly staged: edits stay in local pending state until Save writes them **serially**, key by key, consuming the `Promise<boolean>` acceptance bit — the first `false` stops the loop, shows `saveRejected` and keeps the unwritten changes; a rejecting write shows `saveFailed`. Inputs stay editable during a save, and edits made in flight stay in the pending set instead of being wiped by it; Revert discards the pending edits. Clearing an input means unset, restoring that field's schema default.
|
|
@@ -103,10 +103,10 @@ The whitelist is locked at type level (the `GateCommand` union in `lib/gate-dete
|
|
|
103
103
|
|
|
104
104
|
| Command | When it is issued |
|
|
105
105
|
| --- | --- |
|
|
106
|
-
| `pnpm check` | The root has both `pnpm-workspace.yaml` and `package.json`, and the root scripts declare a non-empty `check`.
|
|
106
|
+
| `pnpm check` | The root has both `pnpm-workspace.yaml` and `package.json`, and the root scripts declare a non-empty `check`. A workspace root without `check` gets no gate at all — it does not fall back to `tsc` (see below) |
|
|
107
107
|
| `cargo check` | The root has a `Cargo.toml` |
|
|
108
108
|
| `ruff check .` | The root has a `ruff.toml`, or a `pyproject.toml` with a `[tool.ruff]` section. A bare `pyproject.toml` does not count — it is Python metadata, not a ruff opt-in |
|
|
109
|
-
| `tsc --noEmit` |
|
|
109
|
+
| `tsc --noEmit` | A **non-workspace** root with both `package.json` and `tsconfig.json`; without `tsconfig.json` it is not a TS project. A workspace root does not qualify: its tsconfig is usually solution-style (`files: []` + `references`), and `tsc --noEmit` checks no subproject and exits 0 |
|
|
110
110
|
|
|
111
111
|
- `tsc` is only a logical name in the command: the entry is resolved at runtime — the project's own `typescript` first, then the copy shipped with this package, finally `tsc` on PATH (reported as not-run by the "tool unavailable" exit); never an `npx` auto-download.
|
|
112
112
|
- Root detection: from each edited file, up to `maxRootDepth` (default 10) levels to the nearest manifest root; a package that is **itself** a workspace root yielding `pnpm check` keeps its own root (a change in one package must not fan out into the whole umbrella workspace), and otherwise a monorepo package is upgraded to the workspace root holding `pnpm-workspace.yaml` — but only when that root **yields a gate**; otherwise the package keeps its own root (an outer workspace without a `check` script must not swallow the package's `tsc`/`ruff` gate).
|
|
@@ -147,17 +147,17 @@ Settings persist in the host's settings file under the dsh data directory; the c
|
|
|
147
147
|
| --- | --- |
|
|
148
148
|
| Zero outbound by default | `memoryFeedback` off plus an empty URL are a double lock — with either empty nothing is pushed and the credential store is not even queried |
|
|
149
149
|
| What leaves the machine | When enabled, only the command, the project root and the full error text (single-lined, untruncated) — sent to **your own** gateway |
|
|
150
|
-
| URL and credential safety | Credentials go to `https` or loopback addresses only (`
|
|
150
|
+
| URL and credential safety | Credentials go to `https` or loopback addresses only (`127.0.0.0/8`, IPv6 loopback, `localhost`); `localhost` depends on the local hosts/DNS and is a deliberate fail-open — if hosts points elsewhere, credentials go there. Plain `http` to a non-loopback host is refused as unconfigured, with the reason stated. A base URL that does not parse as a legal `http(s)` URL counts as unconfigured too; URLs with userinfo are rejected |
|
|
151
151
|
| The key | The value lives in the host credential service (a credentials file under `$DSH_HOME`); settings store only the ref name, and a ref name that is not a valid POSIX variable name counts as unconfigured |
|
|
152
|
-
| Failure handling | Push failures only warn
|
|
152
|
+
| Failure handling | Push failures only warn and never block the turn: configuration/unsafe-URL downgrades are reported once per load; HTTP non-2xx, timeouts and delivery errors are logged on every failure (deduplicating them would hide a persistent fault) |
|
|
153
153
|
| Disk reads | Manifest presence, the root `package.json` scripts, `pyproject.toml` section headers, the head of a spilled truncated file; no file writes, no telemetry |
|
|
154
154
|
| Memory | Session ledgers (edit accounting / injection quota / event cursors) live in process memory and are cleared when the plugin unloads |
|
|
155
155
|
|
|
156
156
|
## <img src="https://api.iconify.design/lucide/triangle-alert.svg?color=%23658EFF&height=22" height="22" width="22" align="absmiddle" alt="" /> Known boundaries
|
|
157
157
|
|
|
158
158
|
- **The whitelist has exactly four commands and cannot be extended via settings.** Roots checked by other tools (`go test`, `mypy`, `eslint`, …) get no gate.
|
|
159
|
-
- **Only successful edits count**, and only writes through `edit` / `write` / `str_replace_editor` (`view` is read-only). Files outside the extension whitelist (md/txt/images/`.sh`) are not counted — none of the gate commands can validate them, and a `.sh` would only drag the root resolution somewhere else and produce a false failure.
|
|
160
|
-
- **Out-of-process subagents (ACP / claude-code / codex) emit no dsh events**, so they are outside the gate by construction; in-process subagents are skipped and their edits
|
|
159
|
+
- **Only successful edits count**, and only writes through `edit` / `write` / `str_replace_editor` / `apply_patch` (`view` is read-only). A multi-file `apply_patch` records every operation target, including both sides of a move. Files outside the extension whitelist (md/txt/images/`.sh`) are not counted — none of the gate commands can validate them, and a `.sh` would only drag the root resolution somewhere else and produce a false failure.
|
|
160
|
+
- **Out-of-process subagents (ACP / claude-code / codex) emit no dsh events**, so they are outside the gate by construction; in-process subagents are skipped too, and their edits land in their own session ledger rather than the parent's — there is currently no guarantee that the parent turn verifies them (they can be missed).
|
|
161
161
|
- **Never talks over the user**: no injection when the inbox has pending items, the turn signal is aborted, or the plugin is unloading. Plan mode is not exempt — a planning turn that touched code is gated the same way.
|
|
162
162
|
- **When evidence is truncated with no spill file, the first error may already be gone**; the text says the evidence is incomplete instead of pretending otherwise.
|
|
163
163
|
- **When the sandbox policy cannot be resolved, no gate runs at all** (injected as not-run); it never falls back to the deployment-level policy — that would run the gate outside the sandbox, get it denied, and produce a false repair instruction.
|
|
@@ -166,21 +166,21 @@ Settings persist in the host's settings file under the dsh data directory; the c
|
|
|
166
166
|
|
|
167
167
|
| Question | Answer |
|
|
168
168
|
| --- | --- |
|
|
169
|
-
| Why did my pnpm project not run `pnpm check`? | Its root `package.json
|
|
169
|
+
| Why did my pnpm project not run `pnpm check`? | Its root must have both `pnpm-workspace.yaml` and `package.json`, with a non-empty `check` script in root scripts. Without `pnpm-workspace.yaml` the next candidate applies (a non-workspace root may reach `tsc`/`ruff`/`cargo`); a workspace root without `check` gets no gate at all (a solution-style tsconfig would make `tsc` permanently green). Deliberate trade: a false failure costs more than a missed check |
|
|
170
170
|
| The gate said "could not run" — is my code broken? | No. Sandbox policy denial, a runner that will not start, a timeout, a cancellation, an exhausted budget, and exit 126/127 **whose stderr names the gate command itself** (tool not installed) all land here. The injected text explicitly says **do not change code because of it**, and it is reported separately as `gate-not-run`. A 126/127 raised *inside* your own `check` script is a code failure and is reported as such |
|
|
171
171
|
| Can it loop forever? | Once the quota is spent it injects one user-visible release note, stops auto-repair, and only counts again after a fully clean pass |
|
|
172
172
|
| Stop it temporarily? | Turn off `enabled` on the card, or run `dsh plugin --profile web remove @jayyuen66/dsh-quality-gate` |
|
|
173
173
|
| Which `tsc` is used? | The project's own install first, then the `typescript` shipped with this package, and PATH only as the last resort — never an `npx` download |
|
|
174
|
-
| What about code edited by subagents? | In-process subagents are
|
|
174
|
+
| What about code edited by subagents? | In-process subagents are skipped as well, and their edits land in their own session ledger rather than the parent's — there is currently no guarantee that the parent turn verifies them (they can be missed); out-of-process subagents emit no dsh events, so they are outside the gate by construction |
|
|
175
175
|
|
|
176
176
|
## <img src="https://api.iconify.design/lucide/wrench.svg?color=%23658EFF&height=22" height="22" width="22" align="absmiddle" alt="" /> Development
|
|
177
177
|
|
|
178
178
|
```sh
|
|
179
179
|
pnpm install
|
|
180
|
-
pnpm run check # typecheck (
|
|
180
|
+
pnpm run check # typecheck (four tsconfigs) → lint → client build → host build → tests (with coverage) → tarball inspection → format
|
|
181
181
|
```
|
|
182
182
|
|
|
183
|
-
- Coverage is measured over `host.ts` and `
|
|
183
|
+
- Coverage is measured over `host.ts`, `lib/**` and `src/ui-messages.ts`, with all four thresholds at 100%. The dictionary is plain data driven headlessly by `test/client-card.test.ts`, so counting it keeps the threshold on the part that is fully verified; `client-entry.ts` is excluded because most of it is the render surface, which needs a DOM harness this package deliberately omits (`react-dom`/`jsdom` are not pulled in). Counting it would turn the thresholds into a permanent red.
|
|
184
184
|
- `test/publish-manifest.test.ts` enforces the publishing shape: entry points resolve, no `@deepseek-ai/*` under `dependencies`, no dates or local paths in the artifacts, with comments and documents scanned the same way. `test/dist-size.test.ts` guards the byte budget of both artifacts; `check:pack` inspects a real `pnpm pack` tarball.
|
|
185
185
|
|
|
186
186
|
## <img src="https://api.iconify.design/lucide/scale.svg?color=%23658EFF&height=22" height="22" width="22" align="absmiddle" alt="" /> License
|
package/README.zh-CN.md
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
<a href="https://www.npmjs.com/package/@jayyuen66/dsh-quality-gate"><img src="https://img.shields.io/npm/d18m/@jayyuen66/dsh-quality-gate?style=flat-square" alt="npm downloads (total)" /></a>
|
|
12
12
|
<a href="https://github.com/JayYuen666/dsh-quality-gate/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/JayYuen666/dsh-quality-gate/ci.yml?branch=main&style=flat-square&label=ci" alt="ci" /></a>
|
|
13
13
|
<img src="https://img.shields.io/badge/license-MIT-4c1?style=flat-square" alt="MIT" />
|
|
14
|
-
<img src="https://img.shields.io/badge/node-%3E%
|
|
15
|
-
<img src="https://img.shields.io/badge/dsh-%5E0.2.1--rc.
|
|
14
|
+
<img src="https://img.shields.io/badge/node-%3E%3D24.9-4c1?style=flat-square&logo=nodedotjs&logoColor=white" alt="node" />
|
|
15
|
+
<img src="https://img.shields.io/badge/dsh-%5E0.2.1--rc.5-658EFF?style=flat-square" alt="dsh" />
|
|
16
16
|
<a href="https://github.com/deepseek-ai/deepseek-harness"><img src="https://img.shields.io/badge/powered_by_dsh-4D6BFE?style=flat-square&logo=deepseek&logoColor=white" alt="powered by dsh" /></a>
|
|
17
17
|
</p>
|
|
18
18
|
|
|
@@ -61,8 +61,8 @@ dsh plugin --profile web add @jayyuen66/dsh-quality-gate
|
|
|
61
61
|
|
|
62
62
|
卸载:`dsh plugin --profile web remove @jayyuen66/dsh-quality-gate`。包在公共 npm 上,安装不需要凭据,也不需要配作用域源。
|
|
63
63
|
|
|
64
|
-
- **要求 Node.js `>=
|
|
65
|
-
- **要求宿主 `dsh` 在 `0.2.1-rc.3` 这条线上。** 宿主读取的版本要求是 `engines.dsh`(`^0.2.1-rc.3`),它**单独决定**兼容性;预发布版本参与范围比较,`0.3.0` 及以后要走显式担责的授权 `dsh plugin --profile <p> allow-version <pkg>@<ver> --dsh-version <exact> --accept-risk
|
|
64
|
+
- **要求 Node.js `>= 24.9`。** 产物用到 `AbortSignal.any` 这类较新的运行面。
|
|
65
|
+
- **要求宿主 `dsh` 在 `0.2.1-rc.5` 或 `0.2.2-alpha.3` 这条线上。** 宿主读取的版本要求是 `engines.dsh`(`^0.2.1-rc.5 || ^0.2.2-alpha.3`),它**单独决定**兼容性;预发布版本参与范围比较,`0.3.0` 及以后要走显式担责的授权 `dsh plugin --profile <p> allow-version <pkg>@<ver> --dsh-version <exact> --accept-risk`。支持窗口是滚动的:本包在发布时把窗口内的每个已发布宿主版本逐版取证,窗口滚动后重新取证,不把这一句里的版本号当长期承诺。
|
|
66
66
|
- **运行时依赖 `@jayyuen66/dsh-plugin-shared` 必须已在同一 registry 上**,否则整组 404。
|
|
67
67
|
- **`typescript` 是真实的 `dependencies`**:`tsc` 门禁的兜底入口——工程自己装的那份优先,其次随包安装的这份,运行期无网络动作,从不走 `npx` 的自动下载。
|
|
68
68
|
- **`zod` 也是真实的 `dependencies`**:编辑台账投影状态与设置卡的值面/草稿面用它校验形状;版本走伞根 catalog。client 产物按宿主 `PLATFORM_MODULES` 保持 `require("zod")` 外部化。
|
|
@@ -93,7 +93,7 @@ dsh plugin --profile web add @jayyuen66/dsh-quality-gate
|
|
|
93
93
|
|
|
94
94
|
## <img src="https://api.iconify.design/lucide/plug.svg?color=%23658EFF&height=22" height="22" width="22" align="absmiddle" alt="" /> 在 dsh 里启用
|
|
95
95
|
|
|
96
|
-
- 组合包形态:`package.json` 的 `dsh.bundle.patch` 指向 `cordis.patch.yml
|
|
96
|
+
- 组合包形态:`package.json` 的 `dsh.bundle.patch` 指向 `cordis.patch.yml`,里面是**两条条目**:`- name: "@jayyuen66/dsh-plugin-shared"`(卡片组件的供应行,**刻意不写 `id`**,所以插件页不会把它列成可停用组件)与 `- id: quality-gate`、`name` 为裸包名(宿主只对裸包名条目扫 `dsh.client`);`dsh plugin --profile web add/remove` 维护这两行。
|
|
97
97
|
- Host 半硬依赖只有 `settings`(`default.inject = ["settings"]`);优先级是 设置卡运行时值 > 行 `config:` > schema 内置默认。
|
|
98
98
|
- Client 半(`dsh.client.platform = web`、`immediately: true`)往插件管理页的 `plugins.bundle.config` 槽注入一张卡。两枚标识刻意不同源:槽位 key 是 **bundle 包名** `@jayyuen66/dsh-quality-gate`,设置命名空间与 `configForms.get()` 用的是**裸条目 id** `quality-gate`——写混了卡片哪都不渲染。
|
|
99
99
|
- 卡的保存是显式两段式:改动先留在本地暂存(状态条提示),点「保存」才逐键**串行**写入并消费 `Promise<boolean>` 受理位——首个 `false` 即停手、显示 `saveRejected` 并保留未落盘的改动;写入 reject 显示 `saveFailed`。保存期间输入框仍可编辑,期间产生的新改动保留在待保存面里,不会被这次保存顺带抹掉;「撤销」丢弃暂存。清空一个输入框 = unset,恢复该字段的 schema 默认。
|
|
@@ -105,10 +105,10 @@ dsh plugin --profile web add @jayyuen66/dsh-quality-gate
|
|
|
105
105
|
|
|
106
106
|
| 命令 | 什么时候发 |
|
|
107
107
|
| --- | --- |
|
|
108
|
-
| `pnpm check` | 根同时有 `pnpm-workspace.yaml` 与 `package.json`,且根 scripts 声明了非空 `check
|
|
108
|
+
| `pnpm check` | 根同时有 `pnpm-workspace.yaml` 与 `package.json`,且根 scripts 声明了非空 `check`。workspace 根没声明 `check` 时一条门禁都不发——它不会降级到 `tsc`(见下) |
|
|
109
109
|
| `cargo check` | 根有 `Cargo.toml` |
|
|
110
110
|
| `ruff check .` | 根有 `ruff.toml`,或 `pyproject.toml` 里有 `[tool.ruff]` 一族段。只有 `pyproject.toml` 不算——那是 Python 元数据文件,不是「用 ruff」的声明 |
|
|
111
|
-
| `tsc --noEmit` | 根同时有 `package.json` 与 `tsconfig.json`;缺 `tsconfig.json` 不算 TS
|
|
111
|
+
| `tsc --noEmit` | **非 workspace** 根同时有 `package.json` 与 `tsconfig.json`;缺 `tsconfig.json` 不算 TS 项目。workspace 根的 tsconfig 多为 solution-style(`files: []` + `references`),`tsc --noEmit` 一个子工程都不检查就退 0,因此不在此列 |
|
|
112
112
|
|
|
113
113
|
- `tsc` 在命令里只是逻辑名:入口在运行期解析——工程自己装的 `typescript` 优先,其次随包那份,最后 PATH 上的 `tsc`(由「工具不可用」出口报成未执行);绝不走 `npx` 的自动下载。
|
|
114
114
|
- 根探测:从编辑文件向上最多 `maxRootDepth`(默认 10)层找最近清单根;子包**自己就是** workspace 根且能产出 `pnpm check` 时就地执行(改一个包不得把整棵伞工作区拖进来);否则 monorepo 子包升到 `pnpm-workspace.yaml` 所在的工作区根——但只在该根**产得出门禁**时升级,再否则保留子包自己的根(外层没有 `check` 脚本的 workspace 不得吃掉子包的 `tsc` / `ruff` 门禁)。
|
|
@@ -149,17 +149,17 @@ dsh plugin --profile web add @jayyuen66/dsh-quality-gate
|
|
|
149
149
|
| --- | --- |
|
|
150
150
|
| 默认零外发 | `memoryFeedback` 关 + 地址空是双保险,任一为空都不推送、也不去查凭据 |
|
|
151
151
|
| 出域内容 | 开启后只把命令、项目根与全文报错(单行化、不截断)发往**你自己配的**网关 |
|
|
152
|
-
| 地址与凭据安全 | 凭据只发 `https` 或回环地址(`
|
|
152
|
+
| 地址与凭据安全 | 凭据只发 `https` 或回环地址(`127.0.0.0/8`、IPv6 回环、`localhost`);其中 `localhost` 依赖本机 hosts/DNS,属有意的 fail-open——hosts 被指向远端时凭据会发往远端。明文 `http` 非回环按未配置拒绝并说明理由。地址解析不出合法 `http(s)` URL 同样按未配置;带 userinfo 的地址一律拒绝 |
|
|
153
153
|
| 密钥 | 值由宿主凭据服务持有(`$DSH_HOME` 下的凭据文件),设置里只存引用名;引用名不合 POSIX 变量名语法即视为未配置 |
|
|
154
|
-
| 失败处理 | 推送失败只 warn
|
|
154
|
+
| 失败处理 | 推送失败只 warn、绝不阻断收口:配置缺失/地址不安全的降级每次装载只说一次;HTTP 非 2xx、超时与投递异常则每次失败各记一条(它们不该被去重掩盖) |
|
|
155
155
|
| 读盘 | 清单存在性、根 `package.json` 的 scripts、`pyproject.toml` 的段头、截断落盘文件的头部;不写任何文件、不遥测 |
|
|
156
156
|
| 内存 | 会话级账本(编辑记账/注入配额/事件游标)都在进程内存,插件卸载即清 |
|
|
157
157
|
|
|
158
158
|
## <img src="https://api.iconify.design/lucide/triangle-alert.svg?color=%23658EFF&height=22" height="22" width="22" align="absmiddle" alt="" /> 已知边界
|
|
159
159
|
|
|
160
160
|
- **白名单只有四条命令,不能经设置扩展。** 用别的检查器(`go test`、`mypy`、`eslint`…)的根得不到门禁。
|
|
161
|
-
- **只记成功编辑**,且只认 `edit` / `write` / `str_replace_editor` 的写入(`view`
|
|
162
|
-
- **out-of-process 子代理(ACP / claude-code / codex)不发 dsh 事件**,天然不在门禁面内;in-process
|
|
161
|
+
- **只记成功编辑**,且只认 `edit` / `write` / `str_replace_editor` / `apply_patch` 的写入(`view` 只读不算)。多文件 `apply_patch` 按每个操作目标分别记账,move 的源与目的都算。扩展名白名单之外的文件(md/txt/图片/`.sh`)不计——没有一条门禁命令能校验它们,`.sh` 只会把根解析到别处跑出假失败。
|
|
162
|
+
- **out-of-process 子代理(ACP / claude-code / codex)不发 dsh 事件**,天然不在门禁面内;in-process 子代理的回合也跳过门禁,且其编辑落在各自会话的台账,不进入父会话的编辑台账——当前不保证被父会话核验(可能漏检)。
|
|
163
163
|
- **不抢话**:inbox 已有待处理消息、回合信号已取消或插件已卸载时不注入。plan mode 不豁免——规划回合改了代码同样收口。
|
|
164
164
|
- **证据被截断且没有落盘文件时,首个错误可能已随截断丢弃**;文案如实说明证据不完整,不假装完整。
|
|
165
165
|
- **沙箱策略解析失败时一门都不执行**(按未执行注入),绝不回落部署级策略——那会让门禁写在沙箱外被拒、产出假的失败指令。
|
|
@@ -168,21 +168,21 @@ dsh plugin --profile web add @jayyuen66/dsh-quality-gate
|
|
|
168
168
|
|
|
169
169
|
| 问题 | 回答 |
|
|
170
170
|
| --- | --- |
|
|
171
|
-
| pnpm 工程为什么没跑 `pnpm check`? |
|
|
171
|
+
| pnpm 工程为什么没跑 `pnpm check`? | 根必须同时有 `pnpm-workspace.yaml` 与 `package.json`,且根 scripts 声明了非空 `check` 脚本;缺 `pnpm-workspace.yaml` 时按下一候选判(非 workspace 根可能落到 `tsc` / `ruff` / `cargo`),而 workspace 根没声明 `check` 时一条门禁都不发(solution-style tsconfig 会让 `tsc` 恒绿)。刻意取舍:假失败比漏检查更贵 |
|
|
172
172
|
| 门禁说「未能执行」,是代码错了吗? | 不是。沙箱策略拒绝、runner 起不来、超时、取消、预算不足,以及 stderr **点名门禁命令自身**(工具没装)的 exit 126/127 都属此类。注入文案明确要求**不要为此修改代码**,并以 `gate-not-run` 单独上报总线。若 126/127 来自你自己的 `check` 脚本内部,那是代码失败,按代码失败上报 |
|
|
173
173
|
| 会不会无限修下去? | 配额耗尽后注入一次用户可见的放行说明并停止自动修复,直到某次全部干净通过才重新计数 |
|
|
174
174
|
| 临时停掉? | 设置卡关 `enabled`,或 `dsh plugin --profile web remove @jayyuen66/dsh-quality-gate` |
|
|
175
175
|
| `tsc` 用哪个版本? | 工程自己装的那份优先,其次随包安装的 `typescript`,都不在才回落 PATH——从不经 `npx` 下载 |
|
|
176
|
-
| 子代理改的代码呢? | in-process
|
|
176
|
+
| 子代理改的代码呢? | in-process 子代理的回合同样跳过门禁,其编辑落在各自会话的台账、不进入父会话台账——当前不保证被父会话核验(可能漏检);out-of-process 子代理不发 dsh 事件,天然不在门禁面内 |
|
|
177
177
|
|
|
178
178
|
## <img src="https://api.iconify.design/lucide/wrench.svg?color=%23658EFF&height=22" height="22" width="22" align="absmiddle" alt="" /> 开发
|
|
179
179
|
|
|
180
180
|
```sh
|
|
181
181
|
pnpm install
|
|
182
|
-
pnpm run check # typecheck
|
|
182
|
+
pnpm run check # typecheck(四个 tsconfig)→ lint → client 构建 → host 构建 → 测试(含覆盖率)→ tarball 体检 → 格式
|
|
183
183
|
```
|
|
184
184
|
|
|
185
|
-
-
|
|
185
|
+
- 覆盖率统计 `host.ts`、`lib/**` 与 `src/ui-messages.ts`,四项阈值 100%。字典是纯数据、`test/client-card.test.ts` 无头就能跑满,计进分母才能让阈值守住它;`client-entry.ts` 不计——它的大头是渲染面,要 DOM 基座才能测,本包刻意不引 `react-dom` 与 `jsdom`,整体计进来会让阈值变成常红。
|
|
186
186
|
- `test/publish-manifest.test.ts` 钉发布形态:入口可解析、`@deepseek-ai/*` 不进 `dependencies`、产物内容无日期与本机路径,注释与文档面同受其扫描。`test/dist-size.test.ts` 守两个产物的体积预算;`check:pack` 对 `pnpm pack` 的真 tarball 做体检。
|
|
187
187
|
|
|
188
188
|
## <img src="https://api.iconify.design/lucide/scale.svg?color=%23658EFF&height=22" height="22" width="22" align="absmiddle" alt="" /> 许可
|