feat: gate factory with cyber receipts

This commit is contained in:
artale 2026-06-18 02:27:44 +02:00
parent 2fadb8a428
commit 93e5e06a9f
2 changed files with 37 additions and 1 deletions

View File

@ -172,6 +172,9 @@ fable-agent plinius godmode "improve explanation quality"
- `--file <path>`
- `factory gate <task>`
- `--repo <path>`
- `--host <host>`
- `--port <n>`
- `--capabilities <path>`
- `--diagnostic-only`
### `forgejo`

View File

@ -1456,8 +1456,11 @@ factory
.command("gate <task>")
.description("Run factory check plus local ZTE/repo verification gate")
.option("--repo <path>", "Repo path to verify", ".")
.option("--host <host>", "Factory SSH host for cyber preflight", "77.42.112.29")
.option("--port <n>", "Factory SSH port for cyber preflight", (v) => Number(v), 2222)
.option("--capabilities <path>", "Capability registry path", "factory-capabilities.yaml")
.option("--diagnostic-only", "Bypass live factory/deploy readiness failures for non-deploy diagnostics")
.action(async (task: string, opts: { repo?: string; diagnosticOnly?: boolean }) => {
.action(async (task: string, opts: { repo?: string; host?: string; port?: number; capabilities?: string; diagnosticOnly?: boolean }) => {
const { spawnSync } = await import("node:child_process");
const repo = path.resolve(opts.repo ?? ".");
const status = await printFactoryCheck();
@ -1469,6 +1472,36 @@ factory
process.exit(1);
}
const { formatCapabilityReport, loadFactoryCapabilities, probeFactoryCapabilities } = await import("./fable5/factory-capabilities.js");
const capabilitiesPath = path.resolve(opts.capabilities ?? "factory-capabilities.yaml");
const capabilities = await probeFactoryCapabilities(loadFactoryCapabilities(capabilitiesPath));
console.log(formatCapabilityReport(capabilities));
if (!capabilities.requiredOk && !opts.diagnosticOnly) {
const { appendZteReceipt, createZteReceipt } = await import("./fable5/zte-protocol.js");
const reason = "required factory capabilities are missing";
appendZteReceipt(repo, createZteReceipt(task, repo, "blocked", ["factory capabilities"], reason));
console.error(` ✗ Factory gate failed: ${reason}; use --diagnostic-only for non-deploy diagnostics.`);
process.exit(1);
}
const { runCyberPreflight } = await import("./fable5/cyber-preflight.js");
const { reconcileCyberPreflight, writeCyberReconcileReceipt } = await import("./fable5/cyber-reconcile.js");
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
const preflightPath = path.join(repo, ".fable", "cyber-preflight", `factory-gate-${stamp}.json`);
const reconcilePath = path.join(repo, ".fable", "cyber-preflight", `factory-gate-${stamp}-reconcile.json`);
const preflight = runCyberPreflight({ host: opts.host ?? "77.42.112.29", port: opts.port, out: preflightPath });
const reconcile = reconcileCyberPreflight(preflightPath, capabilitiesPath);
writeCyberReconcileReceipt(reconcilePath, reconcile);
console.log(` Cyber preflight: ${preflight.decision} (${preflightPath})`);
console.log(` Cyber reconcile: ${reconcile.decision} (${reconcilePath})`);
if ((preflight.decision !== "allow_read_only" || reconcile.decision !== "allow_read_only") && !opts.diagnosticOnly) {
const { appendZteReceipt, createZteReceipt } = await import("./fable5/zte-protocol.js");
const reason = "cyber preflight/reconcile did not pass";
appendZteReceipt(repo, createZteReceipt(task, repo, "blocked", ["cyber preflight", "cyber reconcile"], reason));
console.error(` ✗ Factory gate failed: ${reason}; use --diagnostic-only for non-deploy diagnostics.`);
process.exit(1);
}
console.log(` ZTE Gate: ${task}`);
console.log(` Repo: ${repo}`);
console.log(``);