feat: map flue interop gaps

This commit is contained in:
artale 2026-06-17 23:24:33 +02:00
parent 68a0a48669
commit c7cd28c404
5 changed files with 120 additions and 0 deletions

View File

@ -203,6 +203,7 @@ fable-agent plinius godmode "improve explanation quality"
- `fable5 route <task>` - `fable5 route <task>`
- `-d, --domain <domain>` - `-d, --domain <domain>`
- `fable5 models` - `fable5 models`
- `fable5 flue`
- `fable5 verify <task>` - `fable5 verify <task>`
- `--repo <path>` - `--repo <path>`
- `fable5 goal <text>` - `fable5 goal <text>`

View File

@ -0,0 +1,16 @@
import { describe, expect, it } from "vitest";
import { FLUE_INTEROP_ROWS, formatFlueInteropReport, summarizeFlueInterop } from "./flue-interop.js";
describe("flue interop map", () => {
it("keeps Flue as an inspiration map, not vendored code", () => {
const report = formatFlueInteropReport();
expect(FLUE_INTEROP_ROWS.length).toBeGreaterThan(5);
expect(report).toContain("no vendored Flue code");
expect(report).toContain("next implementable patch: channels");
});
it("prioritizes gaps before partials", () => {
expect(summarizeFlueInterop().next.flueConcept).toBe("channels");
});
});

View File

@ -0,0 +1,92 @@
export type FlueInteropStatus = "covered" | "partial" | "gap";
export interface FlueInteropRow {
flueConcept: string;
fableSurface: string;
status: FlueInteropStatus;
nextPatch: string;
}
export const FLUE_INTEROP_ROWS: FlueInteropRow[] = [
{
flueConcept: "agents",
fableSurface: "fable5 meta/teams/chains",
status: "covered",
nextPatch: "Keep using existing MetaAgent, AgentTeams, and AgentChain surfaces.",
},
{
flueConcept: "workflows",
fableSurface: "fable5 workflow + DynamicWorkflows",
status: "covered",
nextPatch: "No new engine; add examples only when a real workflow needs them.",
},
{
flueConcept: "sandboxes",
fableSurface: "WorktreeManager + factory agent-sandbox capability",
status: "partial",
nextPatch: "Add a sandbox command adapter after one deploy path needs isolation beyond git worktrees.",
},
{
flueConcept: "durable execution",
fableSurface: "session checkpointing + StateStore + ZTE receipts",
status: "partial",
nextPatch: "Record workflow step receipts so interrupted multi-step runs resume from the last passed step.",
},
{
flueConcept: "subagents",
fableSurface: "fable5 teams + fan-out workflow",
status: "covered",
nextPatch: "Use existing team roles; do not add another subagent abstraction.",
},
{
flueConcept: "tools and skills",
fableSurface: "SkillRegistry + SKILLS sync + factory capability registry",
status: "covered",
nextPatch: "Bridge manifests only; do not vendor Flue tool code.",
},
{
flueConcept: "MCP",
fableSurface: "factory capability registry",
status: "partial",
nextPatch: "Add optional MCP endpoint fields to the registry when a live MCP server is adopted.",
},
{
flueConcept: "observability",
fableSurface: "agent-observability + Prometheus/Grafana factory services",
status: "covered",
nextPatch: "Expose existing metrics in receipts before adding tracing code.",
},
{
flueConcept: "channels",
fableSurface: "none explicit",
status: "gap",
nextPatch: "Add a tiny channel manifest: stdin/stdout/files/http/webhook capabilities per executor.",
},
];
export function summarizeFlueInterop(rows: FlueInteropRow[] = FLUE_INTEROP_ROWS): { covered: number; partial: number; gap: number; next: FlueInteropRow } {
const counts = rows.reduce(
(acc, row) => ({ ...acc, [row.status]: acc[row.status] + 1 }),
{ covered: 0, partial: 0, gap: 0 },
);
return { ...counts, next: rows.find((row) => row.status === "gap") ?? rows.find((row) => row.status === "partial") ?? rows[0] };
}
export function formatFlueInteropReport(rows: FlueInteropRow[] = FLUE_INTEROP_ROWS): string {
const summary = summarizeFlueInterop(rows);
const lines = rows.map((row) =>
` ${row.status.toUpperCase().padEnd(7)} ${row.flueConcept.padEnd(18)}${row.fableSurface}\n next: ${row.nextPatch}`,
);
return [
"",
" Flue inspiration map (no vendored Flue code)",
" ─────────────────────────────",
` covered=${summary.covered} partial=${summary.partial} gap=${summary.gap}`,
"",
...lines,
"",
` next implementable patch: ${summary.next.flueConcept}${summary.next.nextPatch}`,
"",
].join("\n");
}

View File

@ -47,5 +47,8 @@ export type { ZteReceipt, ZteSpec, ZteSpecOptions } from "./zte-protocol.js";
export { formatCapabilityReport, loadFactoryCapabilities, parseFactoryCapabilities, probeFactoryCapabilities } from "./factory-capabilities.js"; export { formatCapabilityReport, loadFactoryCapabilities, parseFactoryCapabilities, probeFactoryCapabilities } from "./factory-capabilities.js";
export type { CapabilityProbeResult, CapabilityReport, FactoryCapabilityService } from "./factory-capabilities.js"; export type { CapabilityProbeResult, CapabilityReport, FactoryCapabilityService } from "./factory-capabilities.js";
export { FLUE_INTEROP_ROWS, formatFlueInteropReport, summarizeFlueInterop } from "./flue-interop.js";
export type { FlueInteropRow, FlueInteropStatus } from "./flue-interop.js";
export { createForgejoIntakeReceipt, intakeForgejoItem, parseForgejoRef, routeForgejoLabels } from "./forgejo-intake.js"; export { createForgejoIntakeReceipt, intakeForgejoItem, parseForgejoRef, routeForgejoLabels } from "./forgejo-intake.js";
export type { ForgejoIntakeKind, ForgejoIntakeOptions, ForgejoIntakeReceipt, ForgejoIntakeSource, ForgejoItem, ForgejoRoute } from "./forgejo-intake.js"; export type { ForgejoIntakeKind, ForgejoIntakeOptions, ForgejoIntakeReceipt, ForgejoIntakeSource, ForgejoItem, ForgejoRoute } from "./forgejo-intake.js";

View File

@ -1579,6 +1579,14 @@ fable
console.log(` `); console.log(` `);
}); });
fable
.command("flue")
.description("Show Flue-inspired interop gaps without vendoring Flue")
.action(async () => {
const { formatFlueInteropReport } = await import("./fable5/flue-interop.js");
console.log(formatFlueInteropReport());
});
fable fable
.command("verify <task>") .command("verify <task>")
.description("Run independent verifier against a task") .description("Run independent verifier against a task")