PDX-486: feat(mcp) — strict validator unknown-key detection (Thread A, PR 1/3)#184
Conversation
…-thread feedback codes
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 [<CODE>]: <message>` and appends ` Did you mean '<suggestion>'?` 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) <noreply@anthropic.com>
…01 warning)
RCA: A user typo of 'testCases' (plural) instead of 'testCase' (singular) in
provardx-properties.json slipped past provar_properties_validate because the
validator only checked required fields and enum values — unknown keys were
silently accepted. The subsequent test run exited 0 with zero tests executed,
falsely reporting success. Two safety nets failed in series: the lenient
validator was the first.
Fix: Extend validateProperties to emit a SCHEMA-001 warning for any unknown
top-level, metadata.*, or environment.* key. Canonical key sets are exported
constants (CANONICAL_TOP_LEVEL_KEYS / CANONICAL_METADATA_KEYS /
CANONICAL_ENVIRONMENT_KEYS) so sibling PRs PDX-488 (PROVARHOME-001) and
PDX-494 (PARALLEL-001) can extend them. A minimal inline Levenshtein helper
suggests the closest canonical key within distance 2 ("Did you mean
'testCase'?"). Unknown keys are warnings (not errors) so additive Provar
schema versions do not break older MCP clients. The validate tool description
now references the testCases → testCase example explicitly. A regression
fixture lives at test/fixtures/properties/testcases-typo.json and is shared
with the VALIDATE-TYPO-B half (sibling PR, Thread B).
Out of scope for this PR (Thread A, PR 1 of 3): the testrun zero-tests guard
(VALIDATE-TYPO-B, Thread B), the config_load wiring (final Thread B commit),
PROVARHOME-001 (PDX-488), and PARALLEL-001 (PDX-494).
Depends on #182 (PDX-485 thread-prep: shared warningCodes.ts enum).
Quality Orchestrator🟢 LOW · 🧪 Tests to Run · Running 2 of 52 tests
▶ Run commandnpx vitest run \
unit/mcp/propertiesTools.test.ts \
unit/mcp/utils/warningCodes.test.ts⚡ quality-orchestrator · |
There was a problem hiding this comment.
Pull request overview
Adds strict unknown-key detection to provar_properties_validate (SCHEMA-001) so schema typos like testCases vs testCase are surfaced as warnings with “Did you mean …?” suggestions, preventing silent no-op test runs later in the workflow.
Changes:
- Introduces shared
WARNING_CODES+formatWarning()utility and documents warning codes indocs/mcp.md. - Exports canonical properties key sets and adds Levenshtein-based “closest key” suggestion + SCHEMA-001 warning emission for unknown keys in
provardx-properties.json. - Adds unit tests and a regression fixture covering SCHEMA-001 behavior and suggestion/no-suggestion cases.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
src/mcp/utils/warningCodes.ts |
Adds canonical warning codes + standardized warning formatter used by validators/tools. |
src/mcp/tools/propertiesTools.ts |
Implements canonical key sets + SCHEMA-001 unknown-key detection with Levenshtein suggestions. |
test/unit/mcp/utils/warningCodes.test.ts |
Unit coverage for warning codes mapping and formatter behavior. |
test/unit/mcp/propertiesTools.test.ts |
Adds SCHEMA-001 validation test cases (top-level/metadata/environment, suggestion threshold, fixture). |
test/fixtures/properties/testcases-typo.json |
Regression fixture containing the testCases typo for shared reuse. |
docs/mcp.md |
Documents warning codes + updates provar_properties_validate docs for SCHEMA-001 and output shape. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| | `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 [<CODE>]: <message>` — and when a typo is detected, the message is suffixed with `Did you mean '<suggestion>'?`. See `src/mcp/utils/warningCodes.ts` for the canonical enum. |
…o formatWarning() messages RCA: The "Warning codes" section in docs/mcp.md previously stated that "Warnings emitted programmatically follow the shape WARNING [<CODE>]: <message>". That claim is too broad: provar_properties_validate emits placeholder warnings (pre-existing, non-coded) whose message is a plain string with no WARNING [...] prefix. A reader looking at that line would reasonably expect every warning surface in the codebase to carry a code, then be surprised by the properties validator output and treat it as a regression. Fix: Reword the sentence to scope the structured shape to warnings built via formatWarning() — those have the WARNING [<CODE>]: <message> prefix and the optional Did you mean '<suggestion>'? suffix. Free-form warnings without a structured code (called out explicitly via the properties validator example) remain plain strings. Keeps the reference to src/mcp/utils/warningCodes.ts for the canonical enum. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
mrdailey99
left a comment
There was a problem hiding this comment.
Addressed in a8c05fc: docs clarified — WARNING shape applies only to formatWarning() output, not to free-form warnings.
Context
A user typed
testCases(plural) instead oftestCase(singular) in aprovardx-properties.jsonfile.provar_properties_validateaccepted it(the validator only checked required fields and enum values — unknown keys
were silently ignored). The subsequent
provar_automation_testrunexited 0with zero tests executed and falsely reported "tests run successfully." Two
safety nets failed in series.
This PR is VALIDATE-TYPO-A — the first half of the fix. It plugs the
validator gap.
The companion VALIDATE-TYPO-B (Thread B) adds the
RUN-001zero-testsguard on
provar_automation_testrun, and a final small Thread B commitwill wire
validatePropertiesintoprovar_automation_config_loadsoSCHEMA-001 surfaces before the shared sf config is written. See the umbrella
ticket PDX-486 for the full story.
This is Thread A, PR 1 of 3 in the parallel sequencing from
.claude/plans/file-c-users-mrdai-git-provar-manager-re-compressed-thunder.md.The next two Thread A PRs (PDX-488 PROVARHOME-001 and PDX-494 PARALLEL-001)
will extend the canonical key sets exported here.
Depends on #182 (PDX-485 thread-prep: shared
src/mcp/utils/warningCodes.ts).Why
detection closes it without breaking older clients (warning, not error).
extension point — six threads referencing the same enum names converge
cleanly; six threads inventing their own do not.
turns a silent failure into an obvious one for both humans and agents.
What changed
src/mcp/tools/propertiesTools.tsCANONICAL_TOP_LEVEL_KEYS,CANONICAL_METADATA_KEYS,CANONICAL_ENVIRONMENT_KEYSsourced from the existing required-fieldconstants plus the documented optional fields and template defaults.
levenshtein()andclosestCanonicalKey()helpers.validatePropertiesnow emitsSCHEMA-001warnings for any unknownkey at top-level,
metadata.*, orenvironment.*.provar_properties_validatetool description updated to call out thetestCases→testCaseexample explicitly.test/unit/mcp/propertiesTools.test.ts— 7 new test cases (top-level,metadata, environment, no-near-match, is_valid stays true, fixture round-trip).
test/fixtures/properties/testcases-typo.json— shared regression fixturewith the
testCasestypo (Thread B and the smoke harness will reference it).docs/mcp.md—provar_properties_validatesection updated with SCHEMA-001description, classic-typo example, and clarified output shape (
errorsvs
warningsarrays;is_validonly flips on errors).Out of scope (sibling PRs)
RUN-001zero-tests guard onprovar_automation_testrun— VALIDATE-TYPO-B (Thread B).validatePropertieswired intoprovar_automation_config_load— final Thread B commit.PROVARHOME-001— PDX-488 (Thread A, PR 2).PARALLEL-001— PDX-494 (Thread A, PR 3).package.json/server.jsonbeta bump — sweeper PR after the batch lands.Test plan
yarn compileclean (TypeScript)yarn lintclean (ESLint + script-name lint)node_modules/.bin/nyc node_modules/.bin/mocha "test/**/*.test.ts"— 1166 passing, 1 pending (unchanged from baseline)propertiesTools.test.tstests pass, including the 7 new SCHEMA-001 casescommit-msghook passes (RCA + Fix sections in body)docs/mcp.mdprovar_properties_setschema (cross-reference lines 1082–1097)CANONICAL_*constants)Risk
Low. Warning-only behaviour —
is_validstaystrueon files that haveunknown keys, so no existing-caller breakage. Canonical sets are additive
(future Provar versions that add keys produce one extra warning until those
keys are added to the canonical set; no test run breakage).