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.
@@ -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.README_LITERALS.join("\n")}`,
10
- [DOCUMENTATION_FILE.AGENT_SETUP]: `${headingText(DOCUMENTATION_REQUIREMENT.AGENT_SETUP_HEADINGS)}\n${DOCUMENTATION_REQUIREMENT.AGENT_SETUP_LITERALS.join("\n")}`,
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 missingMarketplaceUpgrade = {
151
+ const missingSetupLink = {
52
152
  ...valid,
53
- [DOCUMENTATION_FILE.README]: valid[DOCUMENTATION_FILE.README].replace(DOCUMENTATION_REQUIREMENT.README_LITERALS[0], ""),
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(missingMarketplaceUpgrade).some(
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 marketplace upgrade guidance was accepted",
163
+ "Missing setup README link was accepted",
62
164
  );
63
165
 
64
- const missingAgentSetupLink = {
166
+ const missingSetupEntryInstruction = {
65
167
  ...valid,
66
- [DOCUMENTATION_FILE.README]: valid[DOCUMENTATION_FILE.README].replace(`[doc](${DOCUMENTATION_REQUIREMENT.README_LINKS[0]})`, ""),
168
+ [DOCUMENTATION_FILE.README]: valid[DOCUMENTATION_FILE.README].replace(DOCUMENTATION_REQUIREMENT.README_SETUP_ENTRY_LITERALS[0], ""),
67
169
  };
68
170
  strictEqual(
69
- evaluateDocumentation(missingAgentSetupLink).some(
70
- (finding) => finding.reason === DOCUMENTATION_REASON.LINK_MISSING && finding.link === DOCUMENTATION_REQUIREMENT.README_LINKS[0],
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 agent setup README link was accepted",
177
+ "Missing README setup instruction was accepted",
74
178
  );
75
179
 
76
- const missingAgentSetup = {...valid};
77
- delete missingAgentSetup[DOCUMENTATION_FILE.AGENT_SETUP];
180
+ const missingSetup = {...valid};
181
+ delete missingSetup[DOCUMENTATION_FILE.SETUP];
78
182
  strictEqual(
79
- evaluateDocumentation(missingAgentSetup).some(
80
- (finding) => finding.reason === DOCUMENTATION_REASON.FILE_MISSING && finding.file === DOCUMENTATION_FILE.AGENT_SETUP,
183
+ evaluateDocumentation(missingSetup).some(
184
+ (finding) => finding.reason === DOCUMENTATION_REASON.FILE_MISSING && finding.file === DOCUMENTATION_FILE.SETUP,
81
185
  ),
82
186
  true,
83
- "Missing agent setup guide was accepted",
187
+ "Missing setup guide was accepted",
84
188
  );
85
189
 
86
- const missingAgentSafetyBoundary = {
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.AGENT_SETUP]: valid[DOCUMENTATION_FILE.AGENT_SETUP].replace(DOCUMENTATION_REQUIREMENT.AGENT_SETUP_LITERALS[2], ""),
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(missingAgentSafetyBoundary).some(
287
+ evaluateDocumentation(missingCombinationInvariant).some(
92
288
  (finding) =>
93
- finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING && finding.literal === DOCUMENTATION_REQUIREMENT.AGENT_SETUP_LITERALS[2],
289
+ finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING &&
290
+ finding.literal === DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_LITERALS[1],
94
291
  ),
95
292
  true,
96
- "Missing agent setup safety boundary was accepted",
293
+ "Missing combination-invariant guidance was accepted",
97
294
  );
98
295
 
99
- const missingGenericServerName = {
296
+ const missingDiagnosticExampleGuidance = {
100
297
  ...valid,
101
- [DOCUMENTATION_FILE.AGENT_SETUP]: valid[DOCUMENTATION_FILE.AGENT_SETUP].replace(DOCUMENTATION_REQUIREMENT.AGENT_SETUP_LITERALS[4], ""),
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(missingGenericServerName).some(
304
+ evaluateDocumentation(missingDiagnosticExampleGuidance).some(
105
305
  (finding) =>
106
- finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING && finding.literal === DOCUMENTATION_REQUIREMENT.AGENT_SETUP_LITERALS[4],
306
+ finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING && finding.literal === DOCUMENTATION_REQUIREMENT.GETTING_STARTED_LITERALS[3],
107
307
  ),
108
308
  true,
109
- "Missing generic MCP server name was accepted",
309
+ "Missing untrusted-diagnostic example guidance was accepted",
110
310
  );
111
311
 
112
- const missingCombinationInvariant = {
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[1],
315
+ DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_LITERALS[4],
116
316
  "",
117
317
  ),
118
318
  };
119
319
  strictEqual(
120
- evaluateDocumentation(missingCombinationInvariant).some(
320
+ evaluateDocumentation(missingDiagnosticSkillGuidance).some(
121
321
  (finding) =>
122
322
  finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING &&
123
- finding.literal === DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_LITERALS[1],
323
+ finding.literal === DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_LITERALS[4],
124
324
  ),
125
325
  true,
126
- "Missing combination-invariant guidance was accepted",
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
- missingMarketplaceUpgrade: "rejected",
136
- missingAgentSetupLink: "rejected",
137
- missingAgentSetup: "rejected",
138
- missingAgentSafetyBoundary: "rejected",
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.AGENT_SETUP, DOCUMENTATION_REQUIREMENT.AGENT_SETUP_HEADINGS],
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.README_LITERALS) {
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 agentSetup = documents[DOCUMENTATION_FILE.AGENT_SETUP];
72
- if (agentSetup !== undefined) {
73
- for (const literal of DOCUMENTATION_REQUIREMENT.AGENT_SETUP_LITERALS) {
74
- if (agentSetup.includes(literal)) continue;
75
- findings.push({file: DOCUMENTATION_FILE.AGENT_SETUP, reason: DOCUMENTATION_REASON.LITERAL_MISSING, literal});
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: