dsh-standards 1.2.2 → 1.4.0
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/40-checker/README.md +18 -1
- package/40-checker/scripts/build-offline-kit.mjs +284 -0
- package/40-checker/scripts/build-sidecar.mjs +714 -0
- package/40-checker/scripts/flow-collect.mjs +7 -1
- package/40-checker/scripts/level-audit.mjs +45 -7
- package/40-checker/scripts/run-tests.mjs +3 -7
- package/40-checker/src/author-cli.mjs +406 -0
- package/40-checker/src/author-report.mjs +271 -0
- package/40-checker/src/bundle-static.mjs +181 -0
- package/40-checker/src/cli.mjs +100 -11
- package/40-checker/src/client-shape.mjs +33 -3
- package/40-checker/src/declarations.mjs +53 -8
- package/40-checker/src/flow-evidence.mjs +106 -1
- package/40-checker/src/index.mjs +6 -4
- package/40-checker/src/manifest.mjs +87 -14
- package/40-checker/src/remediation.mjs +108 -7
- package/40-checker/src/report.mjs +203 -26
- package/40-checker/src/semver.mjs +112 -65
- package/40-checker/src/util.mjs +51 -8
- package/CHANGELOG.md +44 -0
- package/README.en.md +7 -2
- package/README.es.md +7 -2
- package/README.hi.md +7 -2
- package/README.md +35 -18
- package/README.pt.md +7 -2
- package/bin/dsh-standards.mjs +13 -2
- package/docs/assets/dsh-standards-flow-v3-dark.svg +87 -0
- package/docs/assets/dsh-standards-flow-v3-light.svg +87 -0
- package/docs/assets/dsh-standards-flow-v4-dark.svg +79 -0
- package/docs/assets/dsh-standards-flow-v4-light.svg +79 -0
- package/docs/assets/dsh-standards-hero-en-dark.webp +0 -0
- package/docs/assets/dsh-standards-hero-en-light.webp +0 -0
- package/docs/assets/dsh-standards-hero-zh-dark.webp +0 -0
- package/docs/assets/dsh-standards-hero-zh-light.webp +0 -0
- package/docs/coverage.md +82 -0
- package/package.json +1 -1
- package/spec/README.md +2 -0
- package/spec/coverage-contract.md +100 -0
- package/spec/dsc-clauses.json +766 -0
- package/spec/examples/boost-card.example.json +15 -0
- package/spec/examples/flow-evidence.example.json +113 -0
- package/spec/flow-evidence.schema.json +6 -2
- package/spec/remediation.json +54 -54
- package/spec/schemas/advice.schema.json +92 -0
- package/spec/schemas/boost-card.schema.json +107 -0
- package/spec/schemas/conformance.schema.json +848 -0
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Minimal SemVer range matcher for the checker.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* The host evaluates plugin requirements with
|
|
5
5
|
* `semver.satisfies(runtime, range, { includePrerelease: true })`
|
|
6
|
-
* (deepseek-harness/packages/boot/app-boot/src/plugin-compatibility.ts
|
|
7
|
-
* This module implements the subset that appears in package manifests
|
|
8
|
-
* exact versions, `>= > <=
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
6
|
+
* (deepseek-harness/packages/boot/app-boot/src/plugin-compatibility.ts).
|
|
7
|
+
* This module implements the subset that appears in package manifests and in
|
|
8
|
+
* npm documentation: exact versions, `>= > <= < =`, `^`, `~`, `*`/`x`,
|
|
9
|
+
* space-separated AND (including `>= 0.2.2-rc.1`, where npm allows whitespace
|
|
10
|
+
* between an operator and its version), `||` alternatives, hyphen ranges and
|
|
11
|
+
* partial/x-ranges. Prereleases participate in ranges everywhere, matching the
|
|
12
|
+
* host's `includePrerelease: true`.
|
|
13
|
+
*
|
|
14
|
+
* It does NOT implement `!=` or npm's "prerelease only matches the same
|
|
15
|
+
* [major,minor,patch] tuple" exclusion: the host explicitly includes
|
|
16
|
+
* prereleases, so the checker must use the same rule.
|
|
12
17
|
*/
|
|
13
18
|
|
|
14
19
|
const VERSION_RE = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/
|
|
20
|
+
const OPERATORS = new Set(['>=', '<=', '>', '<', '=', '^', '~'])
|
|
15
21
|
|
|
16
22
|
export function parseVersion(value) {
|
|
17
23
|
if (typeof value !== 'string') return null
|
|
@@ -53,18 +59,26 @@ export function compareVersions(left, right) {
|
|
|
53
59
|
return 0
|
|
54
60
|
}
|
|
55
61
|
|
|
62
|
+
// npm encodes an exclusive upper bound as `X.Y.Z-0`, which also excludes
|
|
63
|
+
// prereleases of the boundary version. This matters for `^0.2.2-alpha.2`:
|
|
64
|
+
// 0.3.0-0 must NOT satisfy it, while `<0.3.0` (an explicit user range) does.
|
|
65
|
+
function sentinel(version) {
|
|
66
|
+
return { ...version, prerelease: ['0'] }
|
|
67
|
+
}
|
|
68
|
+
|
|
56
69
|
function upperForCaret(version) {
|
|
57
|
-
if (version.major > 0) return { ...version, major: version.major + 1, minor: 0, patch: 0
|
|
58
|
-
if (version.minor > 0) return { ...version, minor: version.minor + 1, patch: 0
|
|
59
|
-
return { ...version, patch: version.patch + 1
|
|
70
|
+
if (version.major > 0) return sentinel({ ...version, major: version.major + 1, minor: 0, patch: 0 })
|
|
71
|
+
if (version.minor > 0) return sentinel({ ...version, minor: version.minor + 1, patch: 0 })
|
|
72
|
+
return sentinel({ ...version, patch: version.patch + 1 })
|
|
60
73
|
}
|
|
61
74
|
|
|
62
75
|
function upperForTilde(version) {
|
|
63
|
-
return { ...version, minor: version.minor + 1, patch: 0
|
|
76
|
+
return sentinel({ ...version, minor: version.minor + 1, patch: 0 })
|
|
64
77
|
}
|
|
65
78
|
|
|
79
|
+
/** A version with optional missing minor/patch and optional prerelease. */
|
|
66
80
|
function parsePartial(text) {
|
|
67
|
-
const match = /^v?(\d+)(?:\.(\d+|[xX*]))?(?:\.(\d+|[xX*]))?(?:-([0-9A-Za-z.-]+))?$/.exec(text.trim())
|
|
81
|
+
const match = /^v?(\d+)(?:\.(\d+|[xX*]))?(?:\.(\d+|[xX*]))?(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(text.trim())
|
|
68
82
|
if (!match) return null
|
|
69
83
|
return {
|
|
70
84
|
major: Number(match[1]),
|
|
@@ -77,76 +91,112 @@ function parsePartial(text) {
|
|
|
77
91
|
function fullVersion(partial) {
|
|
78
92
|
return { major: partial.major, minor: partial.minor ?? 0, patch: partial.patch ?? 0, prerelease: partial.prerelease }
|
|
79
93
|
}
|
|
80
|
-
function nextMinor(partial) { return { major: partial.major, minor: (partial.minor ?? 0) + 1, patch: 0, prerelease: [] } }
|
|
81
|
-
function nextMajor(partial) { return { major: partial.major + 1, minor: 0, patch: 0, prerelease: [] } }
|
|
94
|
+
function nextMinor(partial) { return { major: partial.major, minor: (partial.minor ?? 0) + 1, patch: 0, prerelease: ['0'] } }
|
|
95
|
+
function nextMajor(partial) { return { major: partial.major + 1, minor: 0, patch: 0, prerelease: ['0'] } }
|
|
82
96
|
function fmt(version) {
|
|
83
97
|
const base = `${version.major}.${version.minor}.${version.patch}`
|
|
84
98
|
return version.prerelease && version.prerelease.length ? `${base}-${version.prerelease.join('.')}` : base
|
|
85
99
|
}
|
|
86
100
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
patch: match[4] === undefined || /[xX*]/.test(match[4]) ? null : Number(match[4]),
|
|
96
|
-
prerelease: match[5] ? match[5].split('.') : [],
|
|
101
|
+
/**
|
|
102
|
+
* Expand one npm comparator (`^1.2`, `>= 0.2.2-rc.1`, `1.x`, ...) into the
|
|
103
|
+
* primitive {@link satisfiesToken} comparators. Returns null when the token is
|
|
104
|
+
* not a range this checker can evaluate.
|
|
105
|
+
*/
|
|
106
|
+
function comparatorsForToken(op, versionText) {
|
|
107
|
+
if (versionText === '*' || versionText === 'x' || versionText === 'X') {
|
|
108
|
+
return op === '=' || op === undefined ? [] : null
|
|
97
109
|
}
|
|
110
|
+
const partial = parsePartial(versionText)
|
|
111
|
+
if (!partial) return null
|
|
112
|
+
const normalized = op ?? '='
|
|
113
|
+
if (!OPERATORS.has(normalized)) return null
|
|
98
114
|
const low = fullVersion(partial)
|
|
99
115
|
const noMinor = partial.minor === null
|
|
100
116
|
const noPatch = partial.patch === null
|
|
101
|
-
|
|
102
|
-
|
|
117
|
+
|
|
118
|
+
if (!noMinor && !noPatch) {
|
|
119
|
+
const exact = parseVersion(fmt(low))
|
|
120
|
+
if (!exact) return null
|
|
121
|
+
if (normalized === '^') return [{ op: '>=', version: exact }, { op: '<', version: upperForCaret(low) }]
|
|
122
|
+
if (normalized === '~') return [{ op: '>=', version: exact }, { op: '<', version: upperForTilde(low) }]
|
|
123
|
+
return [{ op: normalized, version: exact }]
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Partial / x-range. Under includePrerelease npm gives the *lower* bound a
|
|
127
|
+
// `-0` sentinel too, so `^1.2` matches 1.2.0-0 while `>=1.2.3` does not.
|
|
128
|
+
const lowSentinel = sentinel(low)
|
|
129
|
+
switch (normalized) {
|
|
103
130
|
case '^':
|
|
131
|
+
// ^1.2 -> <2.0.0-0, ^0.2 -> <0.3.0-0, ^0.0 -> <0.1.0-0
|
|
132
|
+
return noMinor || partial.major > 0
|
|
133
|
+
? [{ op: '>=', version: lowSentinel }, { op: '<', version: nextMajor(partial) }]
|
|
134
|
+
: [{ op: '>=', version: lowSentinel }, { op: '<', version: nextMinor(partial) }]
|
|
104
135
|
case '~':
|
|
136
|
+
return noMinor
|
|
137
|
+
? [{ op: '>=', version: lowSentinel }, { op: '<', version: nextMajor(partial) }]
|
|
138
|
+
: [{ op: '>=', version: lowSentinel }, { op: '<', version: nextMinor(partial) }]
|
|
105
139
|
case '=':
|
|
106
|
-
return noMinor
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
case '
|
|
110
|
-
case '
|
|
140
|
+
return noMinor
|
|
141
|
+
? [{ op: '>=', version: lowSentinel }, { op: '<', version: nextMajor(partial) }]
|
|
142
|
+
: [{ op: '>=', version: lowSentinel }, { op: '<', version: nextMinor(partial) }]
|
|
143
|
+
case '>=': return [{ op: '>=', version: lowSentinel }]
|
|
144
|
+
case '>': return noMinor ? [{ op: '>=', version: nextMajor(partial) }] : [{ op: '>=', version: nextMinor(partial) }]
|
|
145
|
+
case '<': return [{ op: '<', version: lowSentinel }]
|
|
146
|
+
case '<=': return noMinor ? [{ op: '<', version: nextMajor(partial) }] : [{ op: '<', version: nextMinor(partial) }]
|
|
111
147
|
default: return null
|
|
112
148
|
}
|
|
113
149
|
}
|
|
114
150
|
|
|
115
|
-
/**
|
|
151
|
+
/** Split a `||` alternative into comparator chunks, allowing `>= 1.2.3`. */
|
|
152
|
+
function tokenizeComparators(text) {
|
|
153
|
+
const chunks = []
|
|
154
|
+
let rest = text
|
|
155
|
+
while (rest.trim() !== '') {
|
|
156
|
+
rest = rest.replace(/^\s+/, '')
|
|
157
|
+
const match = /^(>=|<=|>|<|=|\^|~)?\s*([^\s,]+)\s*/.exec(rest)
|
|
158
|
+
if (!match) return null
|
|
159
|
+
const op = match[1]
|
|
160
|
+
const versionText = match[2]
|
|
161
|
+
// An operator with no following version (`>=` alone) is malformed; the
|
|
162
|
+
// regex above would otherwise read the next operator as a version.
|
|
163
|
+
if (!op && OPERATORS.has(versionText)) return null
|
|
164
|
+
chunks.push({ op, versionText })
|
|
165
|
+
rest = rest.slice(match[0].length)
|
|
166
|
+
}
|
|
167
|
+
return chunks.length > 0 ? chunks : null
|
|
168
|
+
}
|
|
169
|
+
|
|
116
170
|
function expandAlternative(alternative) {
|
|
117
171
|
const text = alternative.trim()
|
|
118
|
-
|
|
119
|
-
|
|
172
|
+
// An empty alternative is npm's `*` (node-semver treats `1.2.3 ||` as
|
|
173
|
+
// `*`). The wholly-empty range is rejected by the callers below.
|
|
174
|
+
if (text === '' || text === '*' || text === 'x' || text === 'X') return []
|
|
175
|
+
const hyphen = /^(\S+)\s+-\s+(\S+)$/.exec(text)
|
|
120
176
|
if (hyphen) {
|
|
121
177
|
const left = parsePartial(hyphen[1])
|
|
122
178
|
const right = parsePartial(hyphen[2])
|
|
123
179
|
if (!left || !right) return null
|
|
124
|
-
const low =
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if (
|
|
133
|
-
|
|
180
|
+
const low = fullVersion(left)
|
|
181
|
+
// The left side is a floor: a concrete version keeps its exact prerelease,
|
|
182
|
+
// while a partial version gets the same -0 sentinel npm uses.
|
|
183
|
+
const lowToken = left.prerelease.length > 0
|
|
184
|
+
? { op: '>=', version: low }
|
|
185
|
+
: { op: '>=', version: sentinel(low) }
|
|
186
|
+
if (right.minor === null) return [lowToken, { op: '<', version: nextMajor(right) }]
|
|
187
|
+
if (right.patch === null) return [lowToken, { op: '<', version: nextMinor(right) }]
|
|
188
|
+
if (right.prerelease.length === 0) {
|
|
189
|
+
return [lowToken, { op: '<', version: sentinel({ ...fullVersion(right), patch: fullVersion(right).patch + 1 }) }]
|
|
190
|
+
}
|
|
191
|
+
return [lowToken, { op: '<=', version: fullVersion(right) }]
|
|
134
192
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
function tokenizeAlternative(alternative) {
|
|
139
|
-
const expanded = expandAlternative(alternative)
|
|
140
|
-
if (expanded === null) return null
|
|
141
|
-
if (expanded === '*') return [{ op: '*', version: null }]
|
|
193
|
+
const chunks = tokenizeComparators(text)
|
|
194
|
+
if (!chunks) return null
|
|
142
195
|
const tokens = []
|
|
143
|
-
for (const
|
|
144
|
-
const
|
|
145
|
-
if (
|
|
146
|
-
|
|
147
|
-
const version = parseVersion(match[2])
|
|
148
|
-
if (!version) return null
|
|
149
|
-
tokens.push({ op, version })
|
|
196
|
+
for (const chunk of chunks) {
|
|
197
|
+
const expanded = comparatorsForToken(chunk.op, chunk.versionText)
|
|
198
|
+
if (expanded === null) return null
|
|
199
|
+
tokens.push(...expanded)
|
|
150
200
|
}
|
|
151
201
|
return tokens
|
|
152
202
|
}
|
|
@@ -160,8 +210,6 @@ function satisfiesToken(version, token) {
|
|
|
160
210
|
case '>=': return cmp >= 0
|
|
161
211
|
case '<': return cmp < 0
|
|
162
212
|
case '<=': return cmp <= 0
|
|
163
|
-
case '^': return cmp >= 0 && compareVersions(version, upperForCaret(token.version)) < 0
|
|
164
|
-
case '~': return cmp >= 0 && compareVersions(version, upperForTilde(token.version)) < 0
|
|
165
213
|
default: return false
|
|
166
214
|
}
|
|
167
215
|
}
|
|
@@ -175,10 +223,9 @@ export function satisfies(version, range) {
|
|
|
175
223
|
const parsed = parseVersion(version)
|
|
176
224
|
if (!parsed || typeof range !== 'string') return false
|
|
177
225
|
if (range.trim() === '') return false
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
if (!tokens || tokens.length === 0) continue
|
|
226
|
+
for (const alternative of range.split('||')) {
|
|
227
|
+
const tokens = expandAlternative(alternative)
|
|
228
|
+
if (!tokens) continue
|
|
182
229
|
if (tokens.every(token => satisfiesToken(parsed, token))) return true
|
|
183
230
|
}
|
|
184
231
|
return false
|
|
@@ -188,7 +235,7 @@ export function satisfies(version, range) {
|
|
|
188
235
|
export function validRange(range) {
|
|
189
236
|
if (typeof range !== 'string' || range.trim() === '') return false
|
|
190
237
|
for (const alternative of range.split('||')) {
|
|
191
|
-
if (!
|
|
238
|
+
if (!expandAlternative(alternative)) return false
|
|
192
239
|
}
|
|
193
240
|
return true
|
|
194
241
|
}
|
package/40-checker/src/util.mjs
CHANGED
|
@@ -56,21 +56,31 @@ const BUILD_FILE = /(?:^|\/)(?:build|esbuild|vite|rollup|webpack)\.config\.[cm]?
|
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
58
|
* Collect source files without following symlinks. Deterministic order, bounded.
|
|
59
|
+
* Returns the files plus scan metadata so callers can report truncation instead
|
|
60
|
+
* of pretending the bounded scan saw the whole package.
|
|
59
61
|
* @param {string} root
|
|
60
62
|
* @param {{maxFiles?: number, maxBytes?: number}} [options]
|
|
61
63
|
*/
|
|
62
|
-
export function
|
|
64
|
+
export function scanSourceFiles(root, options = {}) {
|
|
63
65
|
const maxFiles = options.maxFiles ?? 400
|
|
64
66
|
const maxBytes = options.maxBytes ?? 2_000_000
|
|
65
|
-
const
|
|
67
|
+
const paths = []
|
|
66
68
|
let bytes = 0
|
|
69
|
+
let truncated = false
|
|
70
|
+
let skippedForBytes = 0
|
|
67
71
|
const walk = (directory) => {
|
|
68
|
-
if (
|
|
72
|
+
if (paths.length >= maxFiles || bytes >= maxBytes) {
|
|
73
|
+
truncated = true
|
|
74
|
+
return
|
|
75
|
+
}
|
|
69
76
|
let entries
|
|
70
77
|
try { entries = fs.readdirSync(directory, { withFileTypes: true }) } catch { return }
|
|
71
78
|
entries.sort((a, b) => a.name.localeCompare(b.name))
|
|
72
79
|
for (const entry of entries) {
|
|
73
|
-
if (
|
|
80
|
+
if (paths.length >= maxFiles || bytes >= maxBytes) {
|
|
81
|
+
truncated = true
|
|
82
|
+
return
|
|
83
|
+
}
|
|
74
84
|
const absolute = path.join(directory, entry.name)
|
|
75
85
|
if (entry.isDirectory()) {
|
|
76
86
|
if (SKIP_DIRECTORIES.has(entry.name)) continue
|
|
@@ -84,24 +94,57 @@ export function listSourceFiles(root, options = {}) {
|
|
|
84
94
|
if (BUILD_FILE.test(relative)) continue
|
|
85
95
|
let size = 0
|
|
86
96
|
try { size = fs.statSync(absolute).size } catch { continue }
|
|
87
|
-
if (bytes + size > maxBytes)
|
|
97
|
+
if (bytes + size > maxBytes) {
|
|
98
|
+
// A file that does not fit is skipped; later smaller files may still
|
|
99
|
+
// fit, but the scan as a whole is no longer exhaustive.
|
|
100
|
+
truncated = true
|
|
101
|
+
skippedForBytes += 1
|
|
102
|
+
continue
|
|
103
|
+
}
|
|
88
104
|
bytes += size
|
|
89
|
-
|
|
105
|
+
paths.push(absolute)
|
|
90
106
|
}
|
|
91
107
|
}
|
|
92
108
|
walk(root)
|
|
93
|
-
return
|
|
109
|
+
return {
|
|
110
|
+
paths,
|
|
111
|
+
stats: {
|
|
112
|
+
files: paths.length,
|
|
113
|
+
bytes,
|
|
114
|
+
maxFiles,
|
|
115
|
+
maxBytes,
|
|
116
|
+
truncated,
|
|
117
|
+
skippedForBytes,
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function listSourceFiles(root, options = {}) {
|
|
123
|
+
return scanSourceFiles(root, options).paths
|
|
94
124
|
}
|
|
95
125
|
|
|
96
126
|
/** Read all source files once, returning {file, relative, text} records. */
|
|
97
127
|
export function readSources(root, options) {
|
|
98
|
-
return
|
|
128
|
+
return scanSourceFiles(root, options).paths.map(file => ({
|
|
99
129
|
file,
|
|
100
130
|
relative: posixRelative(root, file),
|
|
101
131
|
text: readText(file),
|
|
102
132
|
}))
|
|
103
133
|
}
|
|
104
134
|
|
|
135
|
+
/** Like readSources(), but also returns the bounded-scan metadata. */
|
|
136
|
+
export function readSourcesWithStats(root, options) {
|
|
137
|
+
const { paths, stats } = scanSourceFiles(root, options)
|
|
138
|
+
return {
|
|
139
|
+
sources: paths.map(file => ({
|
|
140
|
+
file,
|
|
141
|
+
relative: posixRelative(root, file),
|
|
142
|
+
text: readText(file),
|
|
143
|
+
})),
|
|
144
|
+
scan: stats,
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
105
148
|
export function regexHits(sources, pattern) {
|
|
106
149
|
const hits = []
|
|
107
150
|
for (const source of sources) {
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,47 @@
|
|
|
1
|
+
## 1.4.0 — 2026-11-06
|
|
2
|
+
|
|
3
|
+
### Added
|
|
4
|
+
|
|
5
|
+
- 契约文件:`spec/schemas/conformance.schema.json`(dsh-conformance/1)、`spec/dsc-clauses.json`(14 父 + 60 子)、`spec/schemas/boost-card.schema.json` + 示例、`spec/examples/flow-evidence.example.json`、`spec/coverage-contract.md`。
|
|
6
|
+
- `notChecked[]`:NA/ERROR/partial 项带 `reason` + `howToEnable`,报告能回答“为什么没查、怎么才能查”。
|
|
7
|
+
- bundle/client 的 DSC-04–09 静态子集(PASS/WARN + partial),不再 8 项全 NA。
|
|
8
|
+
- sidecar 加 `title/repo/license/scope/checks/checkedBy/notChecked`,种 20 条真实插件。
|
|
9
|
+
- `check` 默认 `--entry-claim static --no-exec`,报告标注“未执行插件代码”;host 版本三态(显式/环境/受控探测),默认不执行 PATH 的 dsh。
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- C-2 range 解析:支持 `>= 0.2.2-rc.1`、hyphen/x-range、`||`,prerelease 语义对齐 node-semver;合法范围不再误报 C-2.1,覆盖判断归 C-2.2。
|
|
14
|
+
- DSC-01 检查全部 `exports` 子路径与 `main` 的真实文件存在性,坏入口 FAIL。
|
|
15
|
+
- DSC-10 文件截断改为 `NA + coverage=partial` 并明示截断位置,不再假报 ERROR。
|
|
16
|
+
- advice:54 条规则 docsUrl 改到具体小节;C-11.4/C-12.4 同证据合并;`package.json#field` 补 `file:line`。
|
|
17
|
+
- 文档纠偏:删除 BOOST 的 -17%/-33%/-25%、「保证更省」、「token 直接计价」,明确「BOOST 与 FLOW 不可比、性能未证实」。
|
|
18
|
+
- README/SUPPORTED 对齐 1.4.0,补 Windows 行与 14 项默认检查说明。
|
|
19
|
+
|
|
20
|
+
### Verified
|
|
21
|
+
|
|
22
|
+
- `npm test` 232/232。
|
|
23
|
+
|
|
24
|
+
## 1.3.0 — 2026-11-06
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- `check [dir]` 子命令:默认 cwd、Markdown 报告(一句结论、≤3 条优先修复、分桶全表、advice、免责声明),`--json` 与 `--html`(单文件、内联 CSS、零依赖)。
|
|
29
|
+
- `spec/schemas/advice.schema.json`(`dsh-advice/1`),advice 逐条过 schema。
|
|
30
|
+
- 证据主体绑定:`--flow-evidence --dir` 校验 name/version/treeSha256/tarball 哈希,不一致即 `invalid`。
|
|
31
|
+
- CI 归属:collector 写 `producer.runUrl/repository/commit`,audit 标 `attestation: self-declared|ci`(不升级 claimLevel)。
|
|
32
|
+
- sidecar 生成(`publish/sidecar.json`,`dsh-market-sidecar/1`)、每日过期/撤回 workflow、离线包(~580 KB)。
|
|
33
|
+
- `SUPPORTED.md`(host × node × checker 矩阵 + 证据锚)与 `CHANGELOG-RULES.md`(规则级变更与影响面)。
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
- coverage 按全部 14 项统计,`na/notRun/error` 不再为 0;未查 C-3.1/3.2/3.5 时 `entryBlocking.pass=false`,并输出 `checked/notChecked`。
|
|
38
|
+
- 默认 runtime 锚改为宿主实际版本(`anchor.runtimeSource=host`),与 `--runtime-version` 不一致时 `anchor.suspect=true`;不再固定 `0.2.2-alpha.2`。
|
|
39
|
+
- 报告分三桶:`blocking`/`hygiene`(DSC-11/12/13 声明类)/`notChecked`,text 分组渲染,JSON 加 `buckets/metrics/headline`。
|
|
40
|
+
|
|
41
|
+
### Verified
|
|
42
|
+
|
|
43
|
+
- `npm test` 193/193(含 root `test/`)。
|
|
44
|
+
|
|
1
45
|
## 1.2.2 — 2026-11-06
|
|
2
46
|
|
|
3
47
|
- FLOW 沙箱:`flow collect --sandbox` 在 `unshare -Urn` + `prlimit` 下运行,记录 `sandbox{}`;env 白名单硬断言 token 不在子进程;进程组超时击杀;`producer.kind=sandbox` 仅当隔离项全部生效,否则 `sandbox-degraded` 且 audit 永不 runtime。测试 152 → 159。
|
package/README.en.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
3
|
<picture>
|
|
4
|
-
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-standards-
|
|
5
|
-
<img src="docs/assets/dsh-standards-
|
|
4
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-standards-hero-en-dark.webp">
|
|
5
|
+
<img src="docs/assets/dsh-standards-hero-en-light.webp" width="100%" alt="dsh-standards: DSC-01…13 static checks + L1–L3 runtime, reproducible reports">
|
|
6
6
|
</picture>
|
|
7
7
|
|
|
8
8
|
# dsh-standards
|
|
@@ -76,6 +76,11 @@ The full 51-document index lives in the Chinese [README.md](README.md).
|
|
|
76
76
|
|
|
77
77
|
</details>
|
|
78
78
|
|
|
79
|
+
<picture>
|
|
80
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-standards-flow-v4-dark.svg">
|
|
81
|
+
<img src="docs/assets/dsh-standards-flow-v4-light.svg" width="100%" alt="dsh-standards pipeline: plugin dir → DSC-01…13 → L1–L3 runtime → report → dsh-levels">
|
|
82
|
+
</picture>
|
|
83
|
+
|
|
79
84
|
## Three-step setup
|
|
80
85
|
|
|
81
86
|
**1. Install** (either way; Node `>=24.9.0`):
|
package/README.es.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
3
|
<picture>
|
|
4
|
-
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-standards-
|
|
5
|
-
<img src="docs/assets/dsh-standards-
|
|
4
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-standards-hero-en-dark.webp">
|
|
5
|
+
<img src="docs/assets/dsh-standards-hero-en-light.webp" width="100%" alt="dsh-standards: DSC-01…13 static checks + L1–L3 runtime, reproducible reports">
|
|
6
6
|
</picture>
|
|
7
7
|
|
|
8
8
|
# dsh-standards
|
|
@@ -76,6 +76,11 @@ El índice completo de 51 documentos está en el [README.md](README.md) en chino
|
|
|
76
76
|
|
|
77
77
|
</details>
|
|
78
78
|
|
|
79
|
+
<picture>
|
|
80
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-standards-flow-v4-dark.svg">
|
|
81
|
+
<img src="docs/assets/dsh-standards-flow-v4-light.svg" width="100%" alt="dsh-standards pipeline: plugin dir → DSC-01…13 → L1–L3 runtime → report → dsh-levels">
|
|
82
|
+
</picture>
|
|
83
|
+
|
|
79
84
|
## Instalación en tres pasos
|
|
80
85
|
|
|
81
86
|
**1. Instalar** (dos opciones; Node `>=24.9.0`):
|
package/README.hi.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
3
|
<picture>
|
|
4
|
-
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-standards-
|
|
5
|
-
<img src="docs/assets/dsh-standards-
|
|
4
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-standards-hero-en-dark.webp">
|
|
5
|
+
<img src="docs/assets/dsh-standards-hero-en-light.webp" width="100%" alt="dsh-standards: DSC-01…13 static checks + L1–L3 runtime, reproducible reports">
|
|
6
6
|
</picture>
|
|
7
7
|
|
|
8
8
|
# dsh-standards
|
|
@@ -76,6 +76,11 @@ npm पर पुराना आरक्षण `dsh-standards@0.0.3` केव
|
|
|
76
76
|
|
|
77
77
|
</details>
|
|
78
78
|
|
|
79
|
+
<picture>
|
|
80
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-standards-flow-v4-dark.svg">
|
|
81
|
+
<img src="docs/assets/dsh-standards-flow-v4-light.svg" width="100%" alt="dsh-standards pipeline: plugin dir → DSC-01…13 → L1–L3 runtime → report → dsh-levels">
|
|
82
|
+
</picture>
|
|
83
|
+
|
|
79
84
|
## तीन चरणों में शुरुआत
|
|
80
85
|
|
|
81
86
|
**1. इंस्टॉल** (दो में से कोई भी; Node `>=24.9.0`):
|
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
3
|
<picture>
|
|
4
|
-
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-standards-
|
|
5
|
-
<img src="docs/assets/dsh-standards-
|
|
4
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-standards-hero-zh-dark.webp">
|
|
5
|
+
<img src="docs/assets/dsh-standards-hero-zh-light.webp" width="100%" alt="dsh-standards:DSC-01…13 静态检查 + L1–L3 运行时,报告可复现">
|
|
6
6
|
</picture>
|
|
7
7
|
|
|
8
8
|
# dsh-standards
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
```sh
|
|
25
25
|
npm i -g dsh-standards # 全局:dsh-standards + dsh-levels
|
|
26
|
-
npx dsh-standards <plugin-dir>
|
|
26
|
+
npx dsh-standards check <plugin-dir> # 安全默认:静态检查,不执行插件代码
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
</div>
|
|
@@ -44,9 +44,9 @@ DSH 插件的采用深度分三层,每层一个问题、一套证据状态,*
|
|
|
44
44
|
|
|
45
45
|
仓里就三样东西:`40-checker/`(DS-C 一致性检查器与全部证据脚本)、`bin/`(两个公开入口 `dsh-standards` 与 `dsh-levels`),以及 `spec/` 与 `31`–`35` 号规范正文。工具只读目标包,不改被检对象;结论只认证据,`not-run`、`NA`、`static` 都不会被写成 PASS。
|
|
46
46
|
|
|
47
|
-
npm 上的旧占位 `dsh-standards@0.0.3` 只是名字预留,这条 1.x 线取代了它(当前 `1.
|
|
47
|
+
npm 上的旧占位 `dsh-standards@0.0.3` 只是名字预留,这条 1.x 线取代了它(当前 `1.3.0`);两个 bin 分别叫 `dsh-standards` 与 `dsh-levels`。
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
锚点不是硬编码的通行证:`repo` / `commit` / `cordis` 记在报告里,`dsh` 由 `--host-version`、`DSH_HOST_VERSION` 或显式 `--allow-host-probe` 解析;都没有时报告写「host 版本未知」,并标 `source=default, suspect=true`。换一次版本就是另一把尺子,旧报告不能直接当新版本的证据。
|
|
50
50
|
|
|
51
51
|
### 当前硬数据
|
|
52
52
|
|
|
@@ -55,7 +55,7 @@ npm 上的旧占位 `dsh-standards@0.0.3` 只是名字预留,这条 1.x 线取
|
|
|
55
55
|
- 18 包 base-only runtime:**18/18 有原始 JSON;10/18 G3 verified,8 partial,0 error**;host gate 后 **8 clean + 2 reject + 8 partial**;
|
|
56
56
|
- client runtime:**15/15 `client-module-loaded`**(真实 Chromium);web-app **4/4 loaded**;
|
|
57
57
|
- 不可用:**0 plugin code FAIL;2 host-gate reject(telemetry/verification)**;
|
|
58
|
-
-
|
|
58
|
+
- 检查器自测:**`npm test` 全绿**(本地 Node 24;含安全默认、host 三态、治理、DCO、评价记录、声明、验真、链、漂移测试);
|
|
59
59
|
- J1:**6 PASS + 1 WARN**;LEV-3:volatile **0/20** vs control **20/20** remount;LEV-6:稳定 prefix **100%** vs 易变 **0%**;LEV-7:**8.24×**(合成,非生产基线)。
|
|
60
60
|
|
|
61
61
|
这些数字是自评与仓内证据链的输出,不是第三方复核结论——报告里的 `notClaimed` 字段会把「官方标准、认证、安全审计、排名、生产基线」逐条列出来。
|
|
@@ -72,6 +72,7 @@ npm 上的旧占位 `dsh-standards@0.0.3` 只是名字预留,这条 1.x 线取
|
|
|
72
72
|
- [33-DSH-BOOST 加速实践](33-DSH-BOOST加速实践.md) — 8 条 LEV
|
|
73
73
|
- [34-三层分级模型(GB/GB-T/GB-Z 类比)](34-三层分级模型-GB-GBT-GBZ类比.md) — 为什么是三层、怎么读
|
|
74
74
|
- [35-三层开发建议与验收清单](35-三层开发建议与验收清单.md) — 作者最短路径 / 评审清单
|
|
75
|
+
- [docs/coverage.md](docs/coverage.md) — 14 项检查:默认查什么、不查什么、怎么开启
|
|
75
76
|
- [36-插件全生命周期与工具链](36-插件全生命周期与工具链.md) — 从脚手架到退役
|
|
76
77
|
- [37-PerryLink 插件生态三层对照](37-PerryLink插件生态-三层对照与升级清单.md) — 我的仓怎么升级
|
|
77
78
|
- [38-三层标准 RFC](38-RFC-三层标准-待商议问题.md) — 内测群商议的 8 个问题
|
|
@@ -94,6 +95,11 @@ npm 上的旧占位 `dsh-standards@0.0.3` 只是名字预留,这条 1.x 线取
|
|
|
94
95
|
|
|
95
96
|
</details>
|
|
96
97
|
|
|
98
|
+
<picture>
|
|
99
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-standards-flow-v4-dark.svg">
|
|
100
|
+
<img src="docs/assets/dsh-standards-flow-v4-light.svg" width="100%" alt="dsh-standards 流程:插件目录 → DSC-01…13 静态 → L1–L3 运行时 → 报告 → dsh-levels">
|
|
101
|
+
</picture>
|
|
102
|
+
|
|
97
103
|
## 三步上手
|
|
98
104
|
|
|
99
105
|
**1. 安装**(二选一,Node 需 `>=24.9.0`):
|
|
@@ -111,6 +117,9 @@ npx -p dsh-standards dsh-levels --help
|
|
|
111
117
|
**2. 跑一次检查**(目标是一个含 `package.json` 的包目录):
|
|
112
118
|
|
|
113
119
|
```sh
|
|
120
|
+
# 作者入口(推荐):默认 --entry-claim static --no-exec,不执行插件代码
|
|
121
|
+
dsh-standards check ./my-plugin
|
|
122
|
+
|
|
114
123
|
# 入门兼容性:静态识别,不声明运行时证据
|
|
115
124
|
dsh-standards ./my-plugin --scope entry --entry-claim static
|
|
116
125
|
|
|
@@ -122,6 +131,8 @@ dsh-standards ./my-plugin --format sarif --out report.sarif
|
|
|
122
131
|
dsh-levels ./my-plugin --format json --out audit.json
|
|
123
132
|
```
|
|
124
133
|
|
|
134
|
+
`check` 是面向作者的安全入口:**默认 `--entry-claim static --no-exec`,不执行插件代码**;Markdown 报告顶部会写明「本次未执行插件代码」。要跑 L1–L3,必须显式给 `--runtime <cordis lib/index.js>`(bundle 用 `--host`)或 `--entry-claim runtime`,否则运行时项一律记 `not-run`,绝不写成 PASS。它也不会无条件执行 PATH 里的 `dsh`:anchor 依次取 `--host-version`、`$DSH_HOST_VERSION`,再次才是显式 `--allow-host-probe`;三者都没有时报告写「host 版本未知」,并标 `source=default, suspect=true`。
|
|
135
|
+
|
|
125
136
|
**3. 看结论**——先看 exit code,再看 `status`:
|
|
126
137
|
|
|
127
138
|
```text
|
|
@@ -186,19 +197,21 @@ coverage: mandatory=5 pass=5 warn=0 fail=0 na=0 notRun=0
|
|
|
186
197
|
|
|
187
198
|
## 配置
|
|
188
199
|
|
|
189
|
-
|
|
200
|
+
没有配置文件——全部可调项就是命令行参数。三个入口各一套:`dsh-standards check <package-dir>` 是作者安全入口(默认静态、不执行插件代码),`dsh-standards <package-dir>` 是向后兼容的裸检查器(不附加安全默认,`--help` 打印完整用法),`dsh-levels <package-dir>` 转发给三层审计。下表默认值指 `check`;裸检查器的 `--entry-claim` / `--no-exec` 默认仍是 `runtime` / `false`。
|
|
190
201
|
|
|
191
|
-
最常用的
|
|
202
|
+
最常用的 10 个:
|
|
192
203
|
|
|
193
204
|
| 参数 | 默认值 | 含义 |
|
|
194
205
|
|---|---|---|
|
|
195
206
|
| `<package-dir>` | `.` | 目标包目录,必须含 `package.json`;tarball 不在支持范围 |
|
|
196
207
|
| `--scope` | `ds-c` | `mvp` = 8 个 MVP ID;`ds-c` = 全量声明;`entry` = 兼容性门 |
|
|
197
208
|
| `--format` | `text` | `text` / `json` / `sarif`(SARIF 2.1.0) |
|
|
198
|
-
| `--entry-claim` | `runtime
|
|
199
|
-
| `--host` | — | app-boot 的 `lib/index.js`;bundle 的 G0–G3
|
|
209
|
+
| `--entry-claim` | `static`(`check`;裸检查器 `runtime`) | `runtime` 需 DSC-14 运行时证据;`static` 只声明结构识别 |
|
|
210
|
+
| `--host` | — | app-boot 的 `lib/index.js`;bundle 的 G0–G3 冒烟必需,也视为执行 opt-in |
|
|
200
211
|
| `--profile` | `package` | `package` / `listing` / `both`;给了 `--market-index` 时默认 `listing` |
|
|
201
|
-
| `--no-exec` | `false
|
|
212
|
+
| `--no-exec` | `true`(`check`;裸检查器 `false`) | 跳过 L1–L3,记 `not-run`(绝不记 PASS) |
|
|
213
|
+
| `--host-version` | `$DSH_HOST_VERSION` | `check` 专用的宿主 dsh 版本;不执行任何 `dsh` |
|
|
214
|
+
| `--allow-host-probe` | `false` | 显式允许 `check` 解析 `dsh` 的 JS 入口并 `node ... --version` 探测 |
|
|
202
215
|
| `--out` | — | 报告写入文件;不给就打 stdout |
|
|
203
216
|
|
|
204
217
|
<details>
|
|
@@ -210,19 +223,21 @@ coverage: mandatory=5 pass=5 warn=0 fail=0 na=0 notRun=0
|
|
|
210
223
|
| `--scope` | `ds-c` | 检查范围:`mvp` / `ds-c` / `entry` |
|
|
211
224
|
| `--profile` | `package` | 评估 profile:`package` / `listing` / `both` |
|
|
212
225
|
| `--runtime` | — | 指向构建好的 `@deepseek-ai/cordis` `lib/index.js`,开启 L1–L3 |
|
|
213
|
-
| `--runtime-version` |
|
|
226
|
+
| `--runtime-version` | 宿主版本(`check`);否则内置常量 | 写进 anchor 的 dsh 运行时版本,供 C-2 判定;报告用 `anchor.runtimeSource` 说明来源 |
|
|
214
227
|
| `--cordis-version` | `4.0.6-alpha.1` | 写进 anchor 的 cordis 版本 |
|
|
215
228
|
| `--commit` | `ce7073d51254` | 写进 anchor 的 harness commit |
|
|
216
229
|
| `--install-spec` | — | 给 C-3 用的推荐安装 spec |
|
|
217
230
|
| `--market-index` | — | 格式 1 的市场索引 JSON,开启 listing 模式 |
|
|
218
231
|
| `--plugin-name` | — | 市场索引里的插件名 |
|
|
219
232
|
| `--timeout` | `10000` | L1–L3 超时毫秒(100–120000) |
|
|
220
|
-
| `--no-exec` | `false` | 完全跳过 L1–L3,记 `not-run` / `NA` |
|
|
233
|
+
| `--no-exec` | `false`(裸检查器;`check` 默认 `true`) | 完全跳过 L1–L3,记 `not-run` / `NA` |
|
|
221
234
|
| `--static-only` | `false` | M1 模式:只留 C-1/C-2/C-3/C-11/C-12/C-13,C-4–C-9 直接出界 |
|
|
222
235
|
| `--treat-as-plugin` | `false` | 对 bundle 的 server 入口跑 L1–L3 |
|
|
223
236
|
| `--entry-runtime` | `require` | entry 门要完整 G0–G3,还是接受 `server-entry` 代理 |
|
|
224
|
-
| `--entry-claim` | `runtime` | entry 门要运行时证据(DSC-14),还是只做静态识别 |
|
|
225
|
-
| `--host` | — | app-boot `lib/index.js`,开启 bundle 的 G0–G3
|
|
237
|
+
| `--entry-claim` | `runtime`(裸检查器;`check` 默认 `static`) | entry 门要运行时证据(DSC-14),还是只做静态识别 |
|
|
238
|
+
| `--host` | — | app-boot `lib/index.js`,开启 bundle 的 G0–G3 冒烟(`check` 中视为执行 opt-in) |
|
|
239
|
+
| `--host-version` | `$DSH_HOST_VERSION` | `check` 专用:直接声明宿主版本,不执行 `dsh` |
|
|
240
|
+
| `--allow-host-probe` | `false` | `check` 专用:显式允许探测宿主版本 |
|
|
226
241
|
| `--base` | — | bundle 冒烟用的 base `cordis.patch.yml` |
|
|
227
242
|
| `--web` | — | bundle 冒烟用的 web-app `cordis.patch.yml` |
|
|
228
243
|
| `--bare` | — | host farm 的 bare-module 基线目录 |
|
|
@@ -292,7 +307,8 @@ coverage: mandatory=5 pass=5 warn=0 fail=0 na=0 notRun=0
|
|
|
292
307
|
- **不声称官方推荐或采纳;不声称认证 / 认可 / 安全审计。**
|
|
293
308
|
- **MVP 覆盖缺口**:`--scope mvp` 只落 8 个 ID,`DSC-03/10/11/12/13` 不在其中;它的结论是 `subset: "MVP-13"`、`level: null`,不得当 DS-C 徽章用。
|
|
294
309
|
- **只吃包目录**:目标必须是含 `package.json` 的目录;tarball 支持不在 MVP 里。
|
|
295
|
-
- **运行时结论有前置条件**:没有 `--runtime` 或 `--host` 时 L1–L3 只能记 `not-run`;`--entry-claim static`
|
|
310
|
+
- **运行时结论有前置条件**:没有 `--runtime` 或 `--host` 时 L1–L3 只能记 `not-run`;`--entry-claim static` 永远给不出运行时结论。`check` 默认就是不执行(`--no-exec`),这是刻意的安全默认,不是缺陷。
|
|
311
|
+
- **host 版本可能未知**:`check` 不无条件执行 PATH 里的 `dsh`;没有 `--host-version` / `DSH_HOST_VERSION` / `--allow-host-probe` 时,anchor 写默认常量并标 `source=default, suspect=true`,报告顶部写「host 版本未知」。
|
|
296
312
|
- **结论带版本锚点**:`commit` / `dsh` / `cordis` 变了就是另一把尺子,旧报告不能直接当新版本的证据。
|
|
297
313
|
- **BOOST 数据不是生产基线**:LEV 的 8/8 measured 来自 loader / context / provider-capture / synthetic timer 的合成环境,LEV-7 的 8.24× 同理。
|
|
298
314
|
- **文本改动必须说明**:复制或修改本仓文本时,必须在显著位置给出 Change Notice(来源 commit、改了哪个章节、改了什么、为什么、日期),见 [`MODIFICATION-POLICY.md`](MODIFICATION-POLICY.md);标志与 badge 不随许可证授权,规则见 `governance/mark-policy.md`。
|
|
@@ -303,9 +319,10 @@ coverage: mandatory=5 pass=5 warn=0 fail=0 na=0 notRun=0
|
|
|
303
319
|
git clone https://github.com/PerryLink/dsh-standards.git
|
|
304
320
|
cd dsh-standards
|
|
305
321
|
|
|
306
|
-
node --test 40-checker/test/*.test.mjs #
|
|
322
|
+
node --test 40-checker/test/*.test.mjs # 全绿(含安全默认与 host 三态)
|
|
323
|
+
node bin/dsh-standards.mjs check 40-checker/fixtures/good # 安全默认:静态、不执行
|
|
307
324
|
node bin/dsh-standards.mjs 40-checker/fixtures/good \
|
|
308
|
-
--scope entry --entry-claim static --format text #
|
|
325
|
+
--scope entry --entry-claim static --format text # 裸检查器 fixture 冒烟
|
|
309
326
|
node bin/dsh-levels.mjs 40-checker/fixtures/good --format json
|
|
310
327
|
|
|
311
328
|
# 从源码直连检查器:结构识别 / 声明运行(bundle 需要 host)
|