Files
coreci-chat/scripts/check-llm-mock-guard.mjs
CIAgent 0c15d3d0b2 docs(milestone): complete M2 — MCP Layer & Day 1 Adapters (v0.2)
M2 delivers the read-only MCP capability broker gateway and four Day-1
infrastructure adapters (Proxmox, SSH/Linux, GitHub, Gitea). 13 REQs (015-027)
all pass. 656 tests green. M1 non-regression verified.

MCP spec 2025-06-18 conformance verified (PROTOCOL.md + 7 tests).
Defense-in-depth SSH (broker layer 1 + Relay Agent layer 2 + no-shell exec).
Two-track LLM smoke (Track A mock-path P0 gate passes).
CI: Gitea Actions (.gitea/workflows/ci.yml) with Postgres 16 + RLS verification.

Phases shipped:
  P0  pre-execution          v0.1.0
  P1  Wave F — MCP gateway   v0.1.1
  P2  Wave G — Proxmox       v0.1.2
  P3  Wave H — SSH/Linux     v0.1.3
  P4  Wave I — Git adapters   v0.1.4
  P5  Wave J — SSE+smoke+UI  v0.1.5
  P6  Final — review+ship    v0.1.6 ← milestone release

---ci---
phase: 6
milestone: v0.2
status: complete
phase_role: final
milestone_complete: true
requirements:
  covered: [REQ-015, REQ-016, REQ-017, REQ-018, REQ-019, REQ-020, REQ-021, REQ-022, REQ-023, REQ-024, REQ-025, REQ-026, REQ-027]
  partial: []
---/ci---
2026-08-25 06:14:21 +00:00

87 lines
3.2 KiB
JavaScript

#!/usr/bin/env node
/**
* check-llm-mock-guard.mjs — build-time grep guard (R-008, G-018 Task 6).
*
* `@coreci/llm-mock` is a CI-only devDependency. It MUST NOT appear in the
* prod build output of the control plane (`.next/`), nor in the prod source
* of the broker (`packages/mcp/src/`). The eslint `no-restricted-imports`
* rule is the primary guard; this grep is the defense-in-depth backstop for
* a stray import that slips past linting (e.g. a dynamic import string).
*
* Run via `pnpm check:llm-mock-guard`. Exits non-zero if `llm-mock` appears in:
* - apps/control-plane/.next/** (after `pnpm build`)
* - apps/control-plane/app/** (prod source)
* - apps/control-plane/lib/** (prod source)
* - packages/mcp/src/** (prod broker source)
*
* Exempts tests/** (the smoke imports the mock) and packages/llm-mock/**
* (the package itself).
*
* Usage: node scripts/check-llm-mock-guard.mjs [--built]
* --built: also scan apps/control-plane/.next/ (after a build). Skipped by
* default so the check runs fast in CI before the build step.
*/
import { readdirSync, statSync, readFileSync, existsSync } from "node:fs";
import { join, relative } from "node:path";
import { argv, cwd, exit } from "node:process";
const root = cwd();
const scanBuilt = argv.includes("--built");
/** Directories whose PROD source must not import @coreci/llm-mock. */
const prodSourceRoots = [
join(root, "apps/control-plane/app"),
join(root, "apps/control-plane/lib"),
join(root, "packages/mcp/src"),
];
/** Build output directories scanned only with --built (after `pnpm build`). */
const builtRoots = scanBuilt ? [join(root, "apps/control-plane/.next")] : [];
/** Extensions to scan (source + bundled JS). */
const exts = [".ts", ".tsx", ".js", ".mjs", ".cjs", ".jsx"];
/** Walk a directory recursively, yielding file paths matching the extensions. */
function* walk(dir) {
if (!existsSync(dir)) return;
for (const entry of readdirSync(dir)) {
if (entry === "node_modules" || entry === ".git") continue;
const full = join(dir, entry);
const st = statSync(full);
if (st.isDirectory()) {
yield* walk(full);
} else if (st.isFile() && exts.some((e) => entry.endsWith(e))) {
yield full;
}
}
}
/** The forbidden substrings (catch any import path into the mock). */
const forbidden = ["@coreci/llm-mock", "llm-mock/server", "llm-mock/patterns", "llm-mock/retry"];
let violations = 0;
const roots = [...prodSourceRoots, ...builtRoots];
for (const rootDir of roots) {
for (const file of walk(rootDir)) {
let content;
try {
content = readFileSync(file, "utf8");
} catch {
continue; // unreadable (binary) — skip
}
for (const needle of forbidden) {
if (content.includes(needle)) {
const rel = relative(root, file);
console.error(`[llm-mock-guard] VIOLATION: '${needle}' found in ${rel}`);
violations++;
}
}
}
}
if (violations > 0) {
console.error(`\n[llm-mock-guard] ${violations} violation(s) found. @coreci/llm-mock is CI-only (R-008).`);
console.error("Remove the import from prod code; the LLM smoke imports the mock from tests/**.");
exit(1);
}
console.log(`[llm-mock-guard] OK — no @coreci/llm-mock imports in prod source${scanBuilt ? " or build output" : ""}.`);