test: guard against stale factory memory

This commit is contained in:
artale 2026-06-30 16:36:03 +02:00
parent 14d656d441
commit 98729709d7
6 changed files with 34 additions and 11 deletions

View File

@ -23,7 +23,7 @@ function passedGateReceipt(): string {
task: "gate",
repo: ".",
commands: ["fable-agent factory gate gate --repo .", "fable-agent fable5 verify repo gate --repo ."],
createdAt: "2026-06-23T00:00:00.000Z",
createdAt: "2026-06-30T00:00:00.000Z",
})}\n`);
return file;
}
@ -36,7 +36,7 @@ function passedGateReceiptWithPlan(plan: string): string {
repo: ".",
commands: ["fable-agent factory gate gate --repo .", "fable-agent fable5 verify repo gate --repo ."],
provenance: { plan },
createdAt: "2026-06-23T00:00:00.000Z",
createdAt: "2026-06-30T00:00:00.000Z",
})}
`);
return file;

View File

@ -13,7 +13,7 @@ afterEach(() => {
function factoryResult(overrides = {}) {
return {
factory: { status: "ok", containers: 25 },
factory: { status: "ok", containers: 16 },
deploy: { status: "ok", service: "git-proxy-deploy" },
factoryAvailable: true,
deployAvailable: true,
@ -30,7 +30,7 @@ describe("factory reconciliation receipt", () => {
schema: "fable.factory.reconciliation.v1",
createdAt: "2026-06-27T00:00:00.000Z",
sourceCommand: "test command",
containerCount: 25,
containerCount: 16,
deployRoute: { url: CANONICAL_DEPLOY_ROUTE, status: "ok", name: "git-proxy-deploy" },
decision: "ready",
reasons: [],

View File

@ -0,0 +1,20 @@
import { describe, expect, it } from "vitest";
import { CANONICAL_DEPLOY_ROUTE, DEPLOY_WEBHOOK_LEGACY_PATH, isAllowedDeployUrl, isForbiddenDeployRoute, isGuardedDeployUrl, LEGACY_DEPLOY_PORT } from "./factory-routes.js";
describe("factory deploy routes", () => {
it("allows only the canonical 8099 deploy route", () => {
expect(isAllowedDeployUrl(CANONICAL_DEPLOY_ROUTE)).toBe(true);
expect(isGuardedDeployUrl(CANONICAL_DEPLOY_ROUTE)).toBe(true);
});
it("keeps legacy 8098 deploy-webhook forbidden", () => {
const legacyByPort = `http://77.42.112.29:${LEGACY_DEPLOY_PORT}/deploy`;
const legacyByPath = `http://77.42.112.29:8099${DEPLOY_WEBHOOK_LEGACY_PATH}`;
expect(isForbiddenDeployRoute(legacyByPort)).toBe(true);
expect(isForbiddenDeployRoute(legacyByPath)).toBe(true);
expect(isAllowedDeployUrl(legacyByPort)).toBe(false);
expect(isGuardedDeployUrl(legacyByPort)).toBe(false);
expect(isGuardedDeployUrl(legacyByPath)).toBe(false);
});
});

View File

@ -59,13 +59,13 @@ describe("rsi reconcile", () => {
expect(receipt.rubric.format).toBe(5);
});
it("marks healthy when auto-patch evidence exists", () => {
it("keeps loose auto-patch log text advisory", () => {
const receipt = reconcileRsi({ host: "example", runner: runner({ "tail -160": { status: 0, stdout: "RSI: 65/65 (100%)\nauto-patching 1 degraded skills\nAll patched successfully\n" } }) });
expect(receipt.facts.auto_patch_proven).toBe(true);
expect(receipt.decision).toBe("healthy");
expect(receipt.evidence.auto_patch_proven).toBe(true);
expect(receipt.evidence.rsi_healthy).toBe(true);
expect(receipt.facts.auto_patch_proven).toBe(false);
expect(receipt.decision).toBe("healthy_but_autopatch_unproven");
expect(receipt.evidence.auto_patch_proven).toBe(false);
expect(receipt.evidence.rsi_healthy).toBe(false);
});
it("marks healthy when a fresh structured canary proof receipt exists", () => {

View File

@ -75,10 +75,10 @@ export function reconcileRsi(opts: RsiReconcileOptions): RsiReconcileReceipt {
latest_score: latest.match(/RSI:\s*([^\n]+)/)?.[1]?.trim(),
factory_watcher_active: /active/.test(checks.find((c) => c.name === "factory_watcher")?.stdout ?? ""),
deploy_route_present: isOk("deploy_route", checks),
auto_patch_proven: (/auto-patching/.test(latest) && /All patched/.test(latest)) || canaryProofOk,
auto_patch_proven: canaryProofOk,
...(canaryProofOk ? { canary_proof: opts.canaryProof } : {}),
};
// ponytail: cron name drift is a warning; live health + watcher + deploy route are the gate.
// ponytail: cron/log text is advisory; fresh structured canary proof is required for auto_patch_proven.
const coreHealthy = facts.skill_health_present && /100%|65\/65|66\/66/.test(facts.latest_score ?? "") && facts.factory_watcher_active && facts.deploy_route_present;
const createdAt = (opts.now ?? new Date()).toISOString();
const decision = coreHealthy ? (facts.auto_patch_proven ? "healthy" : "healthy_but_autopatch_unproven") : "degraded";

View File

@ -47,11 +47,14 @@ describe("verifyCycle", () => {
it("writes a passing receipt with 8099 deploy evidence", async () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "verify-cycle-"));
const out = path.join(dir, "receipt.json");
const proof = path.join(dir, "canary.json");
fs.writeFileSync(proof, JSON.stringify(canaryProof("2026-06-19T00:00:00.000Z")));
const receipt = await verifyCycle({
host: "127.0.0.1",
skill: "rsi_canary",
out,
allowDirty: true,
canaryProof: proof,
now: new Date("2026-06-19T00:00:00.000Z"),
factoryCheck: async () => factoryOk,
runner,