mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-17 23:51:08 +00:00
177 lines
5.9 KiB
TypeScript
177 lines
5.9 KiB
TypeScript
import fs from "fs"
|
|
import path from "path"
|
|
import { describe, it, expect, beforeEach, afterEach } from "vitest"
|
|
import { TraceLogger, type MutationClass } from "../src/core/intent/TraceLogger"
|
|
|
|
const orchestrationDir = path.join(process.cwd(), ".orchestration")
|
|
const tracePath = path.join(orchestrationDir, "agent_trace.jsonl")
|
|
|
|
beforeEach(() => {
|
|
if (fs.existsSync(orchestrationDir)) {
|
|
fs.rmSync(orchestrationDir, { recursive: true, force: true })
|
|
}
|
|
})
|
|
|
|
afterEach(() => {
|
|
if (fs.existsSync(orchestrationDir)) {
|
|
fs.rmSync(orchestrationDir, { recursive: true, force: true })
|
|
}
|
|
})
|
|
|
|
describe("Phase 3: AI-Native Git Layer - Semantic Tracking", () => {
|
|
it("generates SHA-256 hashes for content", () => {
|
|
const content = "export const foo = () => {}"
|
|
const hash = TraceLogger.hashContent(content)
|
|
|
|
// Verify hash is a valid SHA-256 (64 hex characters)
|
|
expect(hash).toMatch(/^[a-f0-9]{64}$/)
|
|
|
|
// Verify same content produces same hash
|
|
const hash2 = TraceLogger.hashContent(content)
|
|
expect(hash).toBe(hash2)
|
|
|
|
// Verify different content produces different hash
|
|
const hash3 = TraceLogger.hashContent("different content")
|
|
expect(hash).not.toBe(hash3)
|
|
})
|
|
|
|
it("classifies mutations as AST_REFACTOR for syntax-only changes", () => {
|
|
const original = "function hello() {\n console.log('hello');\n}\n"
|
|
// Minor formatting change: add semicolon (< 20% change)
|
|
const updated = "function hello() {\n console.log('hello');\n};\n"
|
|
|
|
const classification = TraceLogger.classifyMutation(updated, original, false)
|
|
expect(classification).toBe("AST_REFACTOR")
|
|
})
|
|
|
|
it("classifies mutations as INTENT_EVOLUTION for new files", () => {
|
|
const content = "export const newFeature = () => {}"
|
|
|
|
const classification = TraceLogger.classifyMutation(content, undefined, true)
|
|
expect(classification).toBe("INTENT_EVOLUTION")
|
|
})
|
|
|
|
it("classifies mutations as INTENT_EVOLUTION for significant changes (>20%)", () => {
|
|
const original = "function original() {\n return 'hello';\n}"
|
|
// Much longer content
|
|
const updated = `function refactored() {
|
|
// New implementation with additional features
|
|
return 'hello world with new features';
|
|
}
|
|
|
|
export const newExport = () => {}
|
|
export const anotherExport = () => {}`
|
|
|
|
const classification = TraceLogger.classifyMutation(updated, original, false)
|
|
expect(classification).toBe("INTENT_EVOLUTION")
|
|
})
|
|
|
|
it("logs trace entries to agent_trace.jsonl with intent_id and content_hash", () => {
|
|
const logger = new TraceLogger()
|
|
const content = "console.log('test')"
|
|
const intentId = "INT-001"
|
|
const filePath = "src/test.ts"
|
|
|
|
logger.logTrace(intentId, filePath, content, "AST_REFACTOR")
|
|
|
|
// Verify file was created
|
|
expect(fs.existsSync(tracePath)).toBe(true)
|
|
|
|
// Verify entry was logged
|
|
const traces = logger.readTraces()
|
|
expect(traces).toHaveLength(1)
|
|
|
|
const entry = traces[0]
|
|
expect(entry.intent_id).toBe(intentId)
|
|
expect(entry.path).toBe(filePath)
|
|
expect(entry.mutation_class).toBe("AST_REFACTOR")
|
|
expect(entry.content_hash).toBe(TraceLogger.hashContent(content))
|
|
expect(entry.timestamp).toBeDefined()
|
|
})
|
|
|
|
it("logs trace entries with req_id when provided", () => {
|
|
const logger = new TraceLogger()
|
|
const content = "new feature code"
|
|
const intentId = "INT-001"
|
|
const reqId = "REQ-12345"
|
|
|
|
logger.logTrace(intentId, "src/feature.ts", content, "INTENT_EVOLUTION", reqId)
|
|
|
|
const traces = logger.readTraces()
|
|
expect(traces).toHaveLength(1)
|
|
|
|
const entry = traces[0]
|
|
expect(entry.req_id).toBe(reqId)
|
|
expect(entry.mutation_class).toBe("INTENT_EVOLUTION")
|
|
})
|
|
|
|
it("appends multiple trace entries to agent_trace.jsonl", () => {
|
|
const logger = new TraceLogger()
|
|
|
|
// Log first entry
|
|
logger.logTrace("INT-001", "src/auth.ts", "auth code", "AST_REFACTOR")
|
|
|
|
// Log second entry
|
|
logger.logTrace("INT-002", "src/feature.ts", "feature code", "INTENT_EVOLUTION", "REQ-789")
|
|
|
|
const traces = logger.readTraces()
|
|
expect(traces).toHaveLength(2)
|
|
|
|
// Verify first entry
|
|
expect(traces[0].intent_id).toBe("INT-001")
|
|
expect(traces[0].path).toBe("src/auth.ts")
|
|
expect(traces[0].mutation_class).toBe("AST_REFACTOR")
|
|
|
|
// Verify second entry
|
|
expect(traces[1].intent_id).toBe("INT-002")
|
|
expect(traces[1].path).toBe("src/feature.ts")
|
|
expect(traces[1].mutation_class).toBe("INTENT_EVOLUTION")
|
|
expect(traces[1].req_id).toBe("REQ-789")
|
|
})
|
|
|
|
it("queries traces by intent_id", () => {
|
|
const logger = new TraceLogger()
|
|
|
|
// Log traces for different intents
|
|
logger.logTrace("INT-001", "src/auth.ts", "auth code", "AST_REFACTOR")
|
|
logger.logTrace("INT-001", "src/auth-utils.ts", "utils code", "AST_REFACTOR")
|
|
logger.logTrace("INT-002", "src/feature.ts", "feature code", "INTENT_EVOLUTION")
|
|
|
|
const int001Traces = logger.getTracesByIntent("INT-001")
|
|
expect(int001Traces).toHaveLength(2)
|
|
expect(int001Traces.every((e) => e.intent_id === "INT-001")).toBe(true)
|
|
|
|
const int002Traces = logger.getTracesByIntent("INT-002")
|
|
expect(int002Traces).toHaveLength(1)
|
|
expect(int002Traces[0].path).toBe("src/feature.ts")
|
|
})
|
|
|
|
it("handles missing intent_id (null) in traces", () => {
|
|
const logger = new TraceLogger()
|
|
|
|
// Log without intent_id (pre-intent phase)
|
|
logger.logTrace(null, "src/setup.ts", "setup code", "INTENT_EVOLUTION")
|
|
|
|
const traces = logger.readTraces()
|
|
expect(traces).toHaveLength(1)
|
|
expect(traces[0].intent_id).toBeNull()
|
|
})
|
|
|
|
it("serializes trace entries as valid JSON lines format", () => {
|
|
const logger = new TraceLogger()
|
|
logger.logTrace("INT-001", "src/file.ts", "code", "AST_REFACTOR", "REQ-123")
|
|
|
|
const rawContent = fs.readFileSync(tracePath, "utf8")
|
|
const lines = rawContent.trim().split("\n")
|
|
|
|
expect(lines).toHaveLength(1)
|
|
|
|
// Verify each line is valid JSON
|
|
const parsed = JSON.parse(lines[0])
|
|
expect(parsed).toHaveProperty("intent_id")
|
|
expect(parsed).toHaveProperty("mutation_class")
|
|
expect(parsed).toHaveProperty("content_hash")
|
|
expect(parsed).toHaveProperty("timestamp")
|
|
expect(parsed).toHaveProperty("req_id")
|
|
})
|
|
})
|