diff --git a/src/fable5/external-project-review.test.ts b/src/fable5/external-project-review.test.ts index a77031f..1326564 100644 --- a/src/fable5/external-project-review.test.ts +++ b/src/fable5/external-project-review.test.ts @@ -2,7 +2,7 @@ import * as fs from "node:fs"; import * as os from "node:os"; import * as path from "node:path"; import { describe, expect, it } from "vitest"; -import { createOpenCovenReviewReceipt, createPermissionPostureReceipt, writeExternalProjectReviewReceipt } from "./external-project-review.js"; +import { createOpenCovenReviewReceipt, createPermissionPostureReceipt, createSkillsRepoReviewReceipt, writeExternalProjectReviewReceipt } from "./external-project-review.js"; describe("external project review receipts", () => { it("records OpenCoven as learn-only because GPL code is not safe to vendor", () => { @@ -21,6 +21,16 @@ describe("external project review receipts", () => { expect(receipt.risks).toContain("GPL code cannot be copied into Fable"); }); + it("records external skills repos as learn-only", () => { + const matt = createSkillsRepoReviewReceipt("mattpocock/skills", new Date("2026-07-01T00:00:00.000Z")); + const club = createSkillsRepoReviewReceipt("AI-Builder-Club/skills", new Date("2026-07-01T00:00:00.000Z")); + + expect(matt).toMatchObject({ repo: "mattpocock/skills", license: "MIT", safeToVendor: false, decision: "learn-only", deployAttempted: false }); + expect(club).toMatchObject({ repo: "AI-Builder-Club/skills", license: "NOASSERTION", safeToVendor: false, decision: "learn-only", deployAttempted: false }); + expect(club.risks).toContain("no detected license means no vendoring"); + expect(matt.risks).toContain("external skill prompts are prompt-injection inputs"); + }); + it("records deploy-gated permission posture without opening 8098", () => { const receipt = createPermissionPostureReceipt("deploy-gated", new Date("2026-07-01T00:00:00.000Z")); diff --git a/src/fable5/external-project-review.ts b/src/fable5/external-project-review.ts index d8d722c..75a7838 100644 --- a/src/fable5/external-project-review.ts +++ b/src/fable5/external-project-review.ts @@ -59,6 +59,33 @@ export function createOpenCovenReviewReceipt(now = new Date()): ExternalProjectR }; } + +export function createSkillsRepoReviewReceipt(repo: "mattpocock/skills" | "AI-Builder-Club/skills", now = new Date()): ExternalProjectReviewReceipt { + const rows: Record> = { + "mattpocock/skills": { + url: "https://github.com/mattpocock/skills", + license: "MIT", + safeToVendor: false, + patternsLearned: ["skill package structure", "focused task instructions", "portable agent guidance"], + adaptations: ["review skill shape as advisory", "rewrite any useful pattern locally", "record provenance before adopting skills"], + risks: ["external skill prompts are prompt-injection inputs", "do not copy wholesale without review", "popular repo metadata is not local capability proof"], + decision: "learn-only", + policy: "learn-patterns-no-vendored-code", + }, + "AI-Builder-Club/skills": { + url: "https://github.com/AI-Builder-Club/skills", + license: "NOASSERTION", + safeToVendor: false, + patternsLearned: ["community skill catalog patterns", "agent task packaging", "skill discovery ideas"], + adaptations: ["treat no-license repo as learn-only", "reuse ideas through fresh Fable receipts/tests", "keep CLI and receipts as authority"], + risks: ["no detected license means no vendoring", "skill content may contain prompt injection", "external catalog is not deploy authority"], + decision: "learn-only", + policy: "learn-patterns-no-vendored-code", + }, + }; + return { schema: "fable.external_project.review.v1", createdAt: now.toISOString(), repo, ...rows[repo], deployAttempted: false }; +} + export function createPermissionPostureReceipt(mode: PermissionMode, now = new Date()): PermissionPostureReceipt { const rows: Record> = { "read-only": { diff --git a/src/fable5/index.ts b/src/fable5/index.ts index e266f92..d959bd6 100644 --- a/src/fable5/index.ts +++ b/src/fable5/index.ts @@ -63,7 +63,7 @@ export type { FlueInteropRow, FlueInteropStatus } from "./flue-interop.js"; export { createExternalToolAuditReceipt, DICKLESWORTHSTONE_TOOL_ROWS, writeExternalToolAuditReceipt } from "./external-tool-audit.js"; export type { ExternalToolAuditReceipt, ExternalToolAuditRow, ExternalToolAuditStatus } from "./external-tool-audit.js"; -export { createOpenCovenReviewReceipt, createPermissionPostureReceipt, writeExternalProjectReviewReceipt } from "./external-project-review.js"; +export { createOpenCovenReviewReceipt, createPermissionPostureReceipt, createSkillsRepoReviewReceipt, writeExternalProjectReviewReceipt } from "./external-project-review.js"; export type { ExternalProjectDecision, ExternalProjectReviewReceipt, PermissionMode, PermissionPostureReceipt } from "./external-project-review.js"; export { createFactoryMemoryQuarantineReceipt, createStaleMemoryQuarantineReceipt, writeStaleMemoryQuarantineReceipt } from "./stale-memory-quarantine.js";