From 4795b9dd0bdc4c8b7840ac8e363ca25cf3c2329a Mon Sep 17 00:00:00 2001 From: Conal <33135619+Conalh@users.noreply.github.com> Date: Thu, 21 May 2026 18:26:41 -0700 Subject: [PATCH] Use readable surface names in findings --- README.md | 2 +- dist/mesh/engine.js | 19 +++++++++++++++---- package-lock.json | 4 ++-- package.json | 2 +- src/mesh/engine.ts | 20 ++++++++++++++++---- test/cli-output.test.mjs | 21 +++++++++++++-------- test/workflow.test.mjs | 10 +++++----- 7 files changed, 53 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 667fbae..608b9aa 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ jobs: steps: - uses: actions/checkout@v6 - - uses: Conalh/PolicyMesh@v0.1.16 + - uses: Conalh/PolicyMesh@v0.1.17 with: fail-on: none ``` diff --git a/dist/mesh/engine.js b/dist/mesh/engine.js index 4462742..298695e 100644 --- a/dist/mesh/engine.js +++ b/dist/mesh/engine.js @@ -506,7 +506,7 @@ function summarizeEnvKeys(servers) { return servers .map((server) => { const keys = uniqueSorted(Object.keys(server.env)); - return `${server.surfaceId} uses ${keys.length > 0 ? keys.join(', ') : 'no env variables'}`; + return `${surfaceLabel(server.surfaceId)} uses ${keys.length > 0 ? keys.join(', ') : 'no env variables'}`; }) .join('; '); } @@ -530,17 +530,28 @@ function summarizeHeaderKeys(servers) { return servers .map((server) => { const keys = uniqueSorted(Object.keys(server.headers)); - return `${server.surfaceId} uses ${keys.length > 0 ? keys.join(', ') : 'no headers'}`; + return `${surfaceLabel(server.surfaceId)} uses ${keys.length > 0 ? keys.join(', ') : 'no headers'}`; }) .join('; '); } function summarizeEnabledStates(servers) { return servers - .map((server) => `${server.enabled ? 'enabled' : 'disabled'} in ${server.surfaceId}`) + .map((server) => `${server.enabled ? 'enabled' : 'disabled'} in ${surfaceLabel(server.surfaceId)}`) .join('; '); } function formatSurfaceList(surfaces) { - return surfaces.join(', '); + return surfaces.map(surfaceLabel).join(', '); +} +function surfaceLabel(surface) { + const labels = { + root_mcp: 'Root MCP', + cursor_mcp: 'Cursor MCP', + vscode_mcp: 'VS Code MCP', + windsurf_mcp: 'Codeium/Windsurf MCP', + claude: 'Claude', + codex: 'Codex' + }; + return labels[surface]; } function listOtherAgentSurfaces(policies) { const surfaces = policies.mcpSurfaces diff --git a/package-lock.json b/package-lock.json index dd3e6a7..7e9a1b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "policymesh", - "version": "0.1.16", + "version": "0.1.17", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "policymesh", - "version": "0.1.16", + "version": "0.1.17", "license": "MIT", "bin": { "policymesh": "dist/index.js" diff --git a/package.json b/package.json index 8766728..a39d10f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "policymesh", - "version": "0.1.16", + "version": "0.1.17", "description": "Cross-surface AI agent policy consistency review.", "type": "module", "keywords": [ diff --git a/src/mesh/engine.ts b/src/mesh/engine.ts index d282243..5c9f631 100644 --- a/src/mesh/engine.ts +++ b/src/mesh/engine.ts @@ -588,7 +588,7 @@ function summarizeEnvKeys(servers: McpServer[]): string { return servers .map((server) => { const keys = uniqueSorted(Object.keys(server.env)); - return `${server.surfaceId} uses ${keys.length > 0 ? keys.join(', ') : 'no env variables'}`; + return `${surfaceLabel(server.surfaceId)} uses ${keys.length > 0 ? keys.join(', ') : 'no env variables'}`; }) .join('; '); } @@ -616,19 +616,31 @@ function summarizeHeaderKeys(servers: McpServer[]): string { return servers .map((server) => { const keys = uniqueSorted(Object.keys(server.headers)); - return `${server.surfaceId} uses ${keys.length > 0 ? keys.join(', ') : 'no headers'}`; + return `${surfaceLabel(server.surfaceId)} uses ${keys.length > 0 ? keys.join(', ') : 'no headers'}`; }) .join('; '); } function summarizeEnabledStates(servers: McpServer[]): string { return servers - .map((server) => `${server.enabled ? 'enabled' : 'disabled'} in ${server.surfaceId}`) + .map((server) => `${server.enabled ? 'enabled' : 'disabled'} in ${surfaceLabel(server.surfaceId)}`) .join('; '); } function formatSurfaceList(surfaces: SurfaceId[]): string { - return surfaces.join(', '); + return surfaces.map(surfaceLabel).join(', '); +} + +function surfaceLabel(surface: SurfaceId): string { + const labels: Record = { + root_mcp: 'Root MCP', + cursor_mcp: 'Cursor MCP', + vscode_mcp: 'VS Code MCP', + windsurf_mcp: 'Codeium/Windsurf MCP', + claude: 'Claude', + codex: 'Codex' + }; + return labels[surface]; } function listOtherAgentSurfaces(policies: RepoPolicies): SurfaceId[] { diff --git a/test/cli-output.test.mjs b/test/cli-output.test.mjs index 0d4f9c1..4d60556 100644 --- a/test/cli-output.test.mjs +++ b/test/cli-output.test.mjs @@ -219,7 +219,9 @@ test('CLI reports servers missing from configured but empty MCP surfaces', async assert.equal(report.findings[0].severity, 'low'); assert.equal(report.findings[0].subject, 'github'); assert.deepEqual(report.findings[0].surfaces, ['root_mcp', 'cursor_mcp']); - assert.match(report.findings[0].message, /missing from cursor_mcp/); + assert.match(report.findings[0].message, /defined in Root MCP/); + assert.match(report.findings[0].message, /missing from Cursor MCP/); + assert.doesNotMatch(report.findings[0].message, /root_mcp|cursor_mcp/); }); test('CLI reports MCP server enabled-state drift across surfaces', async () => { @@ -239,8 +241,9 @@ test('CLI reports MCP server enabled-state drift across surfaces', async () => { assert.equal(report.findings[0].severity, 'medium'); assert.equal(report.findings[0].subject, 'github'); assert.deepEqual(report.findings[0].surfaces, ['root_mcp', 'cursor_mcp']); - assert.match(report.findings[0].message, /enabled in root_mcp/); - assert.match(report.findings[0].message, /disabled in cursor_mcp/); + assert.match(report.findings[0].message, /enabled in Root MCP/); + assert.match(report.findings[0].message, /disabled in Cursor MCP/); + assert.doesNotMatch(report.findings[0].message, /root_mcp|cursor_mcp/); }); test('CLI reports MCP server environment drift without leaking values', async () => { @@ -261,8 +264,9 @@ test('CLI reports MCP server environment drift without leaking values', async () assert.equal(report.findings[0].subject, 'github'); assert.deepEqual(report.findings[0].surfaces, ['root_mcp', 'vscode_mcp']); assert.match(report.findings[0].message, /environment variable names differ/); - assert.match(report.findings[0].message, /GITHUB_TOKEN/); - assert.match(report.findings[0].message, /GH_TOKEN/); + assert.match(report.findings[0].message, /Root MCP uses GITHUB_TOKEN/); + assert.match(report.findings[0].message, /VS Code MCP uses GH_TOKEN/); + assert.doesNotMatch(report.findings[0].message, /root_mcp|vscode_mcp/); assert.doesNotMatch(stdout, /root-token-value/); assert.doesNotMatch(stdout, /vscode-token-value/); }); @@ -306,8 +310,9 @@ test('CLI reports MCP server header drift without leaking values', async () => { assert.equal(report.findings[0].subject, 'analytics'); assert.deepEqual(report.findings[0].surfaces, ['root_mcp', 'vscode_mcp']); assert.match(report.findings[0].message, /header names differ/); - assert.match(report.findings[0].message, /Authorization/); - assert.match(report.findings[0].message, /X-API-Key/); + assert.match(report.findings[0].message, /Root MCP uses Authorization/); + assert.match(report.findings[0].message, /VS Code MCP uses X-API-Key/); + assert.doesNotMatch(report.findings[0].message, /root_mcp|vscode_mcp/); assert.doesNotMatch(stdout, /root-header-secret/); assert.doesNotMatch(stdout, /vscode-header-secret/); }); @@ -450,7 +455,7 @@ test('CLI emits GitHub annotations for configured surfaces missing MCP servers', const missingAnnotations = stdout .split('\n') - .filter((line) => line.includes('missing from cursor_mcp')); + .filter((line) => line.includes('missing from Cursor MCP')); assert.deepEqual( missingAnnotations.map((line) => /^::warning file=([^,]+)/.exec(line)?.[1]).sort(), diff --git a/test/workflow.test.mjs b/test/workflow.test.mjs index 048ab5f..7bb1765 100644 --- a/test/workflow.test.mjs +++ b/test/workflow.test.mjs @@ -10,15 +10,15 @@ const execFileAsync = promisify(execFile); const testDir = dirname(fileURLToPath(import.meta.url)); const packageRoot = join(testDir, '..'); -test('release metadata is prepared for v0.1.16 Action users', async () => { +test('release metadata is prepared for v0.1.17 Action users', async () => { const packageJson = JSON.parse(await readFile(join(packageRoot, 'package.json'), 'utf8')); const packageLock = JSON.parse(await readFile(join(packageRoot, 'package-lock.json'), 'utf8')); const readme = await readFile(join(packageRoot, 'README.md'), 'utf8'); - assert.equal(packageJson.version, '0.1.16'); - assert.equal(packageLock.version, '0.1.16'); - assert.equal(packageLock.packages[''].version, '0.1.16'); - assert.match(readme, /uses: Conalh\/PolicyMesh@v0\.1\.16/); + assert.equal(packageJson.version, '0.1.17'); + assert.equal(packageLock.version, '0.1.17'); + assert.equal(packageLock.packages[''].version, '0.1.17'); + assert.match(readme, /uses: Conalh\/PolicyMesh@v0\.1\.17/); }); test('package metadata supports OSS discovery', async () => {