import fs from "node:fs"; import path from "node:path"; import { describe, expect, it } from "vitest"; import { getEnvVariableDefinition } from "@toolctrl/config"; import { loadFeatureManifests } from "@toolctrl/plugins"; import { queueNames } from "@toolctrl/queue"; const manifestLoad = await loadFeatureManifests(); const manifests = manifestLoad.manifests; const requiredPlaybooks = [ "docs/agent-entrypoints.md", "docs/agent-playbooks/feature-integration.md", "docs/agent-playbooks/mvp-intake.md", "docs/agent-playbooks/admin-managed-features.md", "docs/agent-playbooks/worker-backed-features.md", ]; function expectStringArray(value: unknown) { expect(Array.isArray(value)).toBe(true); expect((value as unknown[]).every((item) => typeof item === "string")).toBe(true); } describe("agent feature planning docs", () => { it("provides the required agent entrypoint and playbooks", () => { requiredPlaybooks.forEach((file) => { expect(fs.existsSync(path.join(process.cwd(), file)), file).toBe(true); }); }); it("documents the key concepts agents need before implementation", () => { const entrypoint = fs.readFileSync(path.join(process.cwd(), "docs/agent-entrypoints.md"), "utf8"); const featureIntegration = fs.readFileSync(path.join(process.cwd(), "docs/agent-playbooks/feature-integration.md"), "utf8"); const adminManaged = fs.readFileSync(path.join(process.cwd(), "docs/agent-playbooks/admin-managed-features.md"), "utf8"); const workerBacked = fs.readFileSync(path.join(process.cwd(), "docs/agent-playbooks/worker-backed-features.md"), "utf8"); expect(entrypoint).toContain("MVP import"); expect(featureIntegration).toContain("Feature Integration Playbook"); expect(featureIntegration).toContain("Do not expose provider secrets"); expect(adminManaged).toContain("admin-managed"); expect(adminManaged).toContain("Provider keys must not be sent to tenant loaders"); expect(workerBacked).toContain("Browser-backed features require extra constraints"); }); }); describe("feature manifests", () => { it("keeps every manifest on the integration-aware schema", () => { manifests.forEach((manifest) => { expect(manifest.schemaVersion).toBe("1.0"); expect(manifest.id).toMatch(/^[a-z0-9][a-z0-9-]*$/); expect(manifest.title).toEqual(expect.any(String)); expect(["core", "plugin-candidate", "remove-candidate", "mixed"]).toContain(manifest.classification); expect(["active", "proposed", "review-needed"]).toContain(manifest.status); expectStringArray(manifest.rationale); expectStringArray(manifest.codeAreas); expectStringArray(manifest.dependencies); expectStringArray(manifest.consumers); expectStringArray(manifest.boundaries.mustKeep); expectStringArray(manifest.boundaries.mayMoveToPlugin); expectStringArray(manifest.boundaries.mustNotTouch); expectStringArray(manifest.pruning.notes); expectStringArray(manifest.validation.source); expect(manifest.validation.lastReviewed).toMatch(/^\d{4}-\d{2}-\d{2}$/); }); }); it("requires explicit ownership, surfaces, configuration, workers and UAT", () => { manifests.forEach((manifest) => { expect(["tenant-owned", "admin-managed", "hybrid", "system-only"]).toContain(manifest.integration.ownership); expectStringArray(manifest.integration.surfaces.adminRoutes); expectStringArray(manifest.integration.surfaces.tenantRoutes); expectStringArray(manifest.integration.surfaces.publicRoutes); expectStringArray(manifest.integration.surfaces.apiRoutes); expectStringArray(manifest.integration.configuration.admin); expectStringArray(manifest.integration.configuration.tenant); expectStringArray(manifest.integration.configuration.environment); expectStringArray(manifest.integration.configuration.secrets); expect(typeof manifest.integration.workers.required).toBe("boolean"); expectStringArray(manifest.integration.workers.queues); expectStringArray(manifest.integration.workers.jobs); expectStringArray(manifest.integration.workers.notes); expectStringArray(manifest.integration.externalProviders); expectStringArray(manifest.integration.permissions); expectStringArray(manifest.integration.auditEvents); expectStringArray(manifest.integration.billingImpacts); expectStringArray(manifest.integration.uat); expect(manifest.integration.uat.length, manifest.id).toBeGreaterThan(0); }); }); it("loads real docs manifests without loader diagnostics", () => { expect(manifestLoad.ok).toBe(true); expect(manifestLoad.diagnostics).toEqual([]); }); it("guards the Feature Manifest manifest decision contract in docs", () => { const inventory = fs.readFileSync(path.join(process.cwd(), "docs/feature-inventory.md"), "utf8"); const manifestReadme = fs.readFileSync(path.join(process.cwd(), "docs/feature-manifests/README.md"), "utf8"); ["Manifest-Decision", "Pruned Core", "Full-Boilerplate", "plugin-candidate", "remove-candidate"].forEach((anchor) => { expect(inventory, `inventory missing ${anchor}`).toContain(anchor); expect(manifestReadme, `manifest README missing ${anchor}`).toContain(anchor); }); ["core", "plugin-candidates", "remove-candidates"].forEach((manifestIdAnchor) => { expect(manifestReadme, `manifest README missing ${manifestIdAnchor}`).toContain(manifestIdAnchor); }); }); it("guards admin-managed secret ownership and worker-backed declarations", () => { manifests .filter((manifest) => manifest.integration.ownership === "admin-managed") .forEach((manifest) => { expect(manifest.integration.configuration.admin.length, manifest.id).toBeGreaterThan(0); expect(manifest.integration.configuration.secrets.join(" ").toLowerCase(), manifest.id).toMatch(/admin|platform/); expect(manifest.integration.permissions.some((permission) => permission.startsWith("admin.")), manifest.id).toBe(true); expect(manifest.integration.auditEvents.length, manifest.id).toBeGreaterThan(0); }); manifests .filter((manifest) => manifest.integration.workers.required) .forEach((manifest) => { expect(manifest.integration.workers.queues.length, manifest.id).toBeGreaterThan(0); expect(manifest.integration.workers.jobs.length, manifest.id).toBeGreaterThan(0); }); }); it("includes the AI chatbot reference pattern for admin-managed worker-backed features", () => { const chatbot = manifests.find((manifest) => manifest.id === "ai-chatbot-admin-managed"); expect(chatbot).toBeDefined(); expect(chatbot?.integration.ownership).toBe("admin-managed"); expect(chatbot?.integration.externalProviders).toContain("deployment-region-ai-provider"); expect(chatbot?.integration.workers.required).toBe(true); expect(chatbot?.integration.workers.queues).toContain(queueNames.aiChatbot); expect(chatbot?.integration.workers.queues).toContain(queueNames.browserAutomation); expect(chatbot?.integration.workers.jobs).toEqual(expect.arrayContaining(["chat-completion", "browser-session-run", "browser-session-cleanup", "usage-flush"])); expect(chatbot?.integration.configuration.environment).toEqual( expect.arrayContaining([ "MISTRAL_API_KEY", "MISTRAL_MODEL", "SCALEWAY_API_KEY", "SCALEWAY_MODEL", "SCALEWAY_BASE_URL", "ALEPH_ALPHA_API_KEY", "ALEPH_ALPHA_MODEL", "ALEPH_ALPHA_BASE_URL", "BULLMQ_REDIS_URL", "BROWSER_RUNTIME_URL", ]) ); expect(chatbot?.integration.configuration.secrets.join(" ")).toContain("never be exposed"); }); it("keeps admin-managed AI/browser feature contracts backed by config and queue packages", () => { [ "BULLMQ_REDIS_URL", "BROWSER_RUNTIME_URL", "BROWSER_RUNTIME_API_KEY", "BROWSER_SESSION_TIMEOUT_MS", ].forEach((name) => { const definition = getEnvVariableDefinition(name); expect(definition?.classification, name).toBe("optional-provider"); expect(definition?.targets.some((target) => target === "api" || target === "worker"), name).toBe(true); }); expect(queueNames.aiChatbot).toBe("toolctrl.ai-chatbot"); expect(queueNames.browserAutomation).toBe("toolctrl.browser-automation"); }); });