From 63d75979825cf6055ac4f0a8ebec7eb1f0212a31 Mon Sep 17 00:00:00 2001 From: Michael Dailey Date: Tue, 19 May 2026 15:33:44 -0500 Subject: [PATCH 1/2] =?UTF-8?q?PDX-485:=20chore(mcp)=20=E2=80=94=20introdu?= =?UTF-8?q?ce=20shared=20warningCodes.ts=20enum=20for=20cross-thread=20fee?= =?UTF-8?q?dback=20codes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RCA: Six sibling Provar MCP thread PRs (validation, properties, automation, RCA, JUnit, parallel-mode tuning) each independently emit warnings with ad-hoc string prefixes — `WARNING:`, `[provarHome]`, `WARN —` — producing inconsistent surface area for AI agents downstream and making cross-tool typo guidance ("Did you mean...?") harder to standardise. PDX-485 wants a single canonical enum the threads can import, so warning codes are coined once, formatted once, and documented once. Fix: Adds src/mcp/utils/warningCodes.ts exporting WARNING_CODES (PROVARHOME-001, DATA-001, PARALLEL-001, SCHEMA-001, RUN-001, JUNIT-001), a WarningCode type derived from the enum, and a formatWarning(code, message, suggestion?) helper that emits `WARNING []: ` and appends ` Did you mean ''?` when a suggestion is provided. No call sites are touched in this PR — surface area is intentionally minimal so the six sibling thread PRs can import and adopt without merge conflicts. docs/mcp.md gains a new "Warning codes" reference table linked from the table of contents; per-row meanings are placeholders that subsequent thread PRs will refine. Tests: New test/unit/mcp/utils/warningCodes.test.ts covers (1) each WARNING_CODES key maps to its expected wire string, (2) formatWarning without a suggestion returns the prefixed message exactly, (3) formatWarning with a suggestion appends the "Did you mean" suffix exactly, (4) an empty-string suggestion is treated as no suggestion. Validation: yarn compile clean, yarn lint clean, full mocha 1159 passing / 0 failing. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/mcp.md | 18 ++++++++ src/mcp/utils/warningCodes.ts | 22 ++++++++++ test/unit/mcp/utils/warningCodes.test.ts | 53 ++++++++++++++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 src/mcp/utils/warningCodes.ts create mode 100644 test/unit/mcp/utils/warningCodes.test.ts diff --git a/docs/mcp.md b/docs/mcp.md index 33d23421..3ec6bbe9 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -78,6 +78,7 @@ The Provar DX CLI ships with a built-in **Model Context Protocol (MCP) server** - [Quality scores explained](#quality-scores-explained) - [API compatibility — `xml` vs `xml_content`](#api-compatibility--xml-vs-xml_content) - [Performance Tuning](#performance-tuning) +- [Warning codes](#warning-codes) --- @@ -531,6 +532,23 @@ On **Windows**, path comparisons are performed case-insensitively to account for --- +## Warning codes + +Cross-cutting warning codes surfaced by validation, configuration, and run tooling. These complement the per-tool `rule_id` codes (e.g. `TC_001`, `VAR-REF-001`) documented under [Available tools](#available-tools). Subsequent revisions will refine the meanings as the relevant tool surfaces stabilise. + +| Code | Surfaced by | Meaning | +| ---------------- | --------------------------------------- | ------------------------------------------------------------------------- | +| `PROVARHOME-001` | properties / automation tooling | `provarHome` is missing, blank, or does not point to a Provar install | +| `DATA-001` | `provar_testcase_validate` | `` iteration is silently ignored in CLI standalone execution | +| `PARALLEL-001` | automation / run tooling | Parallel-mode cache mismatch between properties and active runtime config | +| `SCHEMA-001` | strict properties / config validators | Unknown or misspelled key in a JSON / properties schema (typo guard) | +| `RUN-001` | `provar_automation_testrun` and friends | Test run produced no executable results — check input selection | +| `JUNIT-001` | report / RCA tooling | JUnit results file is missing, empty, or not parseable | + +Warnings emitted programmatically follow the shape `WARNING []: ` — and when a typo is detected, the message is suffixed with `Did you mean ''?`. See `src/mcp/utils/warningCodes.ts` for the canonical enum. + +--- + ## Available tools ### `provardx_ping` diff --git a/src/mcp/utils/warningCodes.ts b/src/mcp/utils/warningCodes.ts new file mode 100644 index 00000000..9da5d9a3 --- /dev/null +++ b/src/mcp/utils/warningCodes.ts @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2024 Provar Limited. + * All rights reserved. + * Licensed under the BSD 3-Clause license. + * For full license text, see LICENSE.md file in the repo root or https://opensource.org/licenses/BSD-3-Clause + */ + +export const WARNING_CODES = { + PROVARHOME_001: 'PROVARHOME-001', + DATA_001: 'DATA-001', + PARALLEL_001: 'PARALLEL-001', + SCHEMA_001: 'SCHEMA-001', + RUN_001: 'RUN-001', + JUNIT_001: 'JUNIT-001', +} as const; + +export type WarningCode = (typeof WARNING_CODES)[keyof typeof WARNING_CODES]; + +export function formatWarning(code: WarningCode, message: string, suggestion?: string): string { + const base = `WARNING [${code}]: ${message}`; + return suggestion ? `${base} Did you mean '${suggestion}'?` : base; +} diff --git a/test/unit/mcp/utils/warningCodes.test.ts b/test/unit/mcp/utils/warningCodes.test.ts new file mode 100644 index 00000000..3afa5b01 --- /dev/null +++ b/test/unit/mcp/utils/warningCodes.test.ts @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024 Provar Limited. + * All rights reserved. + * Licensed under the BSD 3-Clause license. + * For full license text, see LICENSE.md file in the repo root or https://opensource.org/licenses/BSD-3-Clause + */ + +import { strict as assert } from 'node:assert'; +import { describe, it } from 'mocha'; +import { WARNING_CODES, formatWarning } from '../../../../src/mcp/utils/warningCodes.js'; + +describe('WARNING_CODES', () => { + it('maps each key to its expected wire string', () => { + const expected: Record = { + PROVARHOME_001: 'PROVARHOME-001', + DATA_001: 'DATA-001', + PARALLEL_001: 'PARALLEL-001', + SCHEMA_001: 'SCHEMA-001', + RUN_001: 'RUN-001', + JUNIT_001: 'JUNIT-001', + }; + for (const [key, value] of Object.entries(expected)) { + assert.equal( + (WARNING_CODES as Record)[key], + value, + `WARNING_CODES.${key} should equal '${value}'` + ); + } + }); +}); + +describe('formatWarning', () => { + it('returns the prefixed message exactly when no suggestion is provided', () => { + assert.equal( + formatWarning(WARNING_CODES.PROVARHOME_001, 'provarHome is missing'), + 'WARNING [PROVARHOME-001]: provarHome is missing' + ); + }); + + it('appends the "Did you mean" suffix exactly when a suggestion is provided', () => { + assert.equal( + formatWarning(WARNING_CODES.SCHEMA_001, "unknown key 'parralelMode'", 'parallelMode'), + "WARNING [SCHEMA-001]: unknown key 'parralelMode' Did you mean 'parallelMode'?" + ); + }); + + it('omits the suffix when suggestion is an empty string', () => { + assert.equal( + formatWarning(WARNING_CODES.DATA_001, 'data-table iteration not bound', ''), + 'WARNING [DATA-001]: data-table iteration not bound' + ); + }); +}); From 46f691e91fd9fbae482857996d6d1852acc33b5e Mon Sep 17 00:00:00 2001 From: Michael Dailey Date: Tue, 19 May 2026 16:25:57 -0500 Subject: [PATCH 2/2] =?UTF-8?q?PDX-485:=20test(mcp)=20=E2=80=94=20add=20ex?= =?UTF-8?q?act=20key/value=20set=20assertions=20to=20WARNING=5FCODES=20tes?= =?UTF-8?q?t?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RCA: Copilot review on PR #182 flagged that the parity test only iterated the expected map and asserted forward-direction equality (expected key -> WARNING_CODES[key]). A new enum entry added without updating the test, or an accidentally-removed entry, would not fail the build. Bidirectional coverage was missing. Fix: Hoist the expected map to the describe scope so it is reusable. Add two new assertions: (a) Object.keys(WARNING_CODES).sort() deepEqual Object.keys(expected).sort() — guards against additions and omissions in the key set; (b) Object.values(WARNING_CODES).sort() deepEqual Object.values(expected).sort() — guards against value drift. Tests now fail loudly on silent enum drift in either direction. --- test/unit/mcp/utils/warningCodes.test.ts | 27 +++++++++++++++++------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/test/unit/mcp/utils/warningCodes.test.ts b/test/unit/mcp/utils/warningCodes.test.ts index 3afa5b01..3ae7ff07 100644 --- a/test/unit/mcp/utils/warningCodes.test.ts +++ b/test/unit/mcp/utils/warningCodes.test.ts @@ -10,15 +10,16 @@ import { describe, it } from 'mocha'; import { WARNING_CODES, formatWarning } from '../../../../src/mcp/utils/warningCodes.js'; describe('WARNING_CODES', () => { + const expected: Record = { + PROVARHOME_001: 'PROVARHOME-001', + DATA_001: 'DATA-001', + PARALLEL_001: 'PARALLEL-001', + SCHEMA_001: 'SCHEMA-001', + RUN_001: 'RUN-001', + JUNIT_001: 'JUNIT-001', + }; + it('maps each key to its expected wire string', () => { - const expected: Record = { - PROVARHOME_001: 'PROVARHOME-001', - DATA_001: 'DATA-001', - PARALLEL_001: 'PARALLEL-001', - SCHEMA_001: 'SCHEMA-001', - RUN_001: 'RUN-001', - JUNIT_001: 'JUNIT-001', - }; for (const [key, value] of Object.entries(expected)) { assert.equal( (WARNING_CODES as Record)[key], @@ -27,6 +28,16 @@ describe('WARNING_CODES', () => { ); } }); + + // Guards against silent enum drift: a new code added without updating this test, + // or an accidentally-removed code, must fail the build. + it('contains exactly the expected key set (no additions, no omissions)', () => { + assert.deepEqual(Object.keys(WARNING_CODES).sort(), Object.keys(expected).sort()); + }); + + it('contains exactly the expected value set (no additions, no omissions)', () => { + assert.deepEqual(Object.values(WARNING_CODES).sort(), Object.values(expected).sort()); + }); }); describe('formatWarning', () => {