Use Cases
Ten Fabric Harness agents you can build today, across the software development lifecycle — issue triage, PR review, changelog drafting, test generation, dependency audit, migrations, API docs, bug repro, release notes, and incident runbooks.
Fabric Harness is at its best on agents that do real work — touch files, run shell commands, call APIs, optionally pause for human approval, and produce structured artifacts. This page shows ten concrete SDLC agents you can build today, each in the style of Flue's README examples but using Fabric Harness primitives (skills, capability policy, scoped commands, filesystem sources, runtime modes).
Every example uses imports from the public npm packages — the same ones a new project gets from npm install @fabric-harness/sdk. None of these examples require building Fabric Harness from source.
Quick map: Flue ↔ Fabric Harness
| Flue example | Closest Fabric Harness agent | What Fabric Harness adds |
|---|---|---|
| Quickstart / translation | #1 Issue Triage, #2 PR Reviewer | Typed schemas, capability policy, artifacts |
| Support Agent (R2 KB) | #10 Incident Runbook | withFilesystemSources + R2 source helper |
| Issue Triage (CI) | #1 Issue Triage | Allow/deny + approval gating; durable runtime option |
| Coding Agent (Daytona) | #4 Test Generator, #8 Bug Reproducer | Same remote sandbox shape; pluggable backends |
| Remote MCP Tools | #3 Changelog Writer, #5 Dependency Auditor | MCP + scoped CLI commands side by side |
The structural takeaway is the same in every row: Flue keeps the surface intentionally tiny; Fabric Harness keeps the same primitives and adds typed I/O, capability policy, artifacts, approvals, and a multi-runtime story for cases where production demands them.
1. Issue Triage
Read a GitHub issue, classify severity, suggest labels, draft a reply, and persist a triage report as an artifact. Read-only by default — comments and label changes are gated behind approval.
// .fabricharness/agents/triage.ts
import { agent, defineCommand, schema } from '@fabric-harness/sdk';
import type { CapabilityPolicy } from '@fabric-harness/sdk';
const gh = defineCommand('gh', { env: { GH_TOKEN: process.env.GH_TOKEN } });
const policy: CapabilityPolicy = {
commandPolicy: {
allow: ['git status*', 'git log*', 'git diff*', 'gh issue view*'],
deny: ['git push*', 'gh issue close*', 'gh pr merge*'],
requireApproval: ['gh issue comment*', 'gh issue edit*'],
},
maxCommandTimeoutMs: 30_000,
approvals: { requiredApprovals: 1, defaultTimeoutMs: 60_000, risk: 'medium' },
};
export default agent({
name: 'triage',
input: schema.object({
issueNumber: schema.number(),
title: schema.string(),
body: schema.string(),
repository: schema.string().describe('owner/repo'),
}),
output: schema.object({
severity: schema.enum(['low', 'medium', 'high', 'critical']),
suggestedLabels: schema.array(schema.string()),
suggestedComment: schema.string(),
reportArtifact: schema.string(),
}),
model: process.env.FABRIC_MODEL,
triggers: { webhook: true },
run: async ({ init, input }) => {
const session = await (await init({ policy, sandbox: 'local' })).session();
const result = await session.skill('triage-issue', {
args: input,
commands: [gh],
result: schema.object({
severity: schema.enum(['low', 'medium', 'high', 'critical']),
suggestedLabels: schema.array(schema.string()),
suggestedComment: schema.string(),
}),
});
const ref = await session.artifact(
'triage-report.md',
`# ${input.repository}#${input.issueNumber}\n\nSeverity: **${result.severity}**\n\n${result.suggestedComment}`,
{ contentType: 'text/markdown' },
);
return { ...result, reportArtifact: ref.path };
},
});Flue parity. Flue's Issue Triage example covers the same use case in ~40 lines. Fabric Harness adds typed input/output validation and the requireApproval policy step so an over-eager agent can't post a public comment without a human approver.
Run it:
GH_TOKEN=ghp_... FABRIC_MODEL=anthropic/claude-sonnet-4-6 \
fh run triage --payload '{"issueNumber":42,"title":"crash on save","body":"...","repository":"acme/web"}'2. PR Reviewer
Review a pull request diff against a security / performance / readability lens and produce structured findings. Works in CI — a status check stays green until the agent posts.
// .fabricharness/agents/pr-review.ts
import { agent, defineCommand, schema } from '@fabric-harness/sdk';
const gh = defineCommand('gh', { env: { GH_TOKEN: process.env.GH_TOKEN } });
const git = defineCommand('git');
const findingSchema = schema.object({
file: schema.string(),
line: schema.number().optional(),
severity: schema.enum(['info', 'low', 'medium', 'high', 'critical']),
category: schema.enum(['bug', 'security', 'performance', 'tests', 'readability', 'style']),
message: schema.string(),
suggestion: schema.string().optional(),
});
const reviewSchema = schema.object({
overallRisk: schema.enum(['low', 'medium', 'high']),
approvedToMerge: schema.boolean(),
findings: schema.array(findingSchema),
});
export default agent({
name: 'pr-review',
input: schema.object({
repository: schema.string(),
prNumber: schema.number(),
focus: schema.enum(['security', 'performance', 'tests', 'readability']).default('security'),
}),
output: reviewSchema,
model: process.env.FABRIC_MODEL,
triggers: { webhook: true, manual: true },
run: async ({ init, input }) => {
const session = await (await init({ sandbox: 'local' })).session();
return await session.skill('review-pr', {
args: input,
commands: [gh, git],
result: reviewSchema,
});
},
});A markdown skill at .fabricharness/skills/review-pr.md describes the procedure (gh pr diff, git show, cat the changed files, then return the typed findings). The PR diff and changed-file content stay in-memory in the agent's session — no copy is made.
Flue parity. Flue doesn't ship a PR review example, but the skill + scoped-command pattern matches Flue's Issue Triage exactly.
3. Changelog Writer
Turn merged PRs since the last tag into a user-facing changelog entry, grouped by category, with breaking-change callouts.
// .fabricharness/agents/changelog.ts
import { agent, defineCommand, schema } from '@fabric-harness/sdk';
const gh = defineCommand('gh', { env: { GH_TOKEN: process.env.GH_TOKEN } });
const changelogSchema = schema.object({
version: schema.string(),
summary: schema.string(),
sections: schema.array(schema.object({
title: schema.enum(['Breaking', 'Features', 'Fixes', 'Performance', 'Docs', 'Internal']),
entries: schema.array(schema.object({
title: schema.string(),
prNumber: schema.number(),
author: schema.string(),
})),
})),
markdown: schema.string(),
});
export default agent({
name: 'changelog',
input: schema.object({
repository: schema.string(),
fromRef: schema.string().describe('git ref or tag'),
toRef: schema.string().default('HEAD'),
}),
output: changelogSchema,
model: process.env.FABRIC_MODEL,
run: async ({ init, input }) => {
const session = await (await init({ sandbox: 'local' })).session();
return await session.skill('write-changelog', {
args: input,
commands: [gh],
result: changelogSchema,
});
},
});The skill instructs the model to run gh pr list --search 'merged:>= ...', classify each PR by conventional-commit prefix or label, and emit user-facing prose (not commit messages).
Run on a release tag:
fh run changelog --payload '{"repository":"acme/web","fromRef":"v1.4.0","toRef":"HEAD"}'4. Test Generator
Given a source file path, generate Vitest/Jest unit tests for every exported function. Runs the new tests in a Docker sandbox to verify they pass before returning.
// .fabricharness/agents/gen-tests.ts
import { agent, schema } from '@fabric-harness/sdk';
const testGenSchema = schema.object({
testPath: schema.string(),
testsAdded: schema.number(),
passing: schema.boolean(),
diff: schema.string(),
});
export default agent({
name: 'gen-tests',
input: schema.object({
filePath: schema.string(),
framework: schema.enum(['vitest', 'jest']).default('vitest'),
coverage: schema.enum(['happy-path', 'edge-cases', 'exhaustive']).default('edge-cases'),
}),
output: testGenSchema,
model: process.env.FABRIC_MODEL,
run: async ({ init, input }) => {
// Docker sandbox so the generated tests run isolated from the host.
const session = await (await init({ sandbox: 'docker' })).session();
return await session.skill('generate-tests', {
args: input,
result: testGenSchema,
});
},
});The skill: read the source file, propose tests for each export, write them to a sibling *.test.ts, then run pnpm test --filter <file> inside the sandbox and iterate until tests pass or the iteration budget is exhausted.
Flue parity. Flue's Coding Agent (Daytona) shows the same shape — a remote sandbox, a clone-and-run flow. Fabric Harness uses Docker by default but accepts the same daytona(sandbox) factory if you install @daytona/sdk and the connector recipe.
5. Dependency Auditor
Read package-lock.json / pnpm-lock.yaml and produce a CVE + license report. Runs daily in CI; optional fail-the-build mode.
// .fabricharness/agents/dep-audit.ts
import { agent, connectMcpServer, schema } from '@fabric-harness/sdk';
const auditSchema = schema.object({
totalDeps: schema.number(),
vulnerabilities: schema.array(schema.object({
package: schema.string(),
version: schema.string(),
severity: schema.enum(['low', 'moderate', 'high', 'critical']),
cve: schema.string().optional(),
summary: schema.string(),
fixedIn: schema.string().optional(),
})),
licenseFlags: schema.array(schema.object({
package: schema.string(),
license: schema.string(),
reason: schema.string(),
})),
failed: schema.boolean(),
});
export default agent({
name: 'dep-audit',
input: schema.object({
lockfilePath: schema.string().default('pnpm-lock.yaml'),
failOn: schema.enum(['low', 'moderate', 'high', 'critical', 'never']).default('high'),
}),
output: auditSchema,
model: process.env.FABRIC_MODEL,
triggers: { schedule: '0 6 * * *' }, // daily 06:00 UTC
run: async ({ init, input }) => {
// Optional: connect an OSV / Snyk MCP server for live CVE lookups.
const osv = await connectMcpServer('osv', { url: 'https://api.osv.dev/mcp' });
try {
const session = await (await init({ tools: osv.tools, sandbox: 'local' })).session();
return await session.skill('audit-dependencies', {
args: input,
result: auditSchema,
});
} finally {
await osv.close();
}
},
});Flue parity. Flue's Remote MCP Tools example shows the same connectMcpServer + close-in-finally pattern; Fabric Harness adds the typed result and the triggers.schedule cron string.
6. Schema Migration Author
Compare two versions of a schema (prisma/schema.prisma, drizzle/schema.ts, raw SQL) and emit a forward + rollback migration file pair.
// .fabricharness/agents/migration.ts
import { agent, defineCommand, schema } from '@fabric-harness/sdk';
import type { CapabilityPolicy } from '@fabric-harness/sdk';
const psql = defineCommand('psql');
const policy: CapabilityPolicy = {
commandPolicy: {
allow: ['git diff*', 'git show*', 'cat*', 'psql --command "EXPLAIN*'],
deny: ['psql --command "DROP*', 'psql --command "TRUNCATE*'],
requireApproval: ['psql --command "CREATE*', 'psql --command "ALTER*'],
},
};
const migrationSchema = schema.object({
migrationName: schema.string(),
forwardSql: schema.string(),
rollbackSql: schema.string(),
notes: schema.array(schema.string()),
requiresDowntime: schema.boolean(),
});
export default agent({
name: 'migration',
input: schema.object({
fromRef: schema.string(),
toRef: schema.string().default('HEAD'),
schemaPath: schema.string().default('prisma/schema.prisma'),
dialect: schema.enum(['postgres', 'mysql', 'sqlite']).default('postgres'),
}),
output: migrationSchema,
model: process.env.FABRIC_MODEL,
run: async ({ init, input }) => {
const session = await (await init({ policy, sandbox: 'local' })).session();
const out = await session.skill('write-migration', {
args: input,
commands: [psql],
result: migrationSchema,
});
const stamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
await session.artifact(`migrations/${stamp}_${out.migrationName}.up.sql`, out.forwardSql);
await session.artifact(`migrations/${stamp}_${out.migrationName}.down.sql`, out.rollbackSql);
return out;
},
});Why approval matters here. CREATE/ALTER are gated; the agent can validate via EXPLAIN against a shadow database without touching production. Approvals are first-class — a reviewer's GitHub identity is captured on the approval entry.
7. API Docs Generator
Generate OpenAPI 3.1 + Markdown reference from your route handlers. Supports Express, Fastify, Hono, Next route handlers, NestJS controllers.
// .fabricharness/agents/api-docs.ts
import { agent, schema } from '@fabric-harness/sdk';
const apiDocsSchema = schema.object({
openapi: schema.string(),
markdown: schema.string(),
routesDocumented: schema.number(),
});
export default agent({
name: 'api-docs',
input: schema.object({
rootDir: schema.string().default('src'),
framework: schema.enum(['express', 'fastify', 'hono', 'next', 'nest']).default('hono'),
title: schema.string().default('API'),
version: schema.string().default('1.0.0'),
}),
output: schema.object({
openapiArtifact: schema.string(),
markdownArtifact: schema.string(),
routesDocumented: schema.number(),
}),
model: process.env.FABRIC_MODEL,
run: async ({ init, input }) => {
const session = await (await init({ sandbox: 'local' })).session();
const out = await session.skill('generate-api-docs', { args: input, result: apiDocsSchema });
const oa = await session.artifact('openapi.json', out.openapi, { contentType: 'application/json' });
const md = await session.artifact('api.md', out.markdown, { contentType: 'text/markdown' });
return {
openapiArtifact: oa.path,
markdownArtifact: md.path,
routesDocumented: out.routesDocumented,
};
},
});The skill walks the route files, infers handler signatures, validates response shapes against any present Zod/Valibot schemas, and assembles a consistent OpenAPI spec.
8. Bug Reproducer
Take a free-form bug report, identify the failing path in the codebase, and write a failing test case that reproduces it. Useful as a CI check on issue creation.
// .fabricharness/agents/bug-repro.ts
import { agent, schema } from '@fabric-harness/sdk';
const reproSchema = schema.object({
hypothesis: schema.string(),
suspectFiles: schema.array(schema.string()),
testPath: schema.string(),
testFails: schema.boolean(),
failureSnippet: schema.string().optional(),
});
export default agent({
name: 'bug-repro',
input: schema.object({
title: schema.string(),
body: schema.string(),
expected: schema.string().optional(),
actual: schema.string().optional(),
}),
output: reproSchema,
model: process.env.FABRIC_MODEL,
run: async ({ init, input }) => {
// Docker so the failing test runs isolated and we can capture stderr.
const session = await (await init({ sandbox: 'docker' })).session();
return await session.skill('reproduce-bug', { args: input, result: reproSchema });
},
});Pair with the #1 Issue Triage agent: triage assigns severity, repro produces a failing test. By the time a human picks the issue up, half the work is done.
Flue parity. Flue's Coding Agent (Daytona) shows the remote-sandbox shape this agent uses, just with Daytona instead of Docker.
9. Release Notes Drafter
Combine git log, merged PRs, closed issues, and (optionally) a fumadocs / mkdocs site to produce marketing-tone release notes. Distinct from the changelog: this is the version of release notes you put on a blog or in a release email.
// .fabricharness/agents/release-notes.ts
import { agent, defineCommand, schema } from '@fabric-harness/sdk';
const git = defineCommand('git');
const gh = defineCommand('gh', { env: { GH_TOKEN: process.env.GH_TOKEN } });
const releaseSchema = schema.object({
title: schema.string(),
tagline: schema.string(),
headlineFeatures: schema.array(schema.object({
title: schema.string(),
summary: schema.string(),
prNumbers: schema.array(schema.number()),
})),
upgradeNotes: schema.array(schema.string()),
fullMarkdown: schema.string(),
});
export default agent({
name: 'release-notes',
input: schema.object({
repository: schema.string(),
fromTag: schema.string(),
toTag: schema.string(),
audience: schema.enum(['developers', 'users', 'both']).default('users'),
voice: schema.enum(['neutral', 'enthusiastic', 'formal']).default('neutral'),
}),
output: releaseSchema,
model: process.env.FABRIC_MODEL,
run: async ({ init, input }) => {
const session = await (await init({ role: 'tech-writer', sandbox: 'local' })).session();
const out = await session.skill('draft-release-notes', {
args: input,
commands: [git, gh],
result: releaseSchema,
});
await session.artifact(`release-${input.toTag}.md`, out.fullMarkdown);
return out;
},
});The tech-writer role at .fabricharness/roles/tech-writer.md carries the brand voice — "no marketing fluff, lead with the user benefit, never reuse the commit message verbatim."
10. Incident Runbook Assistant
During an incident, an on-caller pastes alert text or a PagerDuty paste into a chat. The agent searches mounted runbooks, suggests the most likely matching playbook, and walks through the first three remediation steps.
// .fabricharness/agents/runbook.ts
import {
defineAgent,
localDirectorySource,
withFilesystemSources,
} from '@fabric-harness/sdk/lite';
const sandbox = withFilesystemSources('empty', [{
mountAt: '/workspace/runbooks',
source: localDirectorySource('./runbooks', { include: (p) => p.endsWith('.md') }),
}]);
export default defineAgent(async ({ init, payload }) => {
const session = await (await init({
sandbox,
runtime: 'stateless',
role: 'oncaller',
})).session();
const alert = String((payload as { alert?: string })?.alert ?? '');
return await session.skill('match-runbook', { args: { alert } });
}, { name: 'runbook', triggers: { webhook: true } });The skill instructs the model to grep /workspace/runbooks/*.md, score matches against the alert text, return the top 3, and quote the first three steps from the highest-confidence match. The oncaller role keeps replies short and operational.
Flue parity. This is the closest cousin of Flue's Support Agent example. Same primitive — read-only filesystem source mounted into a virtual sandbox — applied to internal SRE/runbook content instead of customer support articles. For a Cloudflare R2 deployment, swap localDirectorySource(...) for r2FilesystemSource(env.RUNBOOKS_BUCKET) (see Filesystem sources).
How they compose
These ten aren't isolated. Real workflows combine them:
graph LR
A[GitHub webhook] --> B[#1 Triage]
B -->|severity high| C[#8 Bug Reproducer]
C --> D[Failing test in PR]
D --> E[#2 PR Reviewer]
E -->|approved| F[Merge]
F --> G[#3 Changelog]
G --> H[Release tag]
H --> I[#9 Release Notes]
H --> J[#5 Dep Audit]Adjacent stages share the same Fabric Harness primitives — one agent's output schema feeds the next agent's input schema, and the policy stays consistent across the pipeline.
Where it fits today
Beyond these ten, Fabric Harness suits any workload that needs:
- Read/write file work + shell — agents that touch source, run linters, generate diffs.
- Capability scoping — secrets bound to commands via
defineCommand, allow/deny lists, approval gates. - Typed boundaries — schemas validated on input and output of every
agent({...})call. - Durable execution —
runtime: 'temporal'for multi-day workflows that must survive crashes. - Headless / edge —
runtime: 'stateless'+@fabric-harness/sdk/litefor Cloudflare Workers, Vercel Edge. - Human-in-the-loop —
requireApprovalplus anonApprovalcallback or persistent store waiter.
Where it's still maturing
| Area | Status |
|---|---|
| Cloudflare R2 filesystem | R2 source helper ships in @fabric-harness/cloudflare; full backup/restore parity remains roadmap. |
| Foundry Hosted Agents | Build/scaffold target ships; live create/update/invoke is preview-only. |
| Azure Container Apps / ACI / AKS | First-class adapter rollout is in progress. |
| Databricks | Roadmap. |
See the full capability matrix for current per-feature, per-target maturity.
What Fabric Harness is not
- Not a chatbot UI. Headless framework. Bring your own UI if you need one.
- Not an LLM provider abstraction. It composes with provider SDKs (Anthropic, OpenAI, Azure OpenAI, Bedrock, Gemini, Vertex, Cohere, OpenAI-compatible) via the model registry.
- Not a hosted service. It's a framework you deploy to your runtime of choice.