diff --git a/CHANGELOG.md b/CHANGELOG.md index a01d2e0..484d0be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Fixed +- **checkPaths false positives** — `checkPaths` now only validates inline code paths from `ROUTER.md`, not all scaffold files. Eliminates false `MISSING_PATH` errors from context docs, pattern files, and tool config files where backtick-wrapped strings are config values, IPs, annotation keys, or other non-path content. [#79](https://github.com/theDakshJaitly/mex/issues/79) + ## [0.6.1] - 2026-06-14 ### Added diff --git a/src/drift/index.ts b/src/drift/index.ts index e47bf4e..6017b2d 100644 --- a/src/drift/index.ts +++ b/src/drift/index.ts @@ -1,5 +1,5 @@ import { readFileSync } from "node:fs"; -import { resolve, relative } from "node:path"; +import { resolve, relative, basename } from "node:path"; import { globSync } from "glob"; import type { MexConfig, DriftReport, DriftIssue, Claim } from "../types.js"; import { extractClaims } from "./claims.js"; @@ -91,7 +91,11 @@ export async function runDriftCheck( } // Run checkers that work on claims - const pathIssues = checkPaths(allClaims, projectRoot, scaffoldRoot); + // Only check paths in ROUTER.md — other scaffold files use backticks for + // non-path content (config values, IPs, annotation keys) that produces + // false MISSING_PATH errors. See https://github.com/theDakshJaitly/mex/issues/79 + const routerClaims = allClaims.filter((c) => basename(c.source) === "ROUTER.md"); + const pathIssues = checkPaths(routerClaims, projectRoot, scaffoldRoot); allIssues.push(...pathIssues); checkerIssueCounts.push(["paths", pathIssues.length]); diff --git a/test/public-api.test.ts b/test/public-api.test.ts index 63f2e15..7f9b444 100644 --- a/test/public-api.test.ts +++ b/test/public-api.test.ts @@ -232,6 +232,52 @@ describe("public API — runDriftCheck", () => { }); }); +describe("public API — runDriftCheck scopes checkPaths to ROUTER.md", () => { + it("does not produce MISSING_PATH issues from non-ROUTER.md files", async () => { + mkdirSync(join(tmpDir, ".mex/context"), { recursive: true }); + writeFileSync( + join(tmpDir, ".mex/ROUTER.md"), + "---\nedges:\n - target: context/architecture.md\n---\n# Router\n\nSee [architecture](context/architecture.md).\n", + ); + // architecture.md has inline code that looks like paths but isn't real files + writeFileSync( + join(tmpDir, ".mex/context/architecture.md"), + "# Architecture\n\nUse `csi.kubeletRootDir: /var/lib/kubelet` and `192.168.5.0/24`.\n", + ); + const report = await runDriftCheck(config); + const pathIssues = report.issues.filter((i) => i.code === "MISSING_PATH"); + // These non-path inline codes should NOT produce MISSING_PATH errors + expect(pathIssues).toHaveLength(0); + }); + + it("still produces MISSING_PATH issues from ROUTER.md", async () => { + writeFileSync( + join(tmpDir, ".mex/ROUTER.md"), + "# Router\n\nSee `src/totally/missing.ts` for details.\n", + ); + const report = await runDriftCheck(config); + const pathIssues = report.issues.filter((i) => i.code === "MISSING_PATH"); + expect(pathIssues).toHaveLength(1); + expect(pathIssues[0].message).toContain("src/totally/missing.ts"); + }); + + it("only flags ROUTER.md paths when both ROUTER.md and AGENTS.md have missing paths", async () => { + writeFileSync( + join(tmpDir, ".mex/ROUTER.md"), + "# Router\n\nSee `src/missing.ts`.\n", + ); + writeFileSync( + join(tmpDir, ".mex/AGENTS.md"), + "# Agents\n\nSee `lib/also/missing.py`.\n", + ); + const report = await runDriftCheck(config); + const pathIssues = report.issues.filter((i) => i.code === "MISSING_PATH"); + // Only the ROUTER.md path should be flagged + expect(pathIssues).toHaveLength(1); + expect(pathIssues[0].message).toContain("src/missing.ts"); + }); +}); + describe("public API — heartbeat", () => { it("checkHeartbeat returns the documented HeartbeatResult shape", () => { const result: HeartbeatResult = checkHeartbeat(config);