SFML is an open standard for a domain specific language to describe how agents and humans interact. SFML formalizes how you, your team or your company build software and enables some people to focus on improving the team prompting process while allowing others to focus on understanding requirements and drive change through the system. It does this be defining how SFML files are created and how SFML conformant implementations processes those files.
SFML should do for your agentic coding processes what Terraform did for your cloud deployment processes. By turning it into code it becomes repeatable, sharable and improvable. Rather than each engineer doing things their own way and thus needed to constantly learn how best to use AI. SFML lets people focus on what it is they drive the most value at in the company. The agentic optimizers can know everyone is building the same way and focus on improving the company software factory and the product builders can focus on making a great product.
SFML is currently at v0.1 and is looking for collaborators to help improve on the idea.
SFML ships with an example implementation you can use to lint and run a factory. Clone the repository, then install and build the CLI:
git clone https://github.com/craftmilldev/sfml.git
cd sfml
npm install --prefix example
npm run build --prefix example
From the repository root, lint the included factory, then run it with a GitHub ticket URL. Replace the sample URL with the issue or PR you want to process:
node example/dist/cli/sfml.js lint .sfml/factory.sfml
node example/dist/cli/sfml.js run .sfml/factory.sfml \
--param ticket_url=<your_github_ticket>
lint checks the factory without running it. run uses the Claude Agent SDK and saves progress to the state file, so you can resume if the factory pauses. See the example runner CLI guide for the resume command and other options.
The CLI is a reference implementation, not a polished developer tool. You can build your own SFML Runner using the conformance tests in this repository. If you do, we welcome a PR linking to it.
SFML is a graph of steps. Each step represents a human or agent action and the result of each step routes the next step. Graphs can loop to allow for feedback processes and budgets can be set to ensure a loop doesn't get out of hand.
Use the tabs below to view the SFML code or a Mermaid diagram of the graph.
sfml: "v0.1"
description: >
Takes a GitHub ticket (issue, PR, or a comment on either) from analysis through
implementation, review, and human-approved merge.
start: analyze
budget: 100.00
parameters:
ticket_url:
type: string
description: The GitHub issue, PR, or comment URL this run is working from.
steps:
analyze:
type: agent
description: Decides whether the ticket is ready to plan, or needs a human answer first.
harness: claude-agent-sdk
harness_config: { cwd: ".", permission_mode: "bypassPermissions" }
prompt_path: prompts/analyze.md
prompt_vars:
ticket_url: parameters.ticket_url
notes: results.human_refine
max_iterations: 8
result_schema:
type: object
required: [ready]
properties:
ready: { type: boolean }
plan: { type: string }
branch: { type: string }
next:
- when: "!last(results.analyze).ready"
to: human_refine
- to: implement
human_refine:
type: human
description: Answers Analyze's open questions (posted on the ticket), then sends it back to try again.
assignee: "on-call-reviewer"
instructions: "'Analyze has open questions on the ticket. Answer them there, then record your decision.'"
max_iterations: 10
result_schema:
type: object
required: [decision]
properties:
decision:
type: string
enum: [retry, abandon]
notes:
type: array
items: { type: string }
next:
- when: "last(results.human_refine).decision == 'abandon'"
to: abandoned
- to: analyze
implement:
type: agent
description: Builds the approved plan (or addresses the latest review feedback) on the branch.
harness: claude-agent-sdk
harness_config: { cwd: ".", permission_mode: "bypassPermissions" }
prompt_path: prompts/implement.md
prompt_vars:
ticket_url: parameters.ticket_url
branch: last(results.analyze).branch
plan_path: last(results.analyze).plan
review_comments: last(results.ai_review).comments
max_iterations: 20
result_schema:
type: object
required: [ok]
properties:
ok: { type: boolean }
next:
- to: ai_review
ai_review:
type: agent
description: Reviews the current diff on the branch; loops back to Implement if it has feedback.
harness: claude-agent-sdk
harness_config: { cwd: ".", permission_mode: "bypassPermissions" }
prompt_path: prompts/ai_review.md
prompt_vars:
ticket_url: parameters.ticket_url
branch: last(results.analyze).branch
pr_url: last(results.create_update_pr).url
max_iterations: 20
result_schema:
type: object
required: [comments]
properties:
comments:
type: array
items: { type: string }
next:
- when: "notEmpty(last(results.ai_review).comments)"
to: implement
- to: create_update_pr
create_update_pr:
type: agent
description: Opens the pull request, or pushes the latest commits to the one already open.
harness: claude-agent-sdk
harness_config: { cwd: ".", permission_mode: "bypassPermissions" }
prompt_path: prompts/create_update_pr.md
prompt_vars:
ticket_url: parameters.ticket_url
branch: last(results.analyze).branch
plan_path: last(results.analyze).plan
pr_url: last(results.create_update_pr).url
max_iterations: 10
result_schema:
type: object
required: [ok]
properties:
ok: { type: boolean }
url: { type: string }
head_sha: { type: string }
next:
- to: human_review
human_review:
type: human
description: Reviews the open pull request, then decides whether it needs more work or is ready to merge.
assignee: "on-call-reviewer"
instructions: "'A pull request is open for review. Review it on GitHub, then record your decision.'"
max_iterations: 30
result_schema:
type: object
required: [decision]
properties:
decision:
type: string
enum: [request_changes, approve, abandon]
notes: { type: string }
next:
- when: "last(results.human_review).decision == 'abandon'"
to: abandoned
- when: "last(results.human_review).decision == 'request_changes'"
to: implement
- to: merge
merge:
type: agent
description: Merges the human-approved pull request.
harness: claude-agent-sdk
harness_config: { cwd: ".", permission_mode: "bypassPermissions" }
prompt_path: prompts/merge.md
prompt_vars:
ticket_url: parameters.ticket_url
pr_url: last(results.create_update_pr).url
budget: 5.00
result_schema:
type: object
required: [ok]
properties:
ok: { type: boolean }
merged_sha: { type: string }
next:
- to: success
success:
type: result
outcome: complete
value: last(results.merge)
abandoned:
type: result
outcome: terminal_failure
flowchart TD
__start__(("start"))
__start__ --> analyze
analyze["analyze<br/>agent: claude-agent-sdk"]
human_refine[/"human_refine<br/>human: on-call-reviewer"/]
implement["implement<br/>agent: claude-agent-sdk"]
ai_review["ai_review<br/>agent: claude-agent-sdk"]
create_update_pr["create_update_pr<br/>agent: claude-agent-sdk"]
human_review[/"human_review<br/>human: on-call-reviewer"/]
merge["merge<br/>agent: claude-agent-sdk"]
success(["success<br/>result: complete"])
abandoned(["abandoned<br/>result: terminal_failure"])
analyze -->|"!last(results.analyze).ready"| human_refine
analyze --> implement
human_refine -->|"last(results.human_refine).decision == 'abandon'"| abandoned
human_refine --> analyze
implement --> ai_review
ai_review -->|"notEmpty(last(results.ai_review).comments)"| implement
ai_review --> create_update_pr
create_update_pr --> human_review
human_review -->|"last(results.human_review).decision == 'abandon'"| abandoned
human_review -->|"last(results.human_review).decision == 'request_changes'"| implement
human_review --> merge
merge --> success
classDef agentStep fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a;
classDef humanStep fill:#fef3c7,stroke:#b45309,color:#78350f;
classDef parallelStep fill:#ede9fe,stroke:#6d28d9,color:#4c1d95;
classDef resultStep fill:#dcfce7,stroke:#15803d,color:#14532d;
class analyze agentStep;
class human_refine humanStep;
class implement agentStep;
class ai_review agentStep;
class create_update_pr agentStep;
class human_review humanStep;
class merge agentStep;
class success resultStep;
class abandoned resultStep;