Files
fusion/packages/dashboard/src/__tests__/planning-flow-diagnostics-guardrail.test.ts
gsxdsm bce7dbd96f chore: consolidate test files into __tests__/ dirs and clean stray engine artifacts
- Move all co-located *.test.* files into sibling __tests__/ directories so the
  layout is consistent across packages (159 renames + content-rewrite moves).
  Updates relative imports, vi.mock specifiers, and __dirname/import.meta.url
  path resolutions where tests read fixtures from disk.
- Drop tracked tsc-emit alongside engine .ts sources (auth-storage/logger/
  skill-resolver/context-limit-detector/pi.{js,d.ts,*.map}). These were
  accidentally committed in a merge and the stale pi.js was masking a real
  test-mock vs source mismatch (tests imported "../pi.js" and vite preferred
  the stale build over pi.ts).
- Add packages/engine/.gitignore to block future src/*.{js,d.ts,map}.
- Refactor plugin pi-module seams (openclaw/paperclip/hermes) to ESM-import
  createFnAgent / promptWithFallback / describeModel from @fusion/engine
  instead of require()-ing packages/engine/src/pi.js. Adds @fusion/engine to
  the two plugin package.jsons that were missing it; exports describeModel
  from the engine public API.
- Fix engine test mocks now that they run against current pi.ts: add
  ModelRegistry.create static to mocks in pi.test.ts and pi-create-fn-agent
  .test.ts; switch three boundary-result toEqual assertions to toMatchObject
  so the new content/isError fields don't trip exact-match comparison.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 19:39:20 -07:00

147 lines
5.5 KiB
TypeScript

/**
* AI-Session Diagnostics Guardrail Test
*
* This test enforces that AI-session modules use the shared
* ai-session-diagnostics helper instead of raw console.* calls for diagnostics.
*
* Guardrail: These modules must NOT contain direct console.log( / console.warn( / console.error(
* calls in AI-session flow code. Raw console diagnostics indicate incomplete migration
* to the shared helper or accidental reintroduction.
*
* @see ai-session-diagnostics.ts for the shared diagnostics contract
*/
// @vitest-environment node
import { describe, expect, it } from "vitest";
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
/**
* List of AI-session modules that must use the shared diagnostics helper.
* These modules handle AI-session flows and must not use raw console.* diagnostics.
*
* Keep `agent-generation.ts` and `ai-session-store.ts` in this list — they are
* long-lived generation/cleanup surfaces with historical raw-console drift.
*/
const AI_SESSION_FLOW_MODULES = [
"planning.ts",
"mission-interview.ts",
"milestone-slice-interview.ts",
"subtask-breakdown.ts",
"agent-generation.ts",
"ai-session-store.ts",
] as const;
type AiSessionFlowModule = (typeof AI_SESSION_FLOW_MODULES)[number];
/**
* Patterns that indicate raw console diagnostics in AI-session failure paths.
* These should be caught by the guardrail.
* Note: Patterns must have global flag (g) for use with matchAll.
*/
const RAW_CONSOLE_PATTERNS: readonly RegExp[] = [
/console\.log\(/g,
/console\.warn\(/g,
/console\.error\(/g,
];
type RawConsolePattern = (typeof RAW_CONSOLE_PATTERNS)[number];
/**
* Read the source content of an AI-session flow module.
* Throws if the file cannot be read.
*/
function readModuleSource(moduleName: AiSessionFlowModule): string {
const modulePath = resolve(import.meta.dirname, "..", moduleName);
return readFileSync(modulePath, "utf-8");
}
/**
* Find all raw console calls in a module's source.
* Returns an array of { pattern, match } objects for each found violation.
*/
function findRawConsoleCalls(
source: string,
patterns: readonly RegExp[]
): Array<{ pattern: RegExp; match: RegExpMatchArray }> {
const violations: Array<{ pattern: RegExp; match: RegExpMatchArray }> = [];
for (const pattern of patterns) {
const matches = source.matchAll(pattern);
for (const match of matches) {
violations.push({ pattern, match });
}
}
return violations;
}
describe("AI-Session Diagnostics Guardrail", () => {
it("explicitly guards agent-generation and ai-session-store modules", () => {
expect(AI_SESSION_FLOW_MODULES).toContain("agent-generation.ts");
expect(AI_SESSION_FLOW_MODULES).toContain("ai-session-store.ts");
});
/**
* Test that each AI-session flow module uses the shared diagnostics helper
* instead of raw console.* calls.
*
* This guardrail prevents:
* - Incomplete migrations where raw console.* remains
* - Accidental reintroduction of raw console diagnostics
* - Inconsistent diagnostics across AI-session modules
*/
describe("AI-session failure paths must use shared diagnostics helper", () => {
for (const moduleName of AI_SESSION_FLOW_MODULES) {
const moduleShortName = moduleName.replace(".ts", "");
it(`${moduleShortName} does not contain raw console.* diagnostics`, () => {
const source = readModuleSource(moduleName);
const violations = findRawConsoleCalls(source, RAW_CONSOLE_PATTERNS);
if (violations.length > 0) {
const violationDetails = violations
.map(({ pattern, match }) => {
// Calculate line number from match index
const beforeMatch = source.slice(0, match.index);
const lineNumber = beforeMatch.split("\n").length;
return ` Line ${lineNumber}: ${match[0]}`;
})
.join("\n");
expect.fail(
`${moduleName} contains raw console.* diagnostics in AI-session flow code.\n` +
`Expected: Use createSessionDiagnostics() + diagnostics.error() from ai-session-diagnostics.js\n` +
`Found ${violations.length} violation(s):\n` +
`${violationDetails}\n\n` +
`Migration guide:\n` +
` 1. Import: import { createSessionDiagnostics } from "../ai-session-diagnostics.js";\n` +
` 2. Create: const diagnostics = createSessionDiagnostics("${moduleShortName}");\n` +
` 3. Replace: console.error("msg:", err) -> diagnostics.errorFromException("msg", err, { sessionId, operation });\n` +
` 4. Replace: console.error("msg") -> diagnostics.error("msg", { sessionId, operation });`
);
}
});
}
});
/**
* Verify that the shared diagnostics helper exists and exports expected APIs.
* This ensures the guardrail itself has a valid target to enforce against.
*/
describe("shared diagnostics helper contract", () => {
it("ai-session-diagnostics exports required APIs", async () => {
const helperPath = resolve(import.meta.dirname, "..", "ai-session-diagnostics.ts");
const helperSource = readFileSync(helperPath, "utf-8");
// Verify the helper exports the core APIs
expect(helperSource).toContain("createSessionDiagnostics");
expect(helperSource).toContain("setDiagnosticsSink");
expect(helperSource).toContain("resetDiagnosticsSink");
expect(helperSource).toContain("nonfatal");
expect(helperSource).toContain("errorFromException");
});
});
});