Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 6 additions & 2 deletions src/drift/index.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -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]);

Expand Down
46 changes: 46 additions & 0 deletions test/public-api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading