semantic-js-mcp 0.10.2 → 0.10.4
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/.codex-plugin/plugin.json +1 -1
- package/CHANGELOG.md +15 -0
- package/README.md +18 -72
- package/SETUP.md +1078 -0
- package/docs/distribution.md +9 -16
- package/docs/getting-started.md +11 -1
- package/lib/diagnostic-evidence.mjs +26 -0
- package/package.json +5 -5
- package/protocol.mjs +22 -2
- package/scripts/check-protocol-literals.mjs +5 -1
- package/scripts/distribution-policy.mjs +1 -1
- package/scripts/documentation-contract.mjs +105 -21
- package/scripts/documentation-gate-smoke.mjs +308 -37
- package/scripts/documentation-gate.mjs +93 -9
- package/scripts/generate-protocol-reference.mjs +4 -0
- package/scripts/negative-verification-smoke.mjs +5 -0
- package/scripts/smoke.mjs +47 -0
- package/scripts/vue-smoke.mjs +11 -0
- package/server.mjs +6 -2
- package/skills/semantic-navigation/SKILL.md +3 -1
- package/skills/semantic-navigation/references/protocol-literals.md +19 -2
- package/AGENT_SETUP.md +0 -176
|
@@ -6,8 +6,9 @@ import {evaluateDocumentation} from "./documentation-gate.mjs";
|
|
|
6
6
|
|
|
7
7
|
const headingText = (headings) => headings.map((heading) => `## ${heading}`).join("\n");
|
|
8
8
|
const valid = {
|
|
9
|
-
[DOCUMENTATION_FILE.README]: `${headingText(DOCUMENTATION_REQUIREMENT.README_HEADINGS)}\n${DOCUMENTATION_REQUIREMENT.README_LINKS.map((link) => `[doc](${link})`).join("\n")}\n${DOCUMENTATION_REQUIREMENT.
|
|
10
|
-
[DOCUMENTATION_FILE.
|
|
9
|
+
[DOCUMENTATION_FILE.README]: `${headingText(DOCUMENTATION_REQUIREMENT.README_HEADINGS)}\n${DOCUMENTATION_REQUIREMENT.README_LINKS.map((link) => `[doc](${link})`).join("\n")}\n${DOCUMENTATION_REQUIREMENT.README_SETUP_ENTRY_LITERALS.join("\n")}`,
|
|
10
|
+
[DOCUMENTATION_FILE.SETUP]: `${headingText(DOCUMENTATION_REQUIREMENT.SETUP_HEADINGS)}\n${DOCUMENTATION_REQUIREMENT.SETUP_LITERALS.join("\n")}`,
|
|
11
|
+
[DOCUMENTATION_FILE.DISTRIBUTION]: `${headingText(DOCUMENTATION_REQUIREMENT.DISTRIBUTION_HEADINGS)}\n${DOCUMENTATION_REQUIREMENT.DISTRIBUTION_LITERALS.join("\n")}`,
|
|
11
12
|
[DOCUMENTATION_FILE.GETTING_STARTED]: `${headingText(DOCUMENTATION_REQUIREMENT.GETTING_STARTED_HEADINGS)}\n${DOCUMENTATION_REQUIREMENT.GETTING_STARTED_LITERALS.join("\n")}`,
|
|
12
13
|
[DOCUMENTATION_FILE.SEMANTIC_NAVIGATION_SKILL]: `${headingText(DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_HEADINGS)}\n${DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_LITERALS.join("\n")}`,
|
|
13
14
|
[DOCUMENTATION_FILE.CONTRIBUTING]: "# Contributing",
|
|
@@ -16,6 +17,105 @@ const valid = {
|
|
|
16
17
|
|
|
17
18
|
strictEqual(evaluateDocumentation(valid).length, 0, "Valid public documentation failed the gate");
|
|
18
19
|
|
|
20
|
+
const lateSetupGuards = {
|
|
21
|
+
...valid,
|
|
22
|
+
[DOCUMENTATION_FILE.SETUP]: valid[DOCUMENTATION_FILE.SETUP].replace("## Guards\n", "").concat("\n## Guards\n"),
|
|
23
|
+
};
|
|
24
|
+
strictEqual(
|
|
25
|
+
evaluateDocumentation(lateSetupGuards).some((finding) => finding.reason === DOCUMENTATION_REASON.SETUP_GUARDS_NOT_FIRST),
|
|
26
|
+
true,
|
|
27
|
+
"Late setup guards were accepted",
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const fencedSetupGuards = {
|
|
31
|
+
...valid,
|
|
32
|
+
[DOCUMENTATION_FILE.SETUP]: valid[DOCUMENTATION_FILE.SETUP].replace("## Guards\n", "```md\n## Guards\n```\n"),
|
|
33
|
+
};
|
|
34
|
+
strictEqual(
|
|
35
|
+
evaluateDocumentation(fencedSetupGuards).some(
|
|
36
|
+
(finding) =>
|
|
37
|
+
finding.reason === DOCUMENTATION_REASON.HEADING_MISSING &&
|
|
38
|
+
finding.file === DOCUMENTATION_FILE.SETUP &&
|
|
39
|
+
finding.heading === DOCUMENTATION_REQUIREMENT.SETUP_FIRST_SECTION,
|
|
40
|
+
),
|
|
41
|
+
true,
|
|
42
|
+
"Setup heading inside a fenced block was accepted",
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const invalidSetupOrder = {
|
|
46
|
+
...valid,
|
|
47
|
+
[DOCUMENTATION_FILE.SETUP]: valid[DOCUMENTATION_FILE.SETUP]
|
|
48
|
+
.replace("## Prerequisites\n", "## Setup Order Placeholder\n")
|
|
49
|
+
.replace("## Choose A Version\n", "## Prerequisites\n")
|
|
50
|
+
.replace("## Setup Order Placeholder\n", "## Choose A Version\n"),
|
|
51
|
+
};
|
|
52
|
+
strictEqual(
|
|
53
|
+
evaluateDocumentation(invalidSetupOrder).some((finding) => finding.reason === DOCUMENTATION_REASON.SETUP_SECTION_ORDER_INVALID),
|
|
54
|
+
true,
|
|
55
|
+
"Invalid setup section order was accepted",
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const earlySetupBackground = {
|
|
59
|
+
...valid,
|
|
60
|
+
[DOCUMENTATION_FILE.SETUP]: valid[DOCUMENTATION_FILE.SETUP]
|
|
61
|
+
.replace("## Background\n", "")
|
|
62
|
+
.replace("## Troubleshooting\n", "## Background\n## Troubleshooting\n"),
|
|
63
|
+
};
|
|
64
|
+
strictEqual(
|
|
65
|
+
evaluateDocumentation(earlySetupBackground).some((finding) => finding.reason === DOCUMENTATION_REASON.SETUP_BACKGROUND_NOT_LAST),
|
|
66
|
+
true,
|
|
67
|
+
"Setup background outside the final section was accepted",
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const lateSetupEntryPoint = {
|
|
71
|
+
...valid,
|
|
72
|
+
[DOCUMENTATION_FILE.README]: valid[DOCUMENTATION_FILE.README].replace("## Setup\n", "").concat("\n## Setup\n"),
|
|
73
|
+
};
|
|
74
|
+
strictEqual(
|
|
75
|
+
evaluateDocumentation(lateSetupEntryPoint).some((finding) => finding.reason === DOCUMENTATION_REASON.SETUP_ENTRY_POINT_NOT_FIRST),
|
|
76
|
+
true,
|
|
77
|
+
"Late setup entry point was accepted",
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const duplicateSetupSection = {
|
|
81
|
+
...valid,
|
|
82
|
+
[DOCUMENTATION_FILE.README]: `${valid[DOCUMENTATION_FILE.README]}\n## ${DOCUMENTATION_REQUIREMENT.README_FORBIDDEN_SETUP_HEADINGS[1]}`,
|
|
83
|
+
};
|
|
84
|
+
strictEqual(
|
|
85
|
+
evaluateDocumentation(duplicateSetupSection).some((finding) => finding.reason === DOCUMENTATION_REASON.DUPLICATE_SETUP_SECTION),
|
|
86
|
+
true,
|
|
87
|
+
"Duplicate README setup section was accepted",
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
for (const literal of DOCUMENTATION_REQUIREMENT.README_FORBIDDEN_SETUP_LITERALS) {
|
|
91
|
+
const duplicateSetupCommand = {
|
|
92
|
+
...valid,
|
|
93
|
+
[DOCUMENTATION_FILE.README]: `${valid[DOCUMENTATION_FILE.README]}\n${literal}`,
|
|
94
|
+
};
|
|
95
|
+
strictEqual(
|
|
96
|
+
evaluateDocumentation(duplicateSetupCommand).some(
|
|
97
|
+
(finding) => finding.reason === DOCUMENTATION_REASON.DUPLICATE_SETUP_COMMAND && finding.literal === literal,
|
|
98
|
+
),
|
|
99
|
+
true,
|
|
100
|
+
`Duplicate README setup command was accepted: ${literal}`,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const duplicateDistributionSetupCommand = {
|
|
105
|
+
...valid,
|
|
106
|
+
[DOCUMENTATION_FILE.DISTRIBUTION]: `${valid[DOCUMENTATION_FILE.DISTRIBUTION]}\n${DOCUMENTATION_REQUIREMENT.DISTRIBUTION_FORBIDDEN_SETUP_LITERALS[0]}`,
|
|
107
|
+
};
|
|
108
|
+
strictEqual(
|
|
109
|
+
evaluateDocumentation(duplicateDistributionSetupCommand).some(
|
|
110
|
+
(finding) =>
|
|
111
|
+
finding.file === DOCUMENTATION_FILE.DISTRIBUTION &&
|
|
112
|
+
finding.reason === DOCUMENTATION_REASON.DUPLICATE_SETUP_COMMAND &&
|
|
113
|
+
finding.literal === DOCUMENTATION_REQUIREMENT.DISTRIBUTION_FORBIDDEN_SETUP_LITERALS[0],
|
|
114
|
+
),
|
|
115
|
+
true,
|
|
116
|
+
"Duplicate distribution setup command was accepted",
|
|
117
|
+
);
|
|
118
|
+
|
|
19
119
|
const missingHeading = {...valid, [DOCUMENTATION_FILE.README]: valid[DOCUMENTATION_FILE.README].replace("## Verification", "")};
|
|
20
120
|
strictEqual(
|
|
21
121
|
evaluateDocumentation(missingHeading).some((finding) => finding.reason === DOCUMENTATION_REASON.HEADING_MISSING),
|
|
@@ -48,96 +148,267 @@ strictEqual(
|
|
|
48
148
|
"Missing canonical example literal was accepted",
|
|
49
149
|
);
|
|
50
150
|
|
|
51
|
-
const
|
|
151
|
+
const missingSetupLink = {
|
|
52
152
|
...valid,
|
|
53
|
-
[DOCUMENTATION_FILE.README]: valid[DOCUMENTATION_FILE.README].
|
|
153
|
+
[DOCUMENTATION_FILE.README]: valid[DOCUMENTATION_FILE.README].replaceAll(
|
|
154
|
+
`](${DOCUMENTATION_REQUIREMENT.README_LINKS[0]})`,
|
|
155
|
+
"](missing-setup.md)",
|
|
156
|
+
),
|
|
54
157
|
};
|
|
55
158
|
strictEqual(
|
|
56
|
-
evaluateDocumentation(
|
|
57
|
-
(finding) =>
|
|
58
|
-
finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING && finding.literal === DOCUMENTATION_REQUIREMENT.README_LITERALS[0],
|
|
159
|
+
evaluateDocumentation(missingSetupLink).some(
|
|
160
|
+
(finding) => finding.reason === DOCUMENTATION_REASON.LINK_MISSING && finding.link === DOCUMENTATION_REQUIREMENT.README_LINKS[0],
|
|
59
161
|
),
|
|
60
162
|
true,
|
|
61
|
-
"Missing
|
|
163
|
+
"Missing setup README link was accepted",
|
|
62
164
|
);
|
|
63
165
|
|
|
64
|
-
const
|
|
166
|
+
const missingSetupEntryInstruction = {
|
|
65
167
|
...valid,
|
|
66
|
-
[DOCUMENTATION_FILE.README]: valid[DOCUMENTATION_FILE.README].replace(
|
|
168
|
+
[DOCUMENTATION_FILE.README]: valid[DOCUMENTATION_FILE.README].replace(DOCUMENTATION_REQUIREMENT.README_SETUP_ENTRY_LITERALS[0], ""),
|
|
67
169
|
};
|
|
68
170
|
strictEqual(
|
|
69
|
-
evaluateDocumentation(
|
|
70
|
-
(finding) =>
|
|
171
|
+
evaluateDocumentation(missingSetupEntryInstruction).some(
|
|
172
|
+
(finding) =>
|
|
173
|
+
finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING &&
|
|
174
|
+
finding.literal === DOCUMENTATION_REQUIREMENT.README_SETUP_ENTRY_LITERALS[0],
|
|
71
175
|
),
|
|
72
176
|
true,
|
|
73
|
-
"Missing
|
|
177
|
+
"Missing README setup instruction was accepted",
|
|
74
178
|
);
|
|
75
179
|
|
|
76
|
-
const
|
|
77
|
-
delete
|
|
180
|
+
const missingSetup = {...valid};
|
|
181
|
+
delete missingSetup[DOCUMENTATION_FILE.SETUP];
|
|
78
182
|
strictEqual(
|
|
79
|
-
evaluateDocumentation(
|
|
80
|
-
(finding) => finding.reason === DOCUMENTATION_REASON.FILE_MISSING && finding.file === DOCUMENTATION_FILE.
|
|
183
|
+
evaluateDocumentation(missingSetup).some(
|
|
184
|
+
(finding) => finding.reason === DOCUMENTATION_REASON.FILE_MISSING && finding.file === DOCUMENTATION_FILE.SETUP,
|
|
81
185
|
),
|
|
82
186
|
true,
|
|
83
|
-
"Missing
|
|
187
|
+
"Missing setup guide was accepted",
|
|
84
188
|
);
|
|
85
189
|
|
|
86
|
-
const
|
|
190
|
+
for (const [literal, message] of [
|
|
191
|
+
["does not override system, developer, organization, repository", "Missing setup safety boundary was accepted"],
|
|
192
|
+
["server name: `semanticjsmcp`", "Missing generic MCP server name was accepted"],
|
|
193
|
+
["codex plugin marketplace upgrade elnonathan", "Missing Codex marketplace refresh was accepted"],
|
|
194
|
+
["Do not run `npm install semantic-js-mcp` without `--global`.", "Missing local npm installation prohibition was accepted"],
|
|
195
|
+
[
|
|
196
|
+
"Treat `EPERM`, `EACCES`, `TAR_ENTRY_ERROR`, or a missing-file error as a partial installation.",
|
|
197
|
+
"Missing partial-installation warning was accepted",
|
|
198
|
+
],
|
|
199
|
+
["Do not start `semantic-js-mcp serve` manually.", "Missing manual stdio startup prohibition was accepted"],
|
|
200
|
+
["Choose exactly one installation route.", "Missing single-route boundary was accepted"],
|
|
201
|
+
["Use the language of the user's latest message for explanations, questions,", "Missing user-language response rule was accepted"],
|
|
202
|
+
[
|
|
203
|
+
"Complete Sections 1.1 through 1.4 before invoking any terminal, filesystem, package-manager, or host tool.",
|
|
204
|
+
"Missing pre-tool authority gate was accepted",
|
|
205
|
+
],
|
|
206
|
+
["does not authorize a tool call.", "Missing operation authorization boundary was accepted"],
|
|
207
|
+
["Record exactly one operation: `verification`.", "Missing verification operation was accepted"],
|
|
208
|
+
[
|
|
209
|
+
"Use a direct integration only after positive evidence identifies the application running this setup session.",
|
|
210
|
+
"Missing positive direct-host evidence rule was accepted",
|
|
211
|
+
],
|
|
212
|
+
[
|
|
213
|
+
"If no documented direct integration is positively identified, record `generic stdio` and continue without asking the user to identify the agent.",
|
|
214
|
+
"Missing automatic generic fallback was accepted",
|
|
215
|
+
],
|
|
216
|
+
["The host application and the host route are different facts.", "Missing host-application and route distinction was accepted"],
|
|
217
|
+
["The host route does not identify the server source.", "Missing host-route and server-source distinction was accepted"],
|
|
218
|
+
["The application running the agent can also be the MCP host.", "Missing current-application host rule was accepted"],
|
|
219
|
+
["Recommended target host: `<application>` using `generic stdio`.", "Missing generic target-host recommendation was accepted"],
|
|
220
|
+
[
|
|
221
|
+
"Do not repeatedly ask for the application name after positive session evidence already identifies it.",
|
|
222
|
+
"Missing repeated host-question prohibition was accepted",
|
|
223
|
+
],
|
|
224
|
+
["Do not present direct integrations as a menu.", "Missing direct-integration menu prohibition was accepted"],
|
|
225
|
+
["The presence of an installed host executable is not positive host evidence.", "Missing executable identity prohibition was accepted"],
|
|
226
|
+
["continue only with the recorded host route", "Missing maintenance host-route guard was accepted"],
|
|
227
|
+
["and server source for the existing installation.", "Missing maintenance server-source guard was accepted"],
|
|
228
|
+
[
|
|
229
|
+
"Do not use `npx`, `npm exec`, `@latest`, or an npm cache as an installation route.",
|
|
230
|
+
"Missing ephemeral installation prohibition was accepted",
|
|
231
|
+
],
|
|
232
|
+
["Do not pipe JSON-RPC messages", "Missing manual JSON-RPC prohibition was accepted"],
|
|
233
|
+
[
|
|
234
|
+
"A manually started server does not prove that the configured host application loaded the MCP tools.",
|
|
235
|
+
"Missing host-integration proof boundary was accepted",
|
|
236
|
+
],
|
|
237
|
+
["Source checkout registration is not supported", "Missing Codex source-checkout boundary was accepted"],
|
|
238
|
+
["When the recorded host route is `Codex direct`, the agent may run the", "Missing Codex prerequisite command ownership was accepted"],
|
|
239
|
+
[
|
|
240
|
+
"I cannot identify the current host application. Which application should I configure?",
|
|
241
|
+
"Missing unknown-host configuration question was accepted",
|
|
242
|
+
],
|
|
243
|
+
["record `user` as the command runner.", "Missing generic command-runner record was accepted"],
|
|
244
|
+
["low side-effect risk, but they are not sandbox-neutral evidence:", "Missing sandbox-neutral version-check boundary was accepted"],
|
|
245
|
+
["The agent must not perform even read-only local checks.", "Missing generic local-inspection prohibition was accepted"],
|
|
246
|
+
["A command name is not enough to infer its argument grammar.", "Missing host-command grammar guard was accepted"],
|
|
247
|
+
["Do not construct the add command unless Section 5.1 recorded its exact current", "Missing add-command grammar checkpoint was accepted"],
|
|
248
|
+
["An added-entry message is not enough.", "Missing saved-entry validation guard was accepted"],
|
|
249
|
+
["A host status of `failed` is a failure, not an expected pre-restart state.", "Missing failed-host-status guard was accepted"],
|
|
250
|
+
["Use `lsp_document_symbols` on that file as the first functional", "Missing bounded functional-test guidance was accepted"],
|
|
251
|
+
["global executable checks for a source checkout.", "Missing source-checkout verification boundary was accepted"],
|
|
252
|
+
["search the user's home directory or unrelated directories for a checkout.", "Missing source-checkout search prohibition was accepted"],
|
|
253
|
+
[
|
|
254
|
+
"A similarly named file is not evidence of a Semantic JS MCP installation.",
|
|
255
|
+
"Missing similarly-named-file evidence boundary was accepted",
|
|
256
|
+
],
|
|
257
|
+
["Before constructing a removal command, confirm the exact remove subcommand", "Missing removal-command grammar guard was accepted"],
|
|
258
|
+
["do not run the global npm", "Missing source-checkout removal boundary was accepted"],
|
|
259
|
+
[
|
|
260
|
+
"Before constructing a source-checkout registration command, confirm the exact",
|
|
261
|
+
"Missing source-checkout registration grammar guard was accepted",
|
|
262
|
+
],
|
|
263
|
+
["report that the source checkout is ready and stop.", "Missing no-host source-checkout outcome was accepted"],
|
|
264
|
+
["No source-code call is required", "Missing installation authority boundary was accepted"],
|
|
265
|
+
]) {
|
|
266
|
+
const missingInstruction = {
|
|
267
|
+
...valid,
|
|
268
|
+
[DOCUMENTATION_FILE.SETUP]: valid[DOCUMENTATION_FILE.SETUP].replace(literal, ""),
|
|
269
|
+
};
|
|
270
|
+
strictEqual(
|
|
271
|
+
evaluateDocumentation(missingInstruction).some(
|
|
272
|
+
(finding) => finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING && finding.literal === literal,
|
|
273
|
+
),
|
|
274
|
+
true,
|
|
275
|
+
message,
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const missingCombinationInvariant = {
|
|
87
280
|
...valid,
|
|
88
|
-
[DOCUMENTATION_FILE.
|
|
281
|
+
[DOCUMENTATION_FILE.SEMANTIC_NAVIGATION_SKILL]: valid[DOCUMENTATION_FILE.SEMANTIC_NAVIGATION_SKILL].replace(
|
|
282
|
+
DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_LITERALS[1],
|
|
283
|
+
"",
|
|
284
|
+
),
|
|
89
285
|
};
|
|
90
286
|
strictEqual(
|
|
91
|
-
evaluateDocumentation(
|
|
287
|
+
evaluateDocumentation(missingCombinationInvariant).some(
|
|
92
288
|
(finding) =>
|
|
93
|
-
finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING &&
|
|
289
|
+
finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING &&
|
|
290
|
+
finding.literal === DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_LITERALS[1],
|
|
94
291
|
),
|
|
95
292
|
true,
|
|
96
|
-
"Missing
|
|
293
|
+
"Missing combination-invariant guidance was accepted",
|
|
97
294
|
);
|
|
98
295
|
|
|
99
|
-
const
|
|
296
|
+
const missingDiagnosticExampleGuidance = {
|
|
100
297
|
...valid,
|
|
101
|
-
[DOCUMENTATION_FILE.
|
|
298
|
+
[DOCUMENTATION_FILE.GETTING_STARTED]: valid[DOCUMENTATION_FILE.GETTING_STARTED].replace(
|
|
299
|
+
DOCUMENTATION_REQUIREMENT.GETTING_STARTED_LITERALS[3],
|
|
300
|
+
"",
|
|
301
|
+
),
|
|
102
302
|
};
|
|
103
303
|
strictEqual(
|
|
104
|
-
evaluateDocumentation(
|
|
304
|
+
evaluateDocumentation(missingDiagnosticExampleGuidance).some(
|
|
105
305
|
(finding) =>
|
|
106
|
-
finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING && finding.literal === DOCUMENTATION_REQUIREMENT.
|
|
306
|
+
finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING && finding.literal === DOCUMENTATION_REQUIREMENT.GETTING_STARTED_LITERALS[3],
|
|
107
307
|
),
|
|
108
308
|
true,
|
|
109
|
-
"Missing
|
|
309
|
+
"Missing untrusted-diagnostic example guidance was accepted",
|
|
110
310
|
);
|
|
111
311
|
|
|
112
|
-
const
|
|
312
|
+
const missingDiagnosticSkillGuidance = {
|
|
113
313
|
...valid,
|
|
114
314
|
[DOCUMENTATION_FILE.SEMANTIC_NAVIGATION_SKILL]: valid[DOCUMENTATION_FILE.SEMANTIC_NAVIGATION_SKILL].replace(
|
|
115
|
-
DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_LITERALS[
|
|
315
|
+
DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_LITERALS[4],
|
|
116
316
|
"",
|
|
117
317
|
),
|
|
118
318
|
};
|
|
119
319
|
strictEqual(
|
|
120
|
-
evaluateDocumentation(
|
|
320
|
+
evaluateDocumentation(missingDiagnosticSkillGuidance).some(
|
|
121
321
|
(finding) =>
|
|
122
322
|
finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING &&
|
|
123
|
-
finding.literal === DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_LITERALS[
|
|
323
|
+
finding.literal === DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_LITERALS[4],
|
|
124
324
|
),
|
|
125
325
|
true,
|
|
126
|
-
"Missing
|
|
326
|
+
"Missing unconfirmed-diagnostic skill guidance was accepted",
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
const missingTrustedPublishingPolicy = {
|
|
330
|
+
...valid,
|
|
331
|
+
[DOCUMENTATION_FILE.DISTRIBUTION]: valid[DOCUMENTATION_FILE.DISTRIBUTION].replace(DOCUMENTATION_REQUIREMENT.DISTRIBUTION_LITERALS[2], ""),
|
|
332
|
+
};
|
|
333
|
+
strictEqual(
|
|
334
|
+
evaluateDocumentation(missingTrustedPublishingPolicy).some(
|
|
335
|
+
(finding) =>
|
|
336
|
+
finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING && finding.literal === DOCUMENTATION_REQUIREMENT.DISTRIBUTION_LITERALS[2],
|
|
337
|
+
),
|
|
338
|
+
true,
|
|
339
|
+
"Missing trusted-publishing policy was accepted",
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
const missingPostpublicationOrder = {
|
|
343
|
+
...valid,
|
|
344
|
+
[DOCUMENTATION_FILE.DISTRIBUTION]: valid[DOCUMENTATION_FILE.DISTRIBUTION].replace(DOCUMENTATION_REQUIREMENT.DISTRIBUTION_LITERALS[5], ""),
|
|
345
|
+
};
|
|
346
|
+
strictEqual(
|
|
347
|
+
evaluateDocumentation(missingPostpublicationOrder).some(
|
|
348
|
+
(finding) =>
|
|
349
|
+
finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING && finding.literal === DOCUMENTATION_REQUIREMENT.DISTRIBUTION_LITERALS[5],
|
|
350
|
+
),
|
|
351
|
+
true,
|
|
352
|
+
"Missing postpublication order was accepted",
|
|
127
353
|
);
|
|
128
354
|
|
|
129
355
|
process.stdout.write(
|
|
130
356
|
`${JSON.stringify(
|
|
131
357
|
{
|
|
132
358
|
validDocumentation: "pass",
|
|
359
|
+
lateSetupGuards: "rejected",
|
|
360
|
+
fencedSetupGuards: "rejected",
|
|
361
|
+
invalidSetupOrder: "rejected",
|
|
362
|
+
earlySetupBackground: "rejected",
|
|
363
|
+
lateSetupEntryPoint: "rejected",
|
|
364
|
+
duplicateSetupSection: "rejected",
|
|
365
|
+
duplicateSetupCommand: "rejected",
|
|
366
|
+
duplicateDistributionSetupCommand: "rejected",
|
|
133
367
|
missingHeading: "rejected",
|
|
134
368
|
missingLiteral: "rejected",
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
369
|
+
missingSetupLink: "rejected",
|
|
370
|
+
missingSetupEntryInstruction: "rejected",
|
|
371
|
+
missingSetup: "rejected",
|
|
372
|
+
missingSetupSafetyBoundary: "rejected",
|
|
139
373
|
missingGenericServerName: "rejected",
|
|
374
|
+
missingUserLanguageResponseRule: "rejected",
|
|
375
|
+
missingCodexMarketplaceRefresh: "rejected",
|
|
376
|
+
missingPreToolAuthorityGate: "rejected",
|
|
377
|
+
missingOperationAuthorizationBoundary: "rejected",
|
|
378
|
+
missingVerificationOperation: "rejected",
|
|
379
|
+
missingPositiveDirectHostEvidence: "rejected",
|
|
380
|
+
missingAutomaticGenericFallback: "rejected",
|
|
381
|
+
missingHostApplicationRouteDistinction: "rejected",
|
|
382
|
+
missingHostRouteServerSourceDistinction: "rejected",
|
|
383
|
+
missingCurrentApplicationHostRule: "rejected",
|
|
384
|
+
missingGenericTargetHostRecommendation: "rejected",
|
|
385
|
+
missingRepeatedHostQuestionProhibition: "rejected",
|
|
386
|
+
missingDirectIntegrationMenuProhibition: "rejected",
|
|
387
|
+
missingExecutableIdentityProhibition: "rejected",
|
|
388
|
+
missingMaintenanceRouteOwnershipGuard: "rejected",
|
|
389
|
+
missingEphemeralInstallationProhibition: "rejected",
|
|
390
|
+
missingManualJsonRpcProhibition: "rejected",
|
|
391
|
+
missingHostIntegrationProofBoundary: "rejected",
|
|
392
|
+
missingGenericConfigurationQuestion: "rejected",
|
|
393
|
+
missingGenericCommandRunnerRecord: "rejected",
|
|
394
|
+
missingSandboxNeutralVersionCheckBoundary: "rejected",
|
|
395
|
+
missingGenericLocalInspectionProhibition: "rejected",
|
|
396
|
+
missingHostCommandGrammarGuard: "rejected",
|
|
397
|
+
missingAddCommandGrammarCheckpoint: "rejected",
|
|
398
|
+
missingSavedEntryValidationGuard: "rejected",
|
|
399
|
+
missingFailedHostStatusGuard: "rejected",
|
|
400
|
+
missingBoundedFunctionalTestGuidance: "rejected",
|
|
401
|
+
missingSourceCheckoutVerificationBoundary: "rejected",
|
|
402
|
+
missingSourceCheckoutSearchProhibition: "rejected",
|
|
403
|
+
missingSimilarlyNamedFileEvidenceBoundary: "rejected",
|
|
404
|
+
missingRemovalCommandGrammarGuard: "rejected",
|
|
405
|
+
missingSourceCheckoutRemovalBoundary: "rejected",
|
|
406
|
+
missingSourceCheckoutRegistrationGrammarGuard: "rejected",
|
|
140
407
|
missingCombinationInvariant: "rejected",
|
|
408
|
+
missingDiagnosticExampleGuidance: "rejected",
|
|
409
|
+
missingDiagnosticSkillGuidance: "rejected",
|
|
410
|
+
missingTrustedPublishingPolicy: "rejected",
|
|
411
|
+
missingPostpublicationOrder: "rejected",
|
|
141
412
|
privateCoordination: "rejected",
|
|
142
413
|
},
|
|
143
414
|
null,
|
|
@@ -13,15 +13,49 @@ import {
|
|
|
13
13
|
PUBLIC_ROOT_DOCUMENT,
|
|
14
14
|
} from "./documentation-contract.mjs";
|
|
15
15
|
|
|
16
|
+
function markdownStructuralLines(source) {
|
|
17
|
+
const lines = [];
|
|
18
|
+
let fence;
|
|
19
|
+
for (const line of source.split("\n")) {
|
|
20
|
+
const match = /^\s{0,3}(`{3,}|~{3,})(.*)$/.exec(line);
|
|
21
|
+
if (match && fence === undefined) {
|
|
22
|
+
fence = {marker: match[1][0], length: match[1].length};
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (match && match[1][0] === fence?.marker && match[1].length >= fence.length && match[2].trim() === "") {
|
|
26
|
+
fence = undefined;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (fence === undefined) lines.push(line);
|
|
30
|
+
}
|
|
31
|
+
return lines;
|
|
32
|
+
}
|
|
33
|
+
|
|
16
34
|
function markdownHeadings(source) {
|
|
17
35
|
return new Set(
|
|
18
|
-
source
|
|
19
|
-
.split("\n")
|
|
36
|
+
markdownStructuralLines(source)
|
|
20
37
|
.map((line) => /^#{1,6}\s+(.+?)\s*$/.exec(line)?.[1])
|
|
21
38
|
.filter(Boolean),
|
|
22
39
|
);
|
|
23
40
|
}
|
|
24
41
|
|
|
42
|
+
function levelTwoHeadings(source) {
|
|
43
|
+
return markdownStructuralLines(source)
|
|
44
|
+
.map((line) => /^##\s+(.+?)\s*$/.exec(line)?.[1])
|
|
45
|
+
.filter(Boolean);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function requiredHeadingsAreOrdered(source, requiredHeadings) {
|
|
49
|
+
const headings = levelTwoHeadings(source);
|
|
50
|
+
let nextIndex = 0;
|
|
51
|
+
for (const requiredHeading of requiredHeadings) {
|
|
52
|
+
const index = headings.indexOf(requiredHeading, nextIndex);
|
|
53
|
+
if (index === -1) return false;
|
|
54
|
+
nextIndex = index + 1;
|
|
55
|
+
}
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
|
|
25
59
|
function repositoryPath(root, absolute) {
|
|
26
60
|
return path.relative(root, absolute).split(path.sep).join("/");
|
|
27
61
|
}
|
|
@@ -35,7 +69,8 @@ export function evaluateDocumentation(documents) {
|
|
|
35
69
|
|
|
36
70
|
const headingRequirements = [
|
|
37
71
|
[DOCUMENTATION_FILE.README, DOCUMENTATION_REQUIREMENT.README_HEADINGS],
|
|
38
|
-
[DOCUMENTATION_FILE.
|
|
72
|
+
[DOCUMENTATION_FILE.SETUP, DOCUMENTATION_REQUIREMENT.SETUP_HEADINGS],
|
|
73
|
+
[DOCUMENTATION_FILE.DISTRIBUTION, DOCUMENTATION_REQUIREMENT.DISTRIBUTION_HEADINGS],
|
|
39
74
|
[DOCUMENTATION_FILE.GETTING_STARTED, DOCUMENTATION_REQUIREMENT.GETTING_STARTED_HEADINGS],
|
|
40
75
|
[DOCUMENTATION_FILE.SEMANTIC_NAVIGATION_SKILL, DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_HEADINGS],
|
|
41
76
|
];
|
|
@@ -50,14 +85,30 @@ export function evaluateDocumentation(documents) {
|
|
|
50
85
|
|
|
51
86
|
const readme = documents[DOCUMENTATION_FILE.README];
|
|
52
87
|
if (readme !== undefined) {
|
|
88
|
+
if (levelTwoHeadings(readme)[0] !== DOCUMENTATION_REQUIREMENT.README_FIRST_SECTION) {
|
|
89
|
+
findings.push({
|
|
90
|
+
file: DOCUMENTATION_FILE.README,
|
|
91
|
+
reason: DOCUMENTATION_REASON.SETUP_ENTRY_POINT_NOT_FIRST,
|
|
92
|
+
expectedHeading: DOCUMENTATION_REQUIREMENT.README_FIRST_SECTION,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
53
95
|
for (const link of DOCUMENTATION_REQUIREMENT.README_LINKS) {
|
|
54
96
|
if (readme.includes(`](${link})`)) continue;
|
|
55
97
|
findings.push({file: DOCUMENTATION_FILE.README, reason: DOCUMENTATION_REASON.LINK_MISSING, link});
|
|
56
98
|
}
|
|
57
|
-
for (const literal of DOCUMENTATION_REQUIREMENT.
|
|
99
|
+
for (const literal of DOCUMENTATION_REQUIREMENT.README_SETUP_ENTRY_LITERALS) {
|
|
58
100
|
if (readme.includes(literal)) continue;
|
|
59
101
|
findings.push({file: DOCUMENTATION_FILE.README, reason: DOCUMENTATION_REASON.LITERAL_MISSING, literal});
|
|
60
102
|
}
|
|
103
|
+
const readmeHeadings = markdownHeadings(readme);
|
|
104
|
+
for (const heading of DOCUMENTATION_REQUIREMENT.README_FORBIDDEN_SETUP_HEADINGS) {
|
|
105
|
+
if (!readmeHeadings.has(heading)) continue;
|
|
106
|
+
findings.push({file: DOCUMENTATION_FILE.README, reason: DOCUMENTATION_REASON.DUPLICATE_SETUP_SECTION, heading});
|
|
107
|
+
}
|
|
108
|
+
for (const literal of DOCUMENTATION_REQUIREMENT.README_FORBIDDEN_SETUP_LITERALS) {
|
|
109
|
+
if (!readme.includes(literal)) continue;
|
|
110
|
+
findings.push({file: DOCUMENTATION_FILE.README, reason: DOCUMENTATION_REASON.DUPLICATE_SETUP_COMMAND, literal});
|
|
111
|
+
}
|
|
61
112
|
}
|
|
62
113
|
|
|
63
114
|
const gettingStarted = documents[DOCUMENTATION_FILE.GETTING_STARTED];
|
|
@@ -68,11 +119,32 @@ export function evaluateDocumentation(documents) {
|
|
|
68
119
|
}
|
|
69
120
|
}
|
|
70
121
|
|
|
71
|
-
const
|
|
72
|
-
if (
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
findings.push({
|
|
122
|
+
const setup = documents[DOCUMENTATION_FILE.SETUP];
|
|
123
|
+
if (setup !== undefined) {
|
|
124
|
+
const setupHeadings = levelTwoHeadings(setup);
|
|
125
|
+
if (setupHeadings[0] !== DOCUMENTATION_REQUIREMENT.SETUP_FIRST_SECTION) {
|
|
126
|
+
findings.push({
|
|
127
|
+
file: DOCUMENTATION_FILE.SETUP,
|
|
128
|
+
reason: DOCUMENTATION_REASON.SETUP_GUARDS_NOT_FIRST,
|
|
129
|
+
expectedHeading: DOCUMENTATION_REQUIREMENT.SETUP_FIRST_SECTION,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
if (!requiredHeadingsAreOrdered(setup, DOCUMENTATION_REQUIREMENT.SETUP_HEADINGS)) {
|
|
133
|
+
findings.push({
|
|
134
|
+
file: DOCUMENTATION_FILE.SETUP,
|
|
135
|
+
reason: DOCUMENTATION_REASON.SETUP_SECTION_ORDER_INVALID,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
if (setupHeadings.at(-1) !== DOCUMENTATION_REQUIREMENT.SETUP_LAST_SECTION) {
|
|
139
|
+
findings.push({
|
|
140
|
+
file: DOCUMENTATION_FILE.SETUP,
|
|
141
|
+
reason: DOCUMENTATION_REASON.SETUP_BACKGROUND_NOT_LAST,
|
|
142
|
+
expectedHeading: DOCUMENTATION_REQUIREMENT.SETUP_LAST_SECTION,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
for (const literal of DOCUMENTATION_REQUIREMENT.SETUP_LITERALS) {
|
|
146
|
+
if (setup.includes(literal)) continue;
|
|
147
|
+
findings.push({file: DOCUMENTATION_FILE.SETUP, reason: DOCUMENTATION_REASON.LITERAL_MISSING, literal});
|
|
76
148
|
}
|
|
77
149
|
}
|
|
78
150
|
|
|
@@ -88,6 +160,18 @@ export function evaluateDocumentation(documents) {
|
|
|
88
160
|
}
|
|
89
161
|
}
|
|
90
162
|
|
|
163
|
+
const distribution = documents[DOCUMENTATION_FILE.DISTRIBUTION];
|
|
164
|
+
if (distribution !== undefined) {
|
|
165
|
+
for (const literal of DOCUMENTATION_REQUIREMENT.DISTRIBUTION_LITERALS) {
|
|
166
|
+
if (distribution.includes(literal)) continue;
|
|
167
|
+
findings.push({file: DOCUMENTATION_FILE.DISTRIBUTION, reason: DOCUMENTATION_REASON.LITERAL_MISSING, literal});
|
|
168
|
+
}
|
|
169
|
+
for (const literal of DOCUMENTATION_REQUIREMENT.DISTRIBUTION_FORBIDDEN_SETUP_LITERALS) {
|
|
170
|
+
if (!distribution.includes(literal)) continue;
|
|
171
|
+
findings.push({file: DOCUMENTATION_FILE.DISTRIBUTION, reason: DOCUMENTATION_REASON.DUPLICATE_SETUP_COMMAND, literal});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
91
175
|
for (const [file, source] of Object.entries(documents)) {
|
|
92
176
|
if (PRIVATE_COORDINATION_PATTERN.test(source)) {
|
|
93
177
|
findings.push({file, reason: DOCUMENTATION_REASON.PRIVATE_COORDINATION});
|
|
@@ -21,9 +21,11 @@ import {
|
|
|
21
21
|
DEFINITION_SELECTION_STATUS,
|
|
22
22
|
DIAGNOSTIC_EVIDENCE_REASON,
|
|
23
23
|
DIAGNOSTIC_FRESHNESS,
|
|
24
|
+
DIAGNOSTIC_GUIDANCE,
|
|
24
25
|
DIAGNOSTIC_LANGUAGE,
|
|
25
26
|
DIAGNOSTIC_PROVIDER,
|
|
26
27
|
DIAGNOSTIC_REGION,
|
|
28
|
+
DIAGNOSTIC_RESULT_FIELD,
|
|
27
29
|
DIAGNOSTIC_SEVERITY,
|
|
28
30
|
DOCTOR_CHECK,
|
|
29
31
|
DOCTOR_DISTRIBUTION_ACCEPTED_STATUS,
|
|
@@ -131,6 +133,8 @@ const content =
|
|
|
131
133
|
section("Diagnostic Language", DIAGNOSTIC_LANGUAGE),
|
|
132
134
|
section("Diagnostic Severity", DIAGNOSTIC_SEVERITY),
|
|
133
135
|
section("Diagnostic Evidence Reason", DIAGNOSTIC_EVIDENCE_REASON),
|
|
136
|
+
section("Diagnostic Result Fields", DIAGNOSTIC_RESULT_FIELD),
|
|
137
|
+
section("Diagnostic Guidance", DIAGNOSTIC_GUIDANCE),
|
|
134
138
|
section("Evidence Status", EVIDENCE_STATUS),
|
|
135
139
|
section("Reference Content Freshness", CONTENT_FRESHNESS),
|
|
136
140
|
section("Error Code", ERROR_CODE),
|
|
@@ -163,6 +163,10 @@ try {
|
|
|
163
163
|
const diagnostics = result(await client.callTool({name: TOOL.DIAGNOSTICS, arguments: {file: target, root: workspace}}), TOOL.DIAGNOSTICS);
|
|
164
164
|
if (diagnostics.result.evidence.status === EVIDENCE_STATUS.UNTRUSTED) {
|
|
165
165
|
assert(diagnostics.collection.status === COLLECTION_STATUS.PARTIAL, "Untrusted diagnostics claimed complete collection");
|
|
166
|
+
assert(
|
|
167
|
+
diagnostics.result.diagnosticUse?.usableAsCurrentDocumentDiagnosticEvidence === false,
|
|
168
|
+
"Untrusted diagnostics claimed usability as current-document evidence",
|
|
169
|
+
);
|
|
166
170
|
assert(
|
|
167
171
|
Object.values(DIAGNOSTIC_EVIDENCE_REASON).includes(diagnostics.result.evidence.reason),
|
|
168
172
|
"Untrusted diagnostics used a non-canonical reason",
|
|
@@ -224,6 +228,7 @@ try {
|
|
|
224
228
|
observed: {
|
|
225
229
|
collectionStatus: diagnostics.collection.status,
|
|
226
230
|
evidenceStatus: diagnostics.result.evidence.status,
|
|
231
|
+
usableAsCurrentDocumentDiagnosticEvidence: diagnostics.result.diagnosticUse.usableAsCurrentDocumentDiagnosticEvidence,
|
|
227
232
|
},
|
|
228
233
|
expected: {
|
|
229
234
|
collectionStatus:
|