import * as fs from "node:fs"; import * as path from "node:path"; export type ExternalProjectDecision = "learn-only" | "blocked"; export type PermissionMode = "read-only" | "plan" | "edit" | "deploy-gated"; export interface ExternalProjectReviewReceipt { schema: "fable.external_project.review.v1"; createdAt: string; repo: string; url: string; license: string; safeToVendor: boolean; patternsLearned: string[]; adaptations: string[]; risks: string[]; decision: ExternalProjectDecision; policy: "learn-patterns-no-vendored-code"; deployAttempted: false; } export interface PermissionPostureReceipt { schema: "fable.permission_posture.receipt.v1"; createdAt: string; mode: PermissionMode; allowed: string[]; blocked: string[]; approvalRequired: boolean; decision: "ready" | "blocked"; deployAttempted: false; } export function createOpenCovenReviewReceipt(now = new Date()): ExternalProjectReviewReceipt { return { schema: "fable.external_project.review.v1", createdAt: now.toISOString(), repo: "OpenCoven/coven-code", url: "https://github.com/OpenCoven/coven-code", license: "GPL-3.0", safeToVendor: false, patternsLearned: [ "local-first agent runtime", "explicit permission modes", "session resume and branching", "agent identity contract", "plugin and bridge seams separated from authority", "no-telemetry-by-default posture", ], adaptations: [ "record external reviews as receipts", "make permission posture explicit", "keep adapters below CLI and receipts as authority", "treat GPL projects as learn-only unless legal review says otherwise", ], risks: ["GPL code cannot be copied into Fable", "remote bridge or MCP must not become deploy authority", "plugin systems add attack surface"], decision: "learn-only", policy: "learn-patterns-no-vendored-code", deployAttempted: false, }; } export function createPermissionPostureReceipt(mode: PermissionMode, now = new Date()): PermissionPostureReceipt { const rows: Record> = { "read-only": { allowed: ["read receipts", "inspect files", "summarize repositories", "emit non-mutating receipts"], blocked: ["write code", "mutate factory", "deploy", "use secrets"], approvalRequired: false, decision: "ready", }, plan: { allowed: ["read receipts", "write plan receipts", "propose diffs"], blocked: ["apply diffs", "deploy", "mutate factory"], approvalRequired: false, decision: "ready", }, edit: { allowed: ["write code", "run tests", "write receipts"], blocked: ["deploy", "mutate factory without gate", "use secrets unless explicitly approved"], approvalRequired: true, decision: "ready", }, "deploy-gated": { allowed: ["deploy only through guarded 8099/deploy with fresh valid gate receipt and explicit approval"], blocked: ["8098/deploy-webhook", "memory-only deploy claims", "deploy without fresh ZTE/factory gate"], approvalRequired: true, decision: "ready", }, }; return { schema: "fable.permission_posture.receipt.v1", createdAt: now.toISOString(), mode, ...rows[mode], deployAttempted: false }; } export function writeExternalProjectReviewReceipt(file: string, receipt: ExternalProjectReviewReceipt | PermissionPostureReceipt): string { fs.mkdirSync(path.dirname(file), { recursive: true }); fs.writeFileSync(file, `${JSON.stringify(receipt, null, 2)}\n`); return file; }