semantic-js-mcp 0.10.0 → 0.10.2
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/AGENT_SETUP.md +176 -0
- package/CHANGELOG.md +18 -0
- package/README.md +17 -3
- package/docs/distribution.md +19 -0
- package/docs/getting-started.md +4 -0
- package/package.json +4 -2
- package/protocol.mjs +1 -1
- package/scripts/codex-plugin-verification.mjs +19 -0
- package/scripts/distribution-policy.mjs +2 -0
- package/scripts/documentation-contract.mjs +43 -3
- package/scripts/documentation-gate-smoke.mjs +93 -2
- package/scripts/documentation-gate.mjs +26 -0
- package/scripts/publish-workflow-smoke.mjs +62 -0
- package/scripts/release-contract.mjs +2 -0
- package/scripts/release-smoke.mjs +22 -1
- package/skills/semantic-navigation/SKILL.md +18 -0
- package/skills/semantic-navigation/references/protocol-literals.md +1 -1
package/AGENT_SETUP.md
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Agent Setup
|
|
2
|
+
|
|
3
|
+
This guide is for a coding agent whose user has explicitly requested that it
|
|
4
|
+
install, configure, update, verify, roll back, or remove Semantic JS MCP. It is
|
|
5
|
+
not an instruction to change an environment merely because this file was read.
|
|
6
|
+
|
|
7
|
+
## Scope
|
|
8
|
+
|
|
9
|
+
First identify the application that will run the MCP server. That application
|
|
10
|
+
is the MCP host. Use the verified Codex procedure only for Codex. For every
|
|
11
|
+
other host, use the generic stdio procedure below and translate its fields into
|
|
12
|
+
the host's documented configuration format. Do not guess a configuration file
|
|
13
|
+
or schema.
|
|
14
|
+
|
|
15
|
+
Semantic JS MCP requires Node.js 22 or newer and `rg` (ripgrep) on `PATH`.
|
|
16
|
+
Before changing host configuration, run:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
node --version
|
|
20
|
+
rg --version
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Stop if Node.js is older than 22 or either command is unavailable. Report the
|
|
24
|
+
missing prerequisite. Do not continue with a partial installation.
|
|
25
|
+
|
|
26
|
+
## Safety Boundaries
|
|
27
|
+
|
|
28
|
+
This guide does not override system, developer, organization, repository, or
|
|
29
|
+
host security policy.
|
|
30
|
+
|
|
31
|
+
- Inspect existing configuration before editing it and change only the entry
|
|
32
|
+
approved by the user.
|
|
33
|
+
- Request any approval required for package installation, global writes,
|
|
34
|
+
configuration changes, network access, or process restart.
|
|
35
|
+
- Never request or expose credentials, disable security controls, or execute a
|
|
36
|
+
remote shell pipeline.
|
|
37
|
+
- Preserve every unrelated MCP server and setting. Never replace an entire
|
|
38
|
+
configuration file to add one server.
|
|
39
|
+
- Stop and report the blocker when the requested host or prerequisite cannot be
|
|
40
|
+
verified safely.
|
|
41
|
+
|
|
42
|
+
## Installation Procedure
|
|
43
|
+
|
|
44
|
+
### Codex
|
|
45
|
+
|
|
46
|
+
Use the public marketplace adapter:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
codex plugin marketplace add elnonathan/semantic-js-mcp
|
|
50
|
+
codex plugin add semantic-js-mcp@elnonathan
|
|
51
|
+
codex plugin list
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Start a new Codex session after installation. The plugin supplies both the MCP
|
|
55
|
+
server and the semantic-navigation skill.
|
|
56
|
+
|
|
57
|
+
### Generic Stdio Hosts
|
|
58
|
+
|
|
59
|
+
Use this procedure only when the host supports local MCP servers over stdio. If
|
|
60
|
+
that capability cannot be confirmed from installed help or host documentation,
|
|
61
|
+
stop and report that the host is unsupported or unverified.
|
|
62
|
+
|
|
63
|
+
After the user approves the package installation and configuration change,
|
|
64
|
+
install the executable:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npm install --global semantic-js-mcp
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Inspect the existing host configuration. Add exactly one MCP server with these
|
|
71
|
+
values:
|
|
72
|
+
|
|
73
|
+
- server name: `semanticjsmcp`
|
|
74
|
+
- transport: `stdio`
|
|
75
|
+
- executable or command: `semantic-js-mcp`
|
|
76
|
+
- arguments: one argument, `serve`
|
|
77
|
+
- enabled: `true`, when the host has an enabled field
|
|
78
|
+
|
|
79
|
+
Do not configure a URL, port, shell command, or credentials. The host must
|
|
80
|
+
start `semantic-js-mcp serve` as a local child process and communicate through
|
|
81
|
+
standard input and standard output. Translate the values above into the host's
|
|
82
|
+
documented schema; do not copy a configuration shape from a different host.
|
|
83
|
+
|
|
84
|
+
Save the smallest possible change. Restart or reload the host when its
|
|
85
|
+
documentation requires it.
|
|
86
|
+
|
|
87
|
+
## Verification
|
|
88
|
+
|
|
89
|
+
For an executable installation, record the installed version and run the
|
|
90
|
+
doctor:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
semantic-js-mcp --version
|
|
94
|
+
semantic-js-mcp doctor
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Read the structured doctor result literally:
|
|
98
|
+
|
|
99
|
+
- `pass`: continue to host tool verification.
|
|
100
|
+
- `untrusted`: continue to host tool verification and report the stated
|
|
101
|
+
uncertainty. Do not describe it as `pass`.
|
|
102
|
+
- `blocked` or `fail`: stop, correct the reported problem when authorized, or
|
|
103
|
+
report the blocker. Do not claim a successful installation.
|
|
104
|
+
|
|
105
|
+
Then verify the host discovers tools whose base names begin with `lsp_`. A host
|
|
106
|
+
may display the server name before the base name. Execute one read-only call
|
|
107
|
+
such as `lsp_document_symbols` against a user-approved JavaScript, TypeScript,
|
|
108
|
+
or Vue file. Do not claim successful installation from configuration text
|
|
109
|
+
alone.
|
|
110
|
+
|
|
111
|
+
For a Codex plugin installation without a global executable, verify the version
|
|
112
|
+
with `codex plugin list`, start a new session, and confirm MCP tool discovery
|
|
113
|
+
there.
|
|
114
|
+
|
|
115
|
+
Installation is successful only when the host discovers the MCP tools and one
|
|
116
|
+
read-only `lsp_*` call returns a result. Package installation or configuration
|
|
117
|
+
text alone is not success.
|
|
118
|
+
|
|
119
|
+
## Update And Rollback
|
|
120
|
+
|
|
121
|
+
Update an existing Codex installation by refreshing its marketplace snapshot
|
|
122
|
+
before reinstalling:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
codex plugin marketplace upgrade elnonathan
|
|
126
|
+
codex plugin add semantic-js-mcp@elnonathan
|
|
127
|
+
codex plugin list
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Update a global npm installation with:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
npm install --global semantic-js-mcp@latest
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Before updating, record the working version. To roll back a global installation,
|
|
137
|
+
install that exact known version, for example:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
npm install --global semantic-js-mcp@0.10.1
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
For a host adapter, restore the previously recorded package version and host
|
|
144
|
+
configuration together. Do not replace or remove unrelated configuration while
|
|
145
|
+
rolling back.
|
|
146
|
+
|
|
147
|
+
## Removal
|
|
148
|
+
|
|
149
|
+
For Codex, remove only this plugin:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
codex plugin remove semantic-js-mcp@elnonathan
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Remove the `elnonathan` marketplace only when the user confirms that no other
|
|
156
|
+
installed plugin uses it:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
codex plugin marketplace remove elnonathan
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
For a global executable, remove the package and only its `semanticjsmcp` MCP
|
|
163
|
+
entry:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
npm uninstall --global semantic-js-mcp
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Restart the host and confirm the tools are no longer registered.
|
|
170
|
+
|
|
171
|
+
## Completion Report
|
|
172
|
+
|
|
173
|
+
Report the detected host, installed or removed version, exact configuration
|
|
174
|
+
entry changed, doctor status when available, tool-discovery result, required
|
|
175
|
+
restart, and any remaining uncertainty. Do not include credentials or unrelated
|
|
176
|
+
configuration values.
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,24 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.10.2] - 2026-07-17
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Add `AGENT_SETUP.md` with safe installation, verification, update, rollback, and removal steps for Codex and standard stdio MCP hosts.
|
|
12
|
+
- Expand semantic-navigation guidance for values assembled from multiple sources so agents inspect existing merge rules, related helpers, consumers, and boundary cases before changing precedence or fallback behavior.
|
|
13
|
+
|
|
14
|
+
## [0.10.1] - 2026-07-17
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- Publish release tags to npm through a protected GitHub Actions environment using short-lived OIDC credentials and automatic provenance.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- Document and validate marketplace refresh before reinstalling an existing Codex plugin.
|
|
23
|
+
- Install and verify ripgrep in the trusted-publishing runner before executing the release gate.
|
|
24
|
+
|
|
7
25
|
## [0.10.0] - 2026-07-17
|
|
8
26
|
|
|
9
27
|
### Added
|
package/README.md
CHANGED
|
@@ -6,6 +6,8 @@ Coding agents can generate code quickly, but reliable engineering requires more
|
|
|
6
6
|
|
|
7
7
|
Semantic JS MCP gives coding agents structured, read-only semantic context so they can navigate, review, and change code with explicit uncertainty.
|
|
8
8
|
|
|
9
|
+
When a user explicitly asks a coding agent to install or configure the package, the agent should follow the [agent setup guide](AGENT_SETUP.md).
|
|
10
|
+
|
|
9
11
|
Its scope is intentionally narrow: it strengthens an agent's understanding of symbol identity, types, references, diagnostics, and evidence coverage. It does not replace architectural reasoning, source inspection, dependency analysis, tests, or runtime observation.
|
|
10
12
|
|
|
11
13
|
**Better agents begin with better evidence.**
|
|
@@ -16,7 +18,7 @@ For each file, it identifies the owning workspace and uses the corresponding Typ
|
|
|
16
18
|
|
|
17
19
|
## Project Status
|
|
18
20
|
|
|
19
|
-
Version `0.10.
|
|
21
|
+
Version `0.10.2` is the current release. APIs and result contracts may evolve while the project remains on the `0.x` release line. See the [roadmap](ROADMAP.md) for areas under consideration.
|
|
20
22
|
|
|
21
23
|
## Installation
|
|
22
24
|
|
|
@@ -31,6 +33,18 @@ codex plugin add semantic-js-mcp@elnonathan
|
|
|
31
33
|
|
|
32
34
|
Codex CLI 0.144.4 or newer is required for npm-backed marketplace installation. Start a new Codex session after installation. The plugin bundles both the MCP server configuration and the semantic-navigation skill.
|
|
33
35
|
|
|
36
|
+
### Updating the Codex plugin
|
|
37
|
+
|
|
38
|
+
Codex installs from a local snapshot of each configured marketplace. Refresh that snapshot before reinstalling so `plugin add` resolves the version currently declared by the marketplace:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
codex plugin marketplace upgrade elnonathan
|
|
42
|
+
codex plugin add semantic-js-mcp@elnonathan
|
|
43
|
+
codex plugin list
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`plugin add` reinstalls the plugin; a separate removal is not required. Start a new Codex session after the command reports the expected version.
|
|
47
|
+
|
|
34
48
|
### MCP executable
|
|
35
49
|
|
|
36
50
|
Install the package globally when using an MCP host that accepts a command-based stdio configuration:
|
|
@@ -78,7 +92,7 @@ For an MCP host that accepts a direct stdio configuration, point it at the check
|
|
|
78
92
|
```json
|
|
79
93
|
{
|
|
80
94
|
"mcpServers": {
|
|
81
|
-
"
|
|
95
|
+
"semanticjsmcp": {
|
|
82
96
|
"command": "node",
|
|
83
97
|
"args": ["/absolute/path/to/semantic-js-mcp/server.mjs"]
|
|
84
98
|
}
|
|
@@ -93,7 +107,7 @@ When the package executable is available on the host's `PATH`, the equivalent st
|
|
|
93
107
|
```json
|
|
94
108
|
{
|
|
95
109
|
"mcpServers": {
|
|
96
|
-
"
|
|
110
|
+
"semanticjsmcp": {
|
|
97
111
|
"command": "semantic-js-mcp",
|
|
98
112
|
"args": ["serve"]
|
|
99
113
|
}
|
package/docs/distribution.md
CHANGED
|
@@ -11,6 +11,16 @@ codex plugin add semantic-js-mcp@elnonathan
|
|
|
11
11
|
|
|
12
12
|
The marketplace entry pins a concrete npm package version. Codex CLI 0.144.4 is the minimum verified client for npm-backed marketplace installation. A release updates the package version, plugin manifest, and marketplace entry together.
|
|
13
13
|
|
|
14
|
+
For an existing marketplace installation, refresh its local snapshot before reinstalling the plugin:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
codex plugin marketplace upgrade elnonathan
|
|
18
|
+
codex plugin add semantic-js-mcp@elnonathan
|
|
19
|
+
codex plugin list
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`plugin add` reinstalls the plugin selected by the refreshed marketplace. Start a new Codex session after the listed version matches the release being installed.
|
|
23
|
+
|
|
14
24
|
## Executable
|
|
15
25
|
|
|
16
26
|
```bash
|
|
@@ -47,6 +57,7 @@ npm run check
|
|
|
47
57
|
npm run check:runtime
|
|
48
58
|
npm run check:documentation
|
|
49
59
|
npm run smoke:ci
|
|
60
|
+
npm run smoke:publish
|
|
50
61
|
npm run smoke:negative
|
|
51
62
|
npm run smoke:doctor
|
|
52
63
|
npm run smoke
|
|
@@ -68,6 +79,14 @@ The local release gate runs every configured source, runtime, semantic, evaluati
|
|
|
68
79
|
|
|
69
80
|
Postpublication verification requires an explicit version and the matching `v<version>` repository tag. It queries that immutable registry version, installs it with a fresh temporary npm cache and consumer project, verifies the installed executable and manifest, then runs the installed doctor to cover MCP startup, tool discovery, TypeScript evidence, and Vue navigation. It also installs the plugin from the tag-pinned marketplace inside a temporary `CODEX_HOME` and verifies the enabled plugin version. Temporary state is removed afterward. An unavailable registry, marketplace, or network is reported as `blocked`.
|
|
70
81
|
|
|
82
|
+
## npm Trusted Publishing
|
|
83
|
+
|
|
84
|
+
The `publish.yml` workflow publishes tags matching `v*` from a GitHub-hosted Node.js 24 runner. It requires the protected `npm-publish` environment, verifies that the tag exactly matches the package version, runs the complete release gate, and publishes through npm Trusted Publishing with OIDC. No long-lived npm token is used. npm generates provenance automatically for the public package.
|
|
85
|
+
|
|
86
|
+
Configure the npm trusted publisher for GitHub user `elnonathan`, repository `semantic-js-mcp`, workflow filename `publish.yml`, and environment `npm-publish`. Allow `npm publish` only. In package publishing access, require two-factor authentication and disallow tokens.
|
|
87
|
+
|
|
88
|
+
Create and push the matching `v<version>` tag only after the release commit is on `main` and its CI matrix passes. Approve the protected environment deployment, verify the published package, and then create the matching GitHub release.
|
|
89
|
+
|
|
71
90
|
## Continuous Integration
|
|
72
91
|
|
|
73
92
|
GitHub Actions runs the complete release gate on Node.js 22 and 24 across Ubuntu, macOS, and Windows. A second Linux matrix runs the same gate on Node.js 22 across Ubuntu, Fedora, Arch Linux, and openSUSE. The gate verifies ripgrep before running static checks, runtime resolution, documentation, CI policy, negative fixtures, doctor, TypeScript and Vue semantics, provider lifecycle, package installation, and the short benchmark.
|
package/docs/getting-started.md
CHANGED
|
@@ -10,6 +10,10 @@ Semantic JS MCP adds static semantic evidence to an agent's existing code invest
|
|
|
10
10
|
|
|
11
11
|
> Review the authentication changes in this diff. Use semantic evidence to verify every material symbol and cross-workspace reference, corroborate reachability with text search and direct source inspection, and distinguish introduced defects from pre-existing behavior.
|
|
12
12
|
|
|
13
|
+
## Review Combination Logic
|
|
14
|
+
|
|
15
|
+
> Review this precedence, fallback, normalization, or merge change. Identify every producer of the combined domain value, recover the invariant encoded by existing helpers and direct consumers, verify the discovered symbols and references with semantic evidence, and test a boundary case that could disprove the proposed rule.
|
|
16
|
+
|
|
13
17
|
## Check Current Diagnostics
|
|
14
18
|
|
|
15
19
|
> Check current diagnostics for `src/handler.ts`. Treat the file as clean only if the language server confirms the current document snapshot; otherwise report the result as untrusted and explain the required follow-up.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "semantic-js-mcp",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.2",
|
|
4
4
|
"description": "Semantic understanding of JavaScript codebases for AI agents",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
".codex-plugin/",
|
|
29
29
|
".mcp.json",
|
|
30
30
|
".prettierrc.json",
|
|
31
|
+
"AGENT_SETUP.md",
|
|
31
32
|
"CHANGELOG.md",
|
|
32
33
|
"CONTRIBUTING.md",
|
|
33
34
|
"ROADMAP.md",
|
|
@@ -44,13 +45,14 @@
|
|
|
44
45
|
"node": ">=22"
|
|
45
46
|
},
|
|
46
47
|
"scripts": {
|
|
47
|
-
"check": "npm run format:check && node --check protocol.mjs && node --check server.mjs && node --check cli.mjs && node --check lib/runtime.mjs && node --check lib/doctor.mjs && node --check lib/semantic-evidence.mjs && node --check lib/pending-requests.mjs && node --check lib/stable-collection.mjs && node --check scripts/generate-protocol-reference.mjs && node --check scripts/check-protocol-literals.mjs && node --check scripts/check-runtime.mjs && node --check scripts/documentation-contract.mjs && node --check scripts/documentation-gate.mjs && node --check scripts/documentation-gate-smoke.mjs && node --check scripts/semantic-js-mcp-ci.mjs && node --check scripts/ci-smoke.mjs && node --check scripts/negative-verification-smoke.mjs && node --check scripts/doctor-smoke.mjs && node --check scripts/smoke.mjs && node --check scripts/vue-smoke.mjs && node --check scripts/lifecycle-smoke.mjs && node --check scripts/lifecycle-memory-contract.mjs && node --check scripts/lifecycle-memory-observer.mjs && node --check scripts/lifecycle-memory-benchmark.mjs && node --check scripts/benchmark.mjs && node --check scripts/distribution-policy.mjs && node --check scripts/distribution-smoke.mjs && node --check scripts/release-contract.mjs && node --check scripts/release-verify.mjs && node --check scripts/release-smoke.mjs && node --check scripts/postpublication-smoke.mjs && node --check scripts/codex-plugin-verification.mjs && node --check scripts/agent-evaluation-contract.mjs && node --check scripts/agent-evaluation.mjs && node --check scripts/agent-evaluation-smoke.mjs && node --check scripts/repository-matrix-contract.mjs && node --check scripts/repository-matrix.mjs && node --check scripts/repository-matrix-smoke.mjs && node scripts/generate-protocol-reference.mjs --check && node scripts/check-protocol-literals.mjs && node scripts/documentation-gate-smoke.mjs",
|
|
48
|
+
"check": "npm run format:check && node --check protocol.mjs && node --check server.mjs && node --check cli.mjs && node --check lib/runtime.mjs && node --check lib/doctor.mjs && node --check lib/semantic-evidence.mjs && node --check lib/pending-requests.mjs && node --check lib/stable-collection.mjs && node --check scripts/generate-protocol-reference.mjs && node --check scripts/check-protocol-literals.mjs && node --check scripts/check-runtime.mjs && node --check scripts/documentation-contract.mjs && node --check scripts/documentation-gate.mjs && node --check scripts/documentation-gate-smoke.mjs && node --check scripts/semantic-js-mcp-ci.mjs && node --check scripts/ci-smoke.mjs && node --check scripts/publish-workflow-smoke.mjs && node --check scripts/negative-verification-smoke.mjs && node --check scripts/doctor-smoke.mjs && node --check scripts/smoke.mjs && node --check scripts/vue-smoke.mjs && node --check scripts/lifecycle-smoke.mjs && node --check scripts/lifecycle-memory-contract.mjs && node --check scripts/lifecycle-memory-observer.mjs && node --check scripts/lifecycle-memory-benchmark.mjs && node --check scripts/benchmark.mjs && node --check scripts/distribution-policy.mjs && node --check scripts/distribution-smoke.mjs && node --check scripts/release-contract.mjs && node --check scripts/release-verify.mjs && node --check scripts/release-smoke.mjs && node --check scripts/postpublication-smoke.mjs && node --check scripts/codex-plugin-verification.mjs && node --check scripts/agent-evaluation-contract.mjs && node --check scripts/agent-evaluation.mjs && node --check scripts/agent-evaluation-smoke.mjs && node --check scripts/repository-matrix-contract.mjs && node --check scripts/repository-matrix.mjs && node --check scripts/repository-matrix-smoke.mjs && node scripts/generate-protocol-reference.mjs --check && node scripts/check-protocol-literals.mjs && node scripts/documentation-gate-smoke.mjs",
|
|
48
49
|
"check:runtime": "node scripts/check-runtime.mjs",
|
|
49
50
|
"check:documentation": "node scripts/documentation-gate.mjs",
|
|
50
51
|
"doctor": "node cli.mjs doctor",
|
|
51
52
|
"ci:evaluate": "node scripts/semantic-js-mcp-ci.mjs",
|
|
52
53
|
"smoke": "node scripts/smoke.mjs",
|
|
53
54
|
"smoke:ci": "node scripts/ci-smoke.mjs",
|
|
55
|
+
"smoke:publish": "node scripts/publish-workflow-smoke.mjs",
|
|
54
56
|
"smoke:negative": "node scripts/negative-verification-smoke.mjs",
|
|
55
57
|
"smoke:doctor": "node scripts/doctor-smoke.mjs",
|
|
56
58
|
"smoke:vue": "node scripts/vue-smoke.mjs",
|
package/protocol.mjs
CHANGED
|
@@ -408,7 +408,7 @@ export const RESULT_SCHEMA = Object.freeze({
|
|
|
408
408
|
VERSION: 7,
|
|
409
409
|
});
|
|
410
410
|
|
|
411
|
-
export const SERVER_VERSION = "0.10.
|
|
411
|
+
export const SERVER_VERSION = "0.10.2";
|
|
412
412
|
|
|
413
413
|
export const REQUIRED_RUNTIME_COMPONENT = Object.freeze({
|
|
414
414
|
TYPESCRIPT_LANGUAGE_SERVER: "typescript-language-server/lib/cli.mjs",
|
|
@@ -71,6 +71,25 @@ export async function verifyCodexPlugin({version, workspace, spawn, runCommand})
|
|
|
71
71
|
];
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
const upgrade = await execute(CODEX_DISTRIBUTION.EXECUTABLE, [
|
|
75
|
+
CODEX_DISTRIBUTION.PLUGIN_COMMAND,
|
|
76
|
+
CODEX_DISTRIBUTION.MARKETPLACE_COMMAND,
|
|
77
|
+
CODEX_DISTRIBUTION.UPGRADE_COMMAND,
|
|
78
|
+
CODEX_DISTRIBUTION.MARKETPLACE_NAME,
|
|
79
|
+
CODEX_DISTRIBUTION.JSON_ARGUMENT,
|
|
80
|
+
]);
|
|
81
|
+
if (upgrade.exitCode !== CI_EXIT_CODE.PASS) {
|
|
82
|
+
const unavailable = upgrade.exitCode === undefined || networkUnavailable(upgrade);
|
|
83
|
+
return [
|
|
84
|
+
check(
|
|
85
|
+
RELEASE_CHECK.CODEX_MARKETPLACE,
|
|
86
|
+
unavailable ? CI_STATUS.BLOCKED : CI_STATUS.FAIL,
|
|
87
|
+
unavailable ? RELEASE_REASON.CODEX_MARKETPLACE_UNAVAILABLE : RELEASE_REASON.CODEX_MARKETPLACE_FAILED,
|
|
88
|
+
{marketplaceRef, message: failureMessage(upgrade)},
|
|
89
|
+
),
|
|
90
|
+
];
|
|
91
|
+
}
|
|
92
|
+
|
|
74
93
|
const checks = [check(RELEASE_CHECK.CODEX_MARKETPLACE, CI_STATUS.PASS, RELEASE_REASON.CHECK_COMPLETED, {marketplaceRef})];
|
|
75
94
|
const installation = await execute(CODEX_DISTRIBUTION.EXECUTABLE, [
|
|
76
95
|
CODEX_DISTRIBUTION.PLUGIN_COMMAND,
|
|
@@ -2,6 +2,7 @@ export const REQUIRED_PACKAGE_FILE = Object.freeze({
|
|
|
2
2
|
PACKAGE_MANIFEST: "package.json",
|
|
3
3
|
PRETTIER_CONFIGURATION: ".prettierrc.json",
|
|
4
4
|
README: "README.md",
|
|
5
|
+
AGENT_SETUP: "AGENT_SETUP.md",
|
|
5
6
|
LICENSE: "LICENSE",
|
|
6
7
|
CLI: "cli.mjs",
|
|
7
8
|
SERVER: "server.mjs",
|
|
@@ -30,6 +31,7 @@ export const CODEX_DISTRIBUTION = Object.freeze({
|
|
|
30
31
|
PLUGIN_COMMAND: "plugin",
|
|
31
32
|
MARKETPLACE_COMMAND: "marketplace",
|
|
32
33
|
ADD_COMMAND: "add",
|
|
34
|
+
UPGRADE_COMMAND: "upgrade",
|
|
33
35
|
LIST_COMMAND: "list",
|
|
34
36
|
REF_ARGUMENT: "--ref",
|
|
35
37
|
JSON_ARGUMENT: "--json",
|
|
@@ -2,28 +2,68 @@ import {DOCTOR_REASON, SEMANTIC_EVIDENCE_FOLLOW_UP_REASON} from "../protocol.mjs
|
|
|
2
2
|
|
|
3
3
|
export const DOCUMENTATION_FILE = Object.freeze({
|
|
4
4
|
README: "README.md",
|
|
5
|
+
AGENT_SETUP: "AGENT_SETUP.md",
|
|
5
6
|
GETTING_STARTED: "docs/getting-started.md",
|
|
7
|
+
SEMANTIC_NAVIGATION_SKILL: "skills/semantic-navigation/SKILL.md",
|
|
6
8
|
CONTRIBUTING: "CONTRIBUTING.md",
|
|
7
9
|
SECURITY: "SECURITY.md",
|
|
8
10
|
});
|
|
9
11
|
|
|
10
|
-
export const PUBLIC_ROOT_DOCUMENT = Object.freeze([
|
|
12
|
+
export const PUBLIC_ROOT_DOCUMENT = Object.freeze([
|
|
13
|
+
"AGENT_SETUP.md",
|
|
14
|
+
"CHANGELOG.md",
|
|
15
|
+
"CONTRIBUTING.md",
|
|
16
|
+
"README.md",
|
|
17
|
+
"ROADMAP.md",
|
|
18
|
+
"SECURITY.md",
|
|
19
|
+
]);
|
|
11
20
|
|
|
12
21
|
export const PUBLIC_DOCUMENT_DIRECTORY = Object.freeze(["docs", "skills"]);
|
|
13
22
|
|
|
14
23
|
export const DOCUMENTATION_REQUIREMENT = Object.freeze({
|
|
15
|
-
README_HEADINGS: Object.freeze([
|
|
24
|
+
README_HEADINGS: Object.freeze([
|
|
25
|
+
"Installation",
|
|
26
|
+
"Updating the Codex plugin",
|
|
27
|
+
"Runtime",
|
|
28
|
+
"Verification",
|
|
29
|
+
"Current Limitations",
|
|
30
|
+
"Reporting Problems",
|
|
31
|
+
]),
|
|
32
|
+
README_LITERALS: Object.freeze(["codex plugin marketplace upgrade elnonathan", "codex plugin add semantic-js-mcp@elnonathan"]),
|
|
16
33
|
GETTING_STARTED_HEADINGS: Object.freeze([
|
|
17
34
|
"Trace A Symbol",
|
|
18
35
|
"Review A Security-Sensitive Change",
|
|
36
|
+
"Review Combination Logic",
|
|
19
37
|
"Check Current Diagnostics",
|
|
20
38
|
"Complete Evidence",
|
|
21
39
|
"Partial Evidence",
|
|
22
40
|
"Untrusted Diagnostics",
|
|
23
41
|
"Startup Failure",
|
|
24
42
|
]),
|
|
25
|
-
|
|
43
|
+
AGENT_SETUP_HEADINGS: Object.freeze([
|
|
44
|
+
"Scope",
|
|
45
|
+
"Safety Boundaries",
|
|
46
|
+
"Installation Procedure",
|
|
47
|
+
"Verification",
|
|
48
|
+
"Update And Rollback",
|
|
49
|
+
"Removal",
|
|
50
|
+
"Completion Report",
|
|
51
|
+
]),
|
|
52
|
+
SEMANTIC_NAVIGATION_SKILL_HEADINGS: Object.freeze(["Preserve Combination Invariants"]),
|
|
53
|
+
README_LINKS: Object.freeze(["AGENT_SETUP.md", "docs/getting-started.md", "CONTRIBUTING.md", "SECURITY.md"]),
|
|
26
54
|
GETTING_STARTED_LITERALS: Object.freeze([SEMANTIC_EVIDENCE_FOLLOW_UP_REASON.COLLECTION_PARTIAL, DOCTOR_REASON.RUNTIME_COMPONENT_MISSING]),
|
|
55
|
+
AGENT_SETUP_LITERALS: Object.freeze([
|
|
56
|
+
"semantic-js-mcp doctor",
|
|
57
|
+
"semantic-js-mcp serve",
|
|
58
|
+
"does not override system, developer, organization, repository",
|
|
59
|
+
"npm uninstall --global semantic-js-mcp",
|
|
60
|
+
"server name: `semanticjsmcp`",
|
|
61
|
+
]),
|
|
62
|
+
SEMANTIC_NAVIGATION_SKILL_LITERALS: Object.freeze([
|
|
63
|
+
"Identify every producer of the value",
|
|
64
|
+
"boundary case that could disprove",
|
|
65
|
+
"conceptual relationships that an LSP cannot establish",
|
|
66
|
+
]),
|
|
27
67
|
});
|
|
28
68
|
|
|
29
69
|
export const DOCUMENTATION_REASON = Object.freeze({
|
|
@@ -6,8 +6,10 @@ 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")}`,
|
|
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")}`,
|
|
10
11
|
[DOCUMENTATION_FILE.GETTING_STARTED]: `${headingText(DOCUMENTATION_REQUIREMENT.GETTING_STARTED_HEADINGS)}\n${DOCUMENTATION_REQUIREMENT.GETTING_STARTED_LITERALS.join("\n")}`,
|
|
12
|
+
[DOCUMENTATION_FILE.SEMANTIC_NAVIGATION_SKILL]: `${headingText(DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_HEADINGS)}\n${DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_LITERALS.join("\n")}`,
|
|
11
13
|
[DOCUMENTATION_FILE.CONTRIBUTING]: "# Contributing",
|
|
12
14
|
[DOCUMENTATION_FILE.SECURITY]: "# Security",
|
|
13
15
|
};
|
|
@@ -46,9 +48,98 @@ strictEqual(
|
|
|
46
48
|
"Missing canonical example literal was accepted",
|
|
47
49
|
);
|
|
48
50
|
|
|
51
|
+
const missingMarketplaceUpgrade = {
|
|
52
|
+
...valid,
|
|
53
|
+
[DOCUMENTATION_FILE.README]: valid[DOCUMENTATION_FILE.README].replace(DOCUMENTATION_REQUIREMENT.README_LITERALS[0], ""),
|
|
54
|
+
};
|
|
55
|
+
strictEqual(
|
|
56
|
+
evaluateDocumentation(missingMarketplaceUpgrade).some(
|
|
57
|
+
(finding) =>
|
|
58
|
+
finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING && finding.literal === DOCUMENTATION_REQUIREMENT.README_LITERALS[0],
|
|
59
|
+
),
|
|
60
|
+
true,
|
|
61
|
+
"Missing marketplace upgrade guidance was accepted",
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const missingAgentSetupLink = {
|
|
65
|
+
...valid,
|
|
66
|
+
[DOCUMENTATION_FILE.README]: valid[DOCUMENTATION_FILE.README].replace(`[doc](${DOCUMENTATION_REQUIREMENT.README_LINKS[0]})`, ""),
|
|
67
|
+
};
|
|
68
|
+
strictEqual(
|
|
69
|
+
evaluateDocumentation(missingAgentSetupLink).some(
|
|
70
|
+
(finding) => finding.reason === DOCUMENTATION_REASON.LINK_MISSING && finding.link === DOCUMENTATION_REQUIREMENT.README_LINKS[0],
|
|
71
|
+
),
|
|
72
|
+
true,
|
|
73
|
+
"Missing agent setup README link was accepted",
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const missingAgentSetup = {...valid};
|
|
77
|
+
delete missingAgentSetup[DOCUMENTATION_FILE.AGENT_SETUP];
|
|
78
|
+
strictEqual(
|
|
79
|
+
evaluateDocumentation(missingAgentSetup).some(
|
|
80
|
+
(finding) => finding.reason === DOCUMENTATION_REASON.FILE_MISSING && finding.file === DOCUMENTATION_FILE.AGENT_SETUP,
|
|
81
|
+
),
|
|
82
|
+
true,
|
|
83
|
+
"Missing agent setup guide was accepted",
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const missingAgentSafetyBoundary = {
|
|
87
|
+
...valid,
|
|
88
|
+
[DOCUMENTATION_FILE.AGENT_SETUP]: valid[DOCUMENTATION_FILE.AGENT_SETUP].replace(DOCUMENTATION_REQUIREMENT.AGENT_SETUP_LITERALS[2], ""),
|
|
89
|
+
};
|
|
90
|
+
strictEqual(
|
|
91
|
+
evaluateDocumentation(missingAgentSafetyBoundary).some(
|
|
92
|
+
(finding) =>
|
|
93
|
+
finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING && finding.literal === DOCUMENTATION_REQUIREMENT.AGENT_SETUP_LITERALS[2],
|
|
94
|
+
),
|
|
95
|
+
true,
|
|
96
|
+
"Missing agent setup safety boundary was accepted",
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const missingGenericServerName = {
|
|
100
|
+
...valid,
|
|
101
|
+
[DOCUMENTATION_FILE.AGENT_SETUP]: valid[DOCUMENTATION_FILE.AGENT_SETUP].replace(DOCUMENTATION_REQUIREMENT.AGENT_SETUP_LITERALS[4], ""),
|
|
102
|
+
};
|
|
103
|
+
strictEqual(
|
|
104
|
+
evaluateDocumentation(missingGenericServerName).some(
|
|
105
|
+
(finding) =>
|
|
106
|
+
finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING && finding.literal === DOCUMENTATION_REQUIREMENT.AGENT_SETUP_LITERALS[4],
|
|
107
|
+
),
|
|
108
|
+
true,
|
|
109
|
+
"Missing generic MCP server name was accepted",
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const missingCombinationInvariant = {
|
|
113
|
+
...valid,
|
|
114
|
+
[DOCUMENTATION_FILE.SEMANTIC_NAVIGATION_SKILL]: valid[DOCUMENTATION_FILE.SEMANTIC_NAVIGATION_SKILL].replace(
|
|
115
|
+
DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_LITERALS[1],
|
|
116
|
+
"",
|
|
117
|
+
),
|
|
118
|
+
};
|
|
119
|
+
strictEqual(
|
|
120
|
+
evaluateDocumentation(missingCombinationInvariant).some(
|
|
121
|
+
(finding) =>
|
|
122
|
+
finding.reason === DOCUMENTATION_REASON.LITERAL_MISSING &&
|
|
123
|
+
finding.literal === DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_LITERALS[1],
|
|
124
|
+
),
|
|
125
|
+
true,
|
|
126
|
+
"Missing combination-invariant guidance was accepted",
|
|
127
|
+
);
|
|
128
|
+
|
|
49
129
|
process.stdout.write(
|
|
50
130
|
`${JSON.stringify(
|
|
51
|
-
{
|
|
131
|
+
{
|
|
132
|
+
validDocumentation: "pass",
|
|
133
|
+
missingHeading: "rejected",
|
|
134
|
+
missingLiteral: "rejected",
|
|
135
|
+
missingMarketplaceUpgrade: "rejected",
|
|
136
|
+
missingAgentSetupLink: "rejected",
|
|
137
|
+
missingAgentSetup: "rejected",
|
|
138
|
+
missingAgentSafetyBoundary: "rejected",
|
|
139
|
+
missingGenericServerName: "rejected",
|
|
140
|
+
missingCombinationInvariant: "rejected",
|
|
141
|
+
privateCoordination: "rejected",
|
|
142
|
+
},
|
|
52
143
|
null,
|
|
53
144
|
2,
|
|
54
145
|
)}\n`,
|
|
@@ -35,7 +35,9 @@ export function evaluateDocumentation(documents) {
|
|
|
35
35
|
|
|
36
36
|
const headingRequirements = [
|
|
37
37
|
[DOCUMENTATION_FILE.README, DOCUMENTATION_REQUIREMENT.README_HEADINGS],
|
|
38
|
+
[DOCUMENTATION_FILE.AGENT_SETUP, DOCUMENTATION_REQUIREMENT.AGENT_SETUP_HEADINGS],
|
|
38
39
|
[DOCUMENTATION_FILE.GETTING_STARTED, DOCUMENTATION_REQUIREMENT.GETTING_STARTED_HEADINGS],
|
|
40
|
+
[DOCUMENTATION_FILE.SEMANTIC_NAVIGATION_SKILL, DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_HEADINGS],
|
|
39
41
|
];
|
|
40
42
|
for (const [file, requiredHeadings] of headingRequirements) {
|
|
41
43
|
if (documents[file] === undefined) continue;
|
|
@@ -52,6 +54,10 @@ export function evaluateDocumentation(documents) {
|
|
|
52
54
|
if (readme.includes(`](${link})`)) continue;
|
|
53
55
|
findings.push({file: DOCUMENTATION_FILE.README, reason: DOCUMENTATION_REASON.LINK_MISSING, link});
|
|
54
56
|
}
|
|
57
|
+
for (const literal of DOCUMENTATION_REQUIREMENT.README_LITERALS) {
|
|
58
|
+
if (readme.includes(literal)) continue;
|
|
59
|
+
findings.push({file: DOCUMENTATION_FILE.README, reason: DOCUMENTATION_REASON.LITERAL_MISSING, literal});
|
|
60
|
+
}
|
|
55
61
|
}
|
|
56
62
|
|
|
57
63
|
const gettingStarted = documents[DOCUMENTATION_FILE.GETTING_STARTED];
|
|
@@ -62,6 +68,26 @@ export function evaluateDocumentation(documents) {
|
|
|
62
68
|
}
|
|
63
69
|
}
|
|
64
70
|
|
|
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});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const semanticNavigationSkill = documents[DOCUMENTATION_FILE.SEMANTIC_NAVIGATION_SKILL];
|
|
80
|
+
if (semanticNavigationSkill !== undefined) {
|
|
81
|
+
for (const literal of DOCUMENTATION_REQUIREMENT.SEMANTIC_NAVIGATION_SKILL_LITERALS) {
|
|
82
|
+
if (semanticNavigationSkill.includes(literal)) continue;
|
|
83
|
+
findings.push({
|
|
84
|
+
file: DOCUMENTATION_FILE.SEMANTIC_NAVIGATION_SKILL,
|
|
85
|
+
reason: DOCUMENTATION_REASON.LITERAL_MISSING,
|
|
86
|
+
literal,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
65
91
|
for (const [file, source] of Object.entries(documents)) {
|
|
66
92
|
if (PRIVATE_COORDINATION_PATTERN.test(source)) {
|
|
67
93
|
findings.push({file, reason: DOCUMENTATION_REASON.PRIVATE_COORDINATION});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import {deepStrictEqual, strictEqual} from "node:assert";
|
|
4
|
+
import {readFile} from "node:fs/promises";
|
|
5
|
+
import {fileURLToPath} from "node:url";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import {parse} from "yaml";
|
|
8
|
+
|
|
9
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
10
|
+
const workflowFile = path.join(root, ".github", "workflows", "publish.yml");
|
|
11
|
+
const workflowText = await readFile(workflowFile, "utf8");
|
|
12
|
+
const workflow = parse(workflowText);
|
|
13
|
+
const publish = workflow.jobs?.publish;
|
|
14
|
+
const steps = publish?.steps || [];
|
|
15
|
+
const setupNode = steps.find((step) => step.uses === "actions/setup-node@v6");
|
|
16
|
+
const commands = steps.flatMap((step) => (typeof step.run === "string" ? [step.run] : []));
|
|
17
|
+
|
|
18
|
+
deepStrictEqual(Object.keys(workflow.on || {}), ["push"], "Publish workflow must only run from tag pushes");
|
|
19
|
+
deepStrictEqual(workflow.on?.push?.tags, ["v*"], "Publish workflow must run only for release tags");
|
|
20
|
+
deepStrictEqual(
|
|
21
|
+
workflow.permissions,
|
|
22
|
+
{contents: "read", "id-token": "write"},
|
|
23
|
+
"Publish workflow must grant only repository read and OIDC permissions",
|
|
24
|
+
);
|
|
25
|
+
strictEqual(publish?.if, "github.repository == 'elnonathan/semantic-js-mcp'", "Publishing must be restricted to the canonical repository");
|
|
26
|
+
strictEqual(publish?.["runs-on"], "ubuntu-latest", "Publishing must use a GitHub-hosted runner");
|
|
27
|
+
strictEqual(publish?.environment, "npm-publish", "Publishing must use the protected npm environment");
|
|
28
|
+
strictEqual(workflow.concurrency?.["cancel-in-progress"], false, "An active publication must never be cancelled by another tag");
|
|
29
|
+
strictEqual(setupNode?.with?.["node-version"], 24, "Publishing must use Node.js 24");
|
|
30
|
+
strictEqual(setupNode?.with?.["registry-url"], "https://registry.npmjs.org", "Publishing must target npmjs.org");
|
|
31
|
+
strictEqual(setupNode?.with?.["package-manager-cache"], false, "Release builds must not use a package-manager cache");
|
|
32
|
+
strictEqual(
|
|
33
|
+
commands.includes("sudo apt-get update && sudo apt-get install --yes ripgrep"),
|
|
34
|
+
true,
|
|
35
|
+
"Publishing must install the required ripgrep executable",
|
|
36
|
+
);
|
|
37
|
+
strictEqual(commands.includes("rg --version"), true, "Publishing must verify ripgrep before the release gate");
|
|
38
|
+
strictEqual(commands.includes("npm install --global npm@11.17.0"), true, "Publishing must install the verified npm CLI");
|
|
39
|
+
strictEqual(commands.includes("npm ci"), true, "Publishing must install the locked dependency tree");
|
|
40
|
+
strictEqual(commands.includes("npm run release:verify"), true, "Publishing must run the complete release gate");
|
|
41
|
+
strictEqual(commands.includes("npm publish --access public"), true, "Publishing must use the public OIDC publish path");
|
|
42
|
+
strictEqual(
|
|
43
|
+
commands.some((command) => command.includes("GITHUB_REF_NAME") && command.includes("package.json")),
|
|
44
|
+
true,
|
|
45
|
+
"Publishing must reject a tag that differs from the package version",
|
|
46
|
+
);
|
|
47
|
+
strictEqual(workflowText.includes("NODE_AUTH_TOKEN"), false, "OIDC publishing must not use an npm token");
|
|
48
|
+
strictEqual(workflowText.includes("secrets."), false, "OIDC publishing must not read repository secrets");
|
|
49
|
+
|
|
50
|
+
process.stdout.write(
|
|
51
|
+
`${JSON.stringify(
|
|
52
|
+
{
|
|
53
|
+
workflow: "publish.yml",
|
|
54
|
+
trigger: "release-tags",
|
|
55
|
+
authentication: "oidc",
|
|
56
|
+
environment: publish.environment,
|
|
57
|
+
tokenSecret: "absent",
|
|
58
|
+
},
|
|
59
|
+
null,
|
|
60
|
+
2,
|
|
61
|
+
)}\n`,
|
|
62
|
+
);
|
|
@@ -10,6 +10,7 @@ export const RELEASE_CHECK = Object.freeze({
|
|
|
10
10
|
RUNTIME: "runtime",
|
|
11
11
|
DOCUMENTATION: "documentation",
|
|
12
12
|
CI_POLICY: "ci-policy",
|
|
13
|
+
PUBLISH_WORKFLOW: "publish-workflow",
|
|
13
14
|
NEGATIVE_VERIFICATION: "negative-verification",
|
|
14
15
|
DOCTOR: "doctor",
|
|
15
16
|
TYPESCRIPT_SMOKE: "typescript-smoke",
|
|
@@ -57,6 +58,7 @@ export const RELEASE_LOCAL_CHECKS = Object.freeze([
|
|
|
57
58
|
Object.freeze({name: RELEASE_CHECK.RUNTIME, npmScript: "check:runtime"}),
|
|
58
59
|
Object.freeze({name: RELEASE_CHECK.DOCUMENTATION, npmScript: "check:documentation"}),
|
|
59
60
|
Object.freeze({name: RELEASE_CHECK.CI_POLICY, npmScript: "smoke:ci"}),
|
|
61
|
+
Object.freeze({name: RELEASE_CHECK.PUBLISH_WORKFLOW, npmScript: "smoke:publish"}),
|
|
60
62
|
Object.freeze({name: RELEASE_CHECK.NEGATIVE_VERIFICATION, npmScript: "smoke:negative"}),
|
|
61
63
|
Object.freeze({name: RELEASE_CHECK.DOCTOR, npmScript: "smoke:doctor"}),
|
|
62
64
|
Object.freeze({name: RELEASE_CHECK.TYPESCRIPT_SMOKE, npmScript: "smoke"}),
|
|
@@ -81,7 +81,11 @@ try {
|
|
|
81
81
|
calls[0].args.includes(`${CODEX_DISTRIBUTION.VERSION_REF_PREFIX}${version}`),
|
|
82
82
|
"Codex marketplace verification did not use an immutable version ref",
|
|
83
83
|
);
|
|
84
|
-
assert(
|
|
84
|
+
assert(
|
|
85
|
+
calls[1].args.includes(CODEX_DISTRIBUTION.UPGRADE_COMMAND) && calls[1].args.includes(CODEX_DISTRIBUTION.MARKETPLACE_NAME),
|
|
86
|
+
"Codex marketplace verification omitted the configured snapshot refresh",
|
|
87
|
+
);
|
|
88
|
+
assert(calls[2].args.includes(CODEX_DISTRIBUTION.PLUGIN_SELECTOR), "Codex verification used the wrong plugin selector");
|
|
85
89
|
assert(
|
|
86
90
|
calls.every((call) => call.options.env[CODEX_DISTRIBUTION.HOME_ENVIRONMENT_VARIABLE].startsWith(codexWorkspace)),
|
|
87
91
|
"Codex verification did not isolate its state under the temporary workspace",
|
|
@@ -105,6 +109,22 @@ try {
|
|
|
105
109
|
networkChecks[0].reason === RELEASE_REASON.CODEX_MARKETPLACE_UNAVAILABLE,
|
|
106
110
|
"Unavailable Codex marketplace network used the wrong reason",
|
|
107
111
|
);
|
|
112
|
+
|
|
113
|
+
let upgradeCall = 0;
|
|
114
|
+
const upgradeNetworkChecks = await verifyCodexPlugin({
|
|
115
|
+
version,
|
|
116
|
+
workspace: codexWorkspace,
|
|
117
|
+
runCommand: async () => {
|
|
118
|
+
upgradeCall++;
|
|
119
|
+
if (upgradeCall === 1) return {exitCode: CI_EXIT_CODE.PASS, stdout: "{}", stderr: ""};
|
|
120
|
+
return {exitCode: CI_EXIT_CODE.FAIL, stdout: "", stderr: "network is unreachable"};
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
assert(upgradeNetworkChecks[0].status === CI_STATUS.BLOCKED, "Unavailable marketplace upgrade was not blocked");
|
|
124
|
+
assert(
|
|
125
|
+
upgradeNetworkChecks[0].reason === RELEASE_REASON.CODEX_MARKETPLACE_UNAVAILABLE,
|
|
126
|
+
"Unavailable marketplace upgrade used the wrong reason",
|
|
127
|
+
);
|
|
108
128
|
} finally {
|
|
109
129
|
await rm(codexWorkspace, {recursive: true, force: true});
|
|
110
130
|
}
|
|
@@ -116,6 +136,7 @@ process.stdout.write(
|
|
|
116
136
|
missingPublishedVersion: "blocked",
|
|
117
137
|
isolatedCodexPluginInstallation: "ok",
|
|
118
138
|
unavailableCodexMarketplace: "blocked",
|
|
139
|
+
unavailableCodexMarketplaceUpgrade: "blocked",
|
|
119
140
|
},
|
|
120
141
|
null,
|
|
121
142
|
2,
|
|
@@ -131,6 +131,24 @@ Omitted collection limits still mean unlimited. The server never inserts a hidde
|
|
|
131
131
|
4. Inspect focused tests for asserted behavior; run them when permitted.
|
|
132
132
|
5. Re-read cited locations before presenting a finding.
|
|
133
133
|
|
|
134
|
+
### Preserve Combination Invariants
|
|
135
|
+
|
|
136
|
+
When a change combines multiple sources into one domain value or introduces
|
|
137
|
+
precedence, fallback, normalization, ordering, or merge logic:
|
|
138
|
+
|
|
139
|
+
1. Identify every producer of the value, not only the source involved in the
|
|
140
|
+
reported example.
|
|
141
|
+
2. Find existing comparison, normalization, fallback, and combination helpers
|
|
142
|
+
and state the invariant they encode.
|
|
143
|
+
3. Inspect direct consumers and focused tests to confirm how the combined value
|
|
144
|
+
is treated as a contract.
|
|
145
|
+
4. Exercise a boundary case that could disprove the proposed precedence or
|
|
146
|
+
merge rule.
|
|
147
|
+
|
|
148
|
+
Semantic tools verify the identities and references discovered during this
|
|
149
|
+
work. Use text search, direct source inspection, tests, and domain reasoning for
|
|
150
|
+
conceptual relationships that an LSP cannot establish.
|
|
151
|
+
|
|
134
152
|
An actionable finding requires a reachable path, a violated contract, and concrete impact. For commit or pull-request reviews, compare with the exact parent and classify material conclusions as `introduced`, `modified`, or `pre-existing`.
|
|
135
153
|
|
|
136
154
|
Empty symbol, reference, or diagnostic results are evidence to investigate. Use `collection`, `textSearch`, `rg`, and direct inspection to establish the supported conclusion.
|