Review whether a DSH plugin: declarations match artifacts, references to harness actually exist, contract pitfalls are avoided, documentation is in sync, and acceptance evidence can be bound to a candidate.
Development and paradigm basis:references/dsh-plugin-development-standards.md(plugin forms, distribution, tools/events/slot, client artifact contract, typed locale, theme variables, harness reference file index).
This skill does not replace the implementer. When creating a plugin, do a design pre-check first, and come back for formal acceptance after implementation—reviewer and producer do not share responsibility.
Supporting files:
scripts/lint.mjs→ §1 Mechanical layer (version-aware, rule list below)scripts/test-lint.mjs→ Self-test (self-contained fixture, proving each rule catches invalid samples and passes valid controls)config/harness-path.example.yaml→ Copy asconfig/harness-path.local.yamlfill in the local harness repository path (do not commit)templates/plugin-quality-report.md→ Report template for formal review (conclusions carry NOT_VERIFIED semantics)
Dependencies
- Node ≥ 18.6 (uses
node:module.isBuiltin), no third-party packages—scripts work out of the box, no installation required. - harness traceability requires a local DSH source repository (
gitwhen available, also report commit; if unavailable, this item is NOT_VERIFIED).
Working principles
- Mechanical first, then semantic: what scripts can check (declarations/artifacts/reconciliation) should not waste manual effort.
- Version-aware, do not freeze assumptions: platform module table, inline safety regexes, theme variables, and package manifest are all read from the current harness source declarations; version and commit go first through the official dsh executable gate (
apps/cli/lib/bin.js --version) + git traceability. All empirically tested conclusions in the specification documents must be annotated with the verified version. - Every API/event/slot reference to harness must be traced to a source path—do not trust documentation memory (empirical cases: documentation wrote a nonexistent CLI flag, a nonexistent
agent/post-stepevent, an already-removeddsh-client-runtime)。 - Do not trust self-reported PASS; formal acceptance must bind the boot evidence of the current commit + browser rendering evidence.
- Objective defects fail-closed;semantic uncertainty only yields WARN / NOT_VERIFIED, never a false PASS。
harness root resolution
lint.mjs Resolve the DSH source repository (harness root) by the following priority:
--harness-root <路径>parameter- Environment variable
DSH_HARNESS_ROOT config/harness-path.local.yaml(inside the skill directory, do not commit)
When all three are missing, mark version-related check items as NOT_VERIFIED(the process does not crash, other checks continue). After successful resolution, report: DSH version (official gate + root manifest, WARN when the two are inconsistent), git commit, number of platform module table entries. All harness dependency checks are based on that version.
Modes
| Mode | Timing | Required |
|---|---|---|
| Design pre-check | Before writing code | Go through the contract section of the development specification; trace event/slot names first |
| Quick review | Third-party/draft | §1 Mechanical + §2 Facts + §3 Contract; conclusions carry NOT_VERIFIED |
| Formal acceptance | Before releasing/claiming completion | All + §5 candidate binding evidence |
§1 Mechanical layer (run scripts/lint.mjs)
node skills/dsh-plugin-lint/scripts/lint.mjs <插件目录> --harness-root <DSH 源码仓库>
node skills/dsh-plugin-lint/scripts/test-lint.mjs # 自测,退出码 0 = 全部规则自证有效
--json will append a line at the end of the document __DSH_LINT_JSON__{...} machine-readable result. Exit code = number of FAILs.
| Section | Check content |
|---|---|
| §0 harness provenance | Version (official gate + manifest cross-check), git commit, platform module table/purity regex parsability, config baseline drift |
| §1 package.json | name resolution;@deepseek-ai/dsh-* dependency pinned version vs current harness version drift |
| §2 dsh.bundle | patch file exists, row references package name |
| §3 exports["."] | node half entry exists |
| §4 dsh.client | platform=web; inject target declared in harness workspace / local node_modules dsh.client(otherwise that edge will never reach the factory, FAIL); inject ↔ devDep type double insurance (WARN);dsh.client.external field type, self-reference (FAIL), baseline redundant line (WARN) |
| §5 client artifact | existence,.cjs suffix, banner/footer contract;artifact bare require ↔ module table reconciliation(comment-aware scan: unknown specifier FAIL — module table cannot respond and will definitely throw; inlined safety layer externalized FAIL; bare builtin require WARN — must be manually confirmed to be within inliner feature detection);build config external ↔ platform baseline ∪ dsh.client.external reconciliation(out-of-bounds FAIL, externals declaration body not found WARN escalated to manual); external dead line (WARN) |
| §6 theme variables | Perform declaration set reconciliation against var(--…) references (declaration sources: harness ui-theme/src/styles/*.css + plugin's own declarations): nonexistent DSH variable FAIL (fallback value would mask theme breakage); unused variables not reported |
| §7 Copy and locale | Hardcoded CJK copy in JSX text/user-visible attributes FAIL; ordinary CJK string literal WARN;locales.ts(or locales/zh.ts+en.ts) zh/en key set parity FAIL |
| §8 Hygiene | scoped package publishConfig.access, scripts.build/test |
| §9 Documentation version declarations | Frozen 0.1.0-rc.x references and removed module dsh-client-runtime references (WARN) in current-state documentation (README/AGENTS/CLAUDE/docs, excluding CHANGELOG and acceptance evidence) |
FAIL blocks. WARN and NOT_VERIFIED do not block, but the formal acceptance report must give a manual conclusion for each item.
§2 Fact layer (verified item by item against harness source code)
| Review item | Provenance method |
|---|---|
| Event name exists and semantics are correct | packages/core/agent/src/runtime-types.ts(note:does not have agent/post-step) |
| slot name has actually been declared | packages/client/ui-*/src grep in slots.inject(' / renderSlot('; slot'stype declarationin the owning client package, out-of-tree requires devDep (types) + dsh.client.inject(load order) double insurance |
| Tool API shape | docs/cookbook/adding-a-tool.md + packages/core/tools/src/index.ts |
| lossless JSON validation | packages/util/values/src/index.ts(walkJsonValue; old path packages/core/session/src/json.ts no longer exists) |
| Lifecycle/service | ctx.get(optional) vs inject(hard dependency, if missing the entire plugin will not activate) |
For the harness repository path and full index, see the development spec §Reference File Index. When verifying, use --harness-root the referenced repository as authoritative, and record its version and commit in the report.
§3 Contract layer (pitfalls actually hit in practice, check item by item)
- lossless JSON: any property value of a tool return value that is
undefinedis rejected (recursively)—deep-strip before returning. - DSL restrictions:
parametersdoes not support object-nested objects; standalone const schema literals mustas const。 - client artifacts: the banner must be constructed
var module = { exports: {} }; var exports = module.exports;;"type":"module"in-package cjs artifacts mustoutExtensionsforce.js; artifact bare require may only request platform baseline ordsh.client.externaldeclared specifiers. - client purity: non-baseline, non-
dsh.client.external, non-inline safe-list@deepseek-ai/*value imports are forbidden (type-only is erased and not restricted)—the build purity gate will error out directly. - Subprocess: long tasks async
spawn+exec.signal;spawnSyncfreezes the entire harness. - Explicit arguments: do not rely on the called CLI's silent defaults (a real case: contract-copilot's review_intensity default silently becomes "assertive").
- fail-loud: configuration errors throw inside apply; optional service defaults must explicitly state the degradation path.
- Exit code classification: domain results (partial/rejected) go into the canonical value without throwing; only infrastructure failures throw.
- Build pipeline: do not use pipes that swallow exit codes (
cmd | grep | head && nextonce made a tsc failure silent, leaving a stale bundle on disk for hours). - HMR boundary (historical conclusion, re-verify per version): in the 0.1.0-rc.7 era, bidirectional testing showed that neither half of an out-of-tree plugin hot-loads, and after an update dsh must be restarted; this conclusion has not been retested in later versions—treat it as a to-be-verified hypothesis during review (WARN semantics), and take the current harness's
packages/client/hmrbehavior actually measured as authoritative. - Theme variables: only reference variables from the harness theme declaration set; fallback values (
var(--x, #fff)) are only a last-resort display and cannot hide a broken dark-theme chain. - Typed locale: product UI copy goes through the plugin's own namespace dictionary (zh is the key-set source of truth, en aligned), and components consume it via
PropsLocale<'ns'>'st(); writing copy directly in JSX text/attributes is a violation.
§4 Documentation layer
The target plugin repository's CHANGELOG / DECISIONS / ROADMAP / ARCHITECTURE are in sync with this change; decisions are traceable (consecutive numbering, including retractions). Current-state docs must not retain expired version declarations (§1 §9 will provide a WARN list).
§5 Formal acceptance (candidate binding)
git rev-parse HEADrecord the candidate commit- Clean profile install:
dsh plugin --profile <验收名> add <路径>→--dump-configlayered - node half: boot without errors; tool successfully invoked for real in headless (full pipeline if an LLM is available, single-tool otherwise)
- client half (if any):
__DSH_BOOT__contains this package →/plugins/<id>/client.jsdownloadable → browser rendered screenshot(slot component actually appears) - Evidence (commit + command output + screenshot) written into the acceptance report; missing any →
NOT_VERIFIED
Standard browser forensics tool: ego-browser(agent browser, task space isolation, reuses local login state). For acceptance use ego-browser nodejs <<'EOF' ... EOF driver:gotoAndWait open local web UI → snapshotText/js perform DOM assertions (slot component exists, key content rendered) → captureScreenshot retain evidence. localhost testing requires no public network or extra login; faster than manual clicking and evidence is reviewable. When no ego-browser environment is available, accept manually and annotate the evidence source.
Output format
dsh-plugin-lint 报告 <目标> @ <commit>
harness 溯源: <DSH 版本> @ <harness commit>(解析来源)
§1 机械: [PASS|FAIL|WARN|NOT_VERIFIED...](脚本输出,退出码 = FAIL 数)
§2 事实: 每条引用 → 源码路径 → PASS/FAIL
§3 契约: 逐条 PASS/FAIL/NA
§4 文档: ...
§5 验收: PASS / NOT_VERIFIED(缺什么证据)
结论: 可发布 / 需修复(清单)/ 未验证
Known limitations
- Regex-level parsing, no AST construction: complex patch structures, nested dictionaries, non-flat locale objects are handed off to manual review (for cases the script cannot parse it emits WARN/NOT_VERIFIED, not PASS)
- Artifact bare builtin require can only prompt manual confirmation (the inliner's try/catch feature detection is legitimate; cannot be mechanically determined)
- CJK detection in JSX text positions is heuristic (
>…<interval contains CJK), does not include{}expression internals - The platform module table/purity regex/theme variable set/package manifest are all current facts read at runtime from
--harness-root; they are not distributed with this skill, nor is any version number preset - Does not automatically execute the target repository's build/test (reports the commands that should be run, to prevent accidental damage)