v0.2.0: Git-native architecture (#1)

This commit was merged in pull request #1.
This commit is contained in:
2026-05-29 12:59:45 +00:00
parent 9cf5c000d9
commit 6e637e4af0
50 changed files with 5852 additions and 135 deletions
+164
View File
@@ -0,0 +1,164 @@
import * as fs from "node:fs";
import * as path from "node:path";
import * as os from "node:os";
import { DecisionEngine, DecisionInput } from "../core/decision-engine.js";
import { DEFAULT_CI_CONFIG } from "../types/config.js";
describe("DecisionEngine", () => {
let tempDir: string;
let engine: DecisionEngine;
beforeEach(() => {
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "ci-decision-test-"));
engine = new DecisionEngine(DEFAULT_CI_CONFIG, tempDir);
});
afterEach(() => {
fs.rmSync(tempDir, { recursive: true, force: true });
});
const baseInput: DecisionInput = {
decision: "Use PostgreSQL for storage",
rationale: "Strong ecosystem, ACID compliance needed",
confidence: 0.95,
category: "technology_choice",
alternatives_considered: [
{ option: "MongoDB", rejected_reason: "No ACID transactions" },
{ option: "SQLite", rejected_reason: "No concurrent writes" },
],
learnship_equivalent: "discuss-phase would ask: What database? Options: A) PostgreSQL B) MongoDB",
};
describe("makeDecision", () => {
it("auto-decides with high confidence (above threshold)", () => {
const result = engine.makeDecision(baseInput);
expect(result.escalated).toBe(false);
expect(result.decision.id).toMatch(/^D-\d{3}$/);
expect(result.decision.confidence).toBe(0.95);
expect(result.decision.category).toBe("technology_choice");
});
it("escalates with low confidence (below threshold)", () => {
const result = engine.makeDecision({
...baseInput,
confidence: 0.4,
});
expect(result.escalated).toBe(true);
expect(result.reason).toContain("below threshold");
});
it("auto-decides at exactly threshold confidence", () => {
const result = engine.makeDecision({
...baseInput,
confidence: 0.6,
});
expect(result.escalated).toBe(false);
});
it("increments decision IDs sequentially", () => {
const result1 = engine.makeDecision(baseInput);
const result2 = engine.makeDecision(baseInput);
expect(result1.decision.id).toBe("D-001");
expect(result2.decision.id).toBe("D-002");
});
it("generates commit message for git-native audit trail", () => {
const result = engine.makeDecision(baseInput);
expect(result.commitMessage).toBeDefined();
expect(result.commitMessage).toContain("---ci---");
expect(result.commitMessage).toContain("D-001");
expect(result.commitMessage).toContain("Use PostgreSQL for storage");
});
it("preserves alternatives in the decision", () => {
const result = engine.makeDecision(baseInput);
expect(result.decision.alternatives_considered).toHaveLength(2);
expect(result.decision.alternatives_considered![0].option).toBe("MongoDB");
});
it("sets human_override to null by default", () => {
const result = engine.makeDecision(baseInput);
expect(result.decision.human_override).toBeNull();
});
});
describe("makeHighConfidenceDecision", () => {
it("creates a decision with 0.95 confidence", () => {
const result = engine.makeHighConfidenceDecision(
"Use REST API",
"REST is well-understood and has wide tooling support",
"architecture"
);
expect(result.escalated).toBe(false);
expect(result.decision.confidence).toBe(0.95);
});
});
describe("makeMediumConfidenceDecision", () => {
it("creates a decision with 0.7 confidence", () => {
const result = engine.makeMediumConfidenceDecision(
"Use JWT for auth",
"JWT is standard for stateless APIs",
"implementation_approach"
);
expect(result.escalated).toBe(false);
expect(result.decision.confidence).toBe(0.7);
});
it("escalates if threshold is raised above 0.7", () => {
const strictConfig = {
...DEFAULT_CI_CONFIG,
autonomy: { ...DEFAULT_CI_CONFIG.autonomy, decision_confidence_threshold: 0.8 },
};
const strictEngine = new DecisionEngine(strictConfig, tempDir);
const result = strictEngine.makeMediumConfidenceDecision(
"Use JWT for auth",
"JWT is standard",
"implementation_approach"
);
expect(result.escalated).toBe(true);
});
});
describe("shouldAutoDecide", () => {
it("returns true when confidence meets threshold", () => {
expect(engine.shouldAutoDecide(0.6)).toBe(true);
expect(engine.shouldAutoDecide(0.8)).toBe(true);
expect(engine.shouldAutoDecide(1.0)).toBe(true);
});
it("returns false when confidence is below threshold", () => {
expect(engine.shouldAutoDecide(0.59)).toBe(false);
expect(engine.shouldAutoDecide(0.3)).toBe(false);
expect(engine.shouldAutoDecide(0.0)).toBe(false);
});
});
describe("isIrreversibleAction", () => {
it("detects irreversible actions from escalation_hooks", () => {
expect(engine.isIrreversibleAction("deploy to production")).toBe(true);
expect(engine.isIrreversibleAction("delete_data in database")).toBe(true);
expect(engine.isIrreversibleAction("merge_to_main branch")).toBe(true);
});
it("returns false for non-irreversible actions", () => {
expect(engine.isIrreversibleAction("create file")).toBe(false);
expect(engine.isIrreversibleAction("run tests")).toBe(false);
expect(engine.isIrreversibleAction("refactor code")).toBe(false);
});
});
describe("setPhase", () => {
it("updates the current phase", () => {
engine.setPhase(3);
expect(engine.setPhase).toBeDefined();
});
});
describe("setMilestone", () => {
it("updates the current milestone", () => {
engine.setMilestone("v2.0");
expect(engine.setMilestone).toBeDefined();
});
});
});