SFML
What is SFML

A DSL for how Agents and Humans Interact

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.

Check out the specification and provide feedback.

How to use it

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.

A small example

What a factory actually looks like

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