{
  "items": [
    {
      "type": "entity",
      "name": "story",
      "definition": {
        "entityName": "story",
        "entityIdFieldName": "id",
        "requiredFields": [
          "id"
        ],
        "baseKeys": {
          "pkTemplate": "org#{org};data#story",
          "skTemplate": "story#{id}"
        },
        "parentIndex": {
          "enabled": false,
          "parents": []
        },
        "categoryIndex": {
          "enabled": false,
          "parents": []
        },
        "ownerIndexes": [],
        "lookups": [],
        "lookupIndexes": [],
        "UpdateParams": [
          {
            "name": "id",
            "inputName": "id",
            "colType": "string"
          },
          {
            "name": "name",
            "inputName": "name",
            "colType": "string"
          },
          {
            "name": "description",
            "inputName": "description",
            "colType": "string"
          },
          {
            "name": "acceptance_criteria",
            "inputName": "acceptance_criteria",
            "colType": "string"
          },
          {
            "name": "stage",
            "inputName": "stage",
            "colType": "string"
          },
          {
            "name": "repo",
            "inputName": "repo",
            "colType": "string"
          },
          {
            "name": "feature_branch",
            "inputName": "feature_branch",
            "colType": "string"
          },
          {
            "name": "target_branch",
            "inputName": "target_branch",
            "colType": "string"
          }
        ],
        "UniqueIndexes": []
      }
    },
    {
      "type": "entity",
      "name": "role_prompt",
      "definition": {
        "entityName": "role_prompt",
        "entityIdFieldName": "id",
        "requiredFields": [
          "id"
        ],
        "baseKeys": {
          "pkTemplate": "org#{org};data#role_prompt",
          "skTemplate": "role_prompt#{id}"
        },
        "parentIndex": {
          "enabled": false,
          "parents": []
        },
        "categoryIndex": {
          "enabled": false,
          "parents": []
        },
        "ownerIndexes": [],
        "lookups": [],
        "lookupIndexes": [],
        "UpdateParams": [
          {
            "name": "id",
            "inputName": "id",
            "colType": "string"
          },
          {
            "name": "name",
            "inputName": "name",
            "colType": "string"
          },
          {
            "name": "label",
            "inputName": "label",
            "colType": "string"
          },
          {
            "name": "template",
            "inputName": "template",
            "colType": "string"
          }
        ],
        "UniqueIndexes": []
      }
    },
    {
      "type": "entity",
      "name": "task",
      "definition": {
        "entityName": "task",
        "entityIdFieldName": "id",
        "requiredFields": [
          "id"
        ],
        "baseKeys": {
          "pkTemplate": "org#{org};data#task",
          "skTemplate": "task#{id}"
        },
        "parentIndex": {
          "enabled": false,
          "parents": []
        },
        "categoryIndex": {
          "enabled": false,
          "parents": []
        },
        "ownerIndexes": [],
        "lookups": [],
        "lookupIndexes": [],
        "UpdateParams": [
          {
            "name": "id",
            "inputName": "id",
            "colType": "string"
          },
          {
            "name": "name",
            "inputName": "name",
            "colType": "string"
          },
          {
            "name": "story_id",
            "inputName": "story_id",
            "colType": "string"
          },
          {
            "name": "role",
            "inputName": "role",
            "colType": "string"
          },
          {
            "name": "owner_id",
            "inputName": "owner_id",
            "colType": "string"
          },
          {
            "name": "status",
            "inputName": "status",
            "colType": "string"
          },
          {
            "name": "prompt",
            "inputName": "prompt",
            "colType": "string"
          },
          {
            "name": "repo",
            "inputName": "repo",
            "colType": "string"
          },
          {
            "name": "branch",
            "inputName": "branch",
            "colType": "string"
          },
          {
            "name": "pr_url",
            "inputName": "pr_url",
            "colType": "string"
          },
          {
            "name": "category",
            "inputName": "category",
            "colType": "string"
          },
          {
            "name": "priority",
            "inputName": "priority",
            "colType": "string"
          },
          {
            "name": "pr_number",
            "inputName": "pr_number",
            "colType": "number"
          },
          {
            "name": "pr_state",
            "inputName": "pr_state",
            "colType": "string"
          },
          {
            "name": "merged",
            "inputName": "merged",
            "colType": "boolean"
          },
          {
            "name": "review_state",
            "inputName": "review_state",
            "colType": "string"
          },
          {
            "name": "reviewed",
            "inputName": "reviewed",
            "colType": "string"
          },
          {
            "name": "result",
            "inputName": "result",
            "colType": "string"
          },
          {
            "name": "review_summary",
            "inputName": "review_summary",
            "colType": "string"
          },
          {
            "name": "base_branch",
            "inputName": "base_branch",
            "colType": "string"
          },
          {
            "name": "work_branch",
            "inputName": "work_branch",
            "colType": "string"
          },
          {
            "name": "coding_agent",
            "inputName": "coding_agent",
            "colType": "string"
          },
          {
            "name": "agent_task_id",
            "inputName": "agent_task_id",
            "colType": "string"
          },
          {
            "name": "agent_status",
            "inputName": "agent_status",
            "colType": "string"
          }
        ],
        "UniqueIndexes": []
      }
    },
    {
      "type": "logicblock",
      "name": "create-task",
      "definition": {
        "name": "create-task",
        "description": "Creates a task for one role of a story and drafts its prompt from the role's template plus the story. The owner edits the prompt, then calls submit-task.",
        "method": "POST",
        "enabled": true,
        "trigger": {
          "type": "http"
        },
        "inputSchema": {
          "fields": [
            {
              "name": "story_id",
              "source": "body",
              "required": true
            },
            {
              "name": "role",
              "source": "body",
              "required": true
            },
            {
              "name": "owner_id",
              "source": "body",
              "required": true
            },
            {
              "name": "coding_agent",
              "source": "body",
              "required": false
            }
          ]
        },
        "steps": [
          {
            "id": "story",
            "type": "get_entity",
            "outputAs": "story",
            "config": {
              "apiName": "story",
              "inputValue": "{{input.story_id}}",
              "required": true
            }
          },
          {
            "id": "base",
            "type": "formula",
            "outputAs": "base",
            "config": {
              "expressions": [
                {
                  "name": "branch",
                  "formula": "IF(ISBLANK(story.feature_branch), 'main', story.feature_branch)",
                  "type": "string"
                }
              ]
            }
          },
          {
            "id": "tpl",
            "type": "list_entities",
            "outputAs": "tpl",
            "config": {
              "apiName": "role_prompt",
              "limit": 1,
              "attrFilters": [
                {
                  "field": "name",
                  "op": "eq",
                  "value": "{{input.role}}"
                }
              ]
            }
          },
          {
            "id": "new_task",
            "type": "create_entity",
            "outputAs": "new_task",
            "config": {
              "apiName": "task",
              "fields": [
                {
                  "field": "name",
                  "value": "{{tpl.first.label}}: {{story.name}}"
                },
                {
                  "field": "story_id",
                  "value": "{{story.id}}"
                },
                {
                  "field": "role",
                  "value": "{{input.role}}"
                },
                {
                  "field": "owner_id",
                  "value": "{{input.owner_id}}"
                },
                {
                  "field": "status",
                  "value": "draft"
                },
                {
                  "field": "repo",
                  "value": "{{story.repo}}"
                },
                {
                  "field": "base_branch",
                  "value": "{{base.branch}}"
                },
                {
                  "field": "coding_agent",
                  "value": "{{input.coding_agent}}"
                }
              ]
            }
          },
          {
            "id": "draft",
            "type": "update_entity",
            "outputAs": "draft",
            "config": {
              "apiName": "task",
              "inputValue": "{{new_task.id}}",
              "fields": [
                {
                  "field": "work_branch",
                  "value": "{{new_task.id}}"
                },
                {
                  "field": "prompt",
                  "value": "{{tpl.first.template}}\n\n---\nTask id: {{new_task.id}}\nRepository: {{story.repo}}\nBase branch: {{base.branch}}\nWork branch: {{new_task.id}}\n\nStory: {{story.name}}\n{{story.description}}\n\nAcceptance criteria:\n{{story.acceptance_criteria}}"
                }
              ]
            }
          },
          {
            "id": "task",
            "type": "get_entity",
            "outputAs": "task",
            "config": {
              "apiName": "task",
              "inputValue": "{{new_task.id}}",
              "required": true
            }
          }
        ],
        "outputShape": [
          {
            "key": "task_id",
            "ref": "task.id"
          },
          {
            "key": "task",
            "ref": "task"
          }
        ],
        "onError": "halt"
      }
    },
    {
      "type": "logicblock",
      "name": "submit-task",
      "definition": {
        "name": "submit-task",
        "description": "The owner's gate: saves the (edited) prompt and submits the task, which starts the role's agent. Refused for anyone but the owner, and for a task that is not a draft.",
        "method": "POST",
        "enabled": true,
        "trigger": {
          "type": "http"
        },
        "inputSchema": {
          "fields": [
            {
              "name": "task_id",
              "source": "body",
              "required": true
            },
            {
              "name": "prompt",
              "source": "body",
              "required": false
            }
          ]
        },
        "steps": [
          {
            "id": "task",
            "type": "get_entity",
            "outputAs": "task",
            "config": {
              "apiName": "task",
              "inputValue": "{{input.task_id}}",
              "required": true
            }
          },
          {
            "id": "not_owner",
            "type": "condition",
            "outputAs": "not_owner",
            "config": {
              "field": "{{context.userId}}",
              "operator": "!=",
              "value": "{{task.owner_id}}"
            },
            "steps": [
              {
                "id": "not_owner_msg",
                "type": "set_variable",
                "outputAs": "refusal",
                "config": {
                  "name": "refusal",
                  "value": "Only the owner of this task can submit it."
                }
              },
              {
                "id": "stop_owner",
                "type": "end",
                "outputAs": "stop_owner",
                "config": {
                  "output": "{{refusal}}"
                }
              }
            ],
            "then": [
              "not_owner_msg",
              "stop_owner"
            ],
            "else": []
          },
          {
            "id": "not_draft",
            "type": "condition",
            "outputAs": "not_draft",
            "config": {
              "field": "{{task.status}}",
              "operator": "!=",
              "value": "draft"
            },
            "steps": [
              {
                "id": "not_draft_msg",
                "type": "set_variable",
                "outputAs": "refusal",
                "config": {
                  "name": "refusal",
                  "value": "This task was already submitted."
                }
              },
              {
                "id": "stop_draft",
                "type": "end",
                "outputAs": "stop_draft",
                "config": {
                  "output": "{{refusal}}"
                }
              }
            ],
            "then": [
              "not_draft_msg",
              "stop_draft"
            ],
            "else": []
          },
          {
            "id": "has_prompt",
            "type": "condition",
            "outputAs": "has_prompt",
            "config": {
              "field": "{{input.prompt}}",
              "operator": "!=",
              "value": ""
            },
            "steps": [
              {
                "id": "save_edited",
                "type": "update_entity",
                "outputAs": "save_edited",
                "config": {
                  "apiName": "task",
                  "inputValue": "{{task.id}}",
                  "fields": [
                    {
                      "field": "prompt",
                      "value": "{{input.prompt}}"
                    },
                    {
                      "field": "status",
                      "value": "submitted"
                    }
                  ]
                }
              },
              {
                "id": "save_as_is",
                "type": "update_entity",
                "outputAs": "save_as_is",
                "config": {
                  "apiName": "task",
                  "inputValue": "{{task.id}}",
                  "fields": [
                    {
                      "field": "status",
                      "value": "submitted"
                    }
                  ]
                }
              }
            ],
            "then": [
              "save_edited"
            ],
            "else": [
              "save_as_is"
            ]
          },
          {
            "id": "submitted",
            "type": "get_entity",
            "outputAs": "submitted",
            "config": {
              "apiName": "task",
              "inputValue": "{{task.id}}",
              "required": true
            }
          },
          {
            "id": "use_agent",
            "type": "condition",
            "outputAs": "use_agent",
            "config": {
              "field": "{{submitted.coding_agent}}",
              "operator": "!=",
              "value": ""
            },
            "steps": [
              {
                "id": "start_agent",
                "type": "connector",
                "outputAs": "start_agent",
                "config": {
                  "connectorId": "{{submitted.coding_agent}}",
                  "actionId": "start_task",
                  "fieldMappings": [
                    {
                      "field": "repo",
                      "value": "{{submitted.repo}}"
                    },
                    {
                      "field": "base_branch",
                      "value": "{{submitted.base_branch}}"
                    },
                    {
                      "field": "work_branch",
                      "value": "{{submitted.work_branch}}"
                    },
                    {
                      "field": "prompt",
                      "value": "{{submitted.prompt}}"
                    }
                  ]
                }
              },
              {
                "id": "agent_started",
                "type": "update_entity",
                "outputAs": "agent_started",
                "config": {
                  "apiName": "task",
                  "inputValue": "{{submitted.id}}",
                  "fields": [
                    {
                      "field": "status",
                      "value": "running"
                    },
                    {
                      "field": "agent_task_id",
                      "value": "{{start_agent.task_id}}"
                    },
                    {
                      "field": "agent_status",
                      "value": "started"
                    }
                  ]
                }
              }
            ],
            "then": [
              "start_agent",
              "agent_started"
            ],
            "else": []
          },
          {
            "id": "final",
            "type": "get_entity",
            "outputAs": "final",
            "config": {
              "apiName": "task",
              "inputValue": "{{task.id}}",
              "required": true
            }
          }
        ],
        "outputShape": [
          {
            "key": "task_id",
            "ref": "final.id"
          },
          {
            "key": "status",
            "ref": "final.status"
          },
          {
            "key": "task",
            "ref": "final"
          }
        ],
        "onError": "halt"
      }
    },
    {
      "type": "logicblock",
      "name": "assign-task",
      "definition": {
        "name": "assign-task",
        "description": "The manager assigns a draft task to its owner and sets its category and priority. Only while the task is a draft; the owner then edits the prompt and submits.",
        "method": "POST",
        "enabled": true,
        "trigger": {
          "type": "http"
        },
        "inputSchema": {
          "fields": [
            {
              "name": "task_id",
              "source": "body",
              "required": true
            },
            {
              "name": "owner_id",
              "source": "body",
              "required": false
            },
            {
              "name": "category",
              "source": "body",
              "required": false
            },
            {
              "name": "priority",
              "source": "body",
              "required": false
            }
          ]
        },
        "steps": [
          {
            "id": "task",
            "type": "get_entity",
            "outputAs": "task",
            "config": {
              "apiName": "task",
              "inputValue": "{{input.task_id}}",
              "required": true
            }
          },
          {
            "id": "not_draft",
            "type": "condition",
            "outputAs": "not_draft",
            "config": {
              "field": "{{task.status}}",
              "operator": "!=",
              "value": "draft"
            },
            "steps": [
              {
                "id": "not_draft_msg",
                "type": "set_variable",
                "outputAs": "refusal",
                "config": {
                  "name": "refusal",
                  "value": "Only a draft task can be assigned."
                }
              },
              {
                "id": "stop_draft",
                "type": "end",
                "outputAs": "stop_draft",
                "config": {
                  "output": "{{refusal}}"
                }
              }
            ],
            "then": [
              "not_draft_msg",
              "stop_draft"
            ],
            "else": []
          },
          {
            "id": "keep",
            "type": "formula",
            "outputAs": "keep",
            "config": {
              "expressions": [
                {
                  "name": "owner_id",
                  "formula": "IF(ISBLANK(input.owner_id), IF(ISBLANK(task.owner_id), '', task.owner_id), input.owner_id)",
                  "type": "string"
                },
                {
                  "name": "category",
                  "formula": "IF(ISBLANK(input.category), IF(ISBLANK(task.category), '', task.category), input.category)",
                  "type": "string"
                },
                {
                  "name": "priority",
                  "formula": "IF(ISBLANK(input.priority), IF(ISBLANK(task.priority), '', task.priority), input.priority)",
                  "type": "string"
                }
              ]
            }
          },
          {
            "id": "save",
            "type": "update_entity",
            "outputAs": "save",
            "config": {
              "apiName": "task",
              "inputValue": "{{task.id}}",
              "fields": [
                {
                  "field": "owner_id",
                  "value": "{{keep.owner_id}}"
                },
                {
                  "field": "category",
                  "value": "{{keep.category}}"
                },
                {
                  "field": "priority",
                  "value": "{{keep.priority}}"
                }
              ]
            }
          },
          {
            "id": "assigned",
            "type": "get_entity",
            "outputAs": "assigned",
            "config": {
              "apiName": "task",
              "inputValue": "{{task.id}}",
              "required": true
            }
          }
        ],
        "outputShape": [
          {
            "key": "task_id",
            "ref": "assigned.id"
          },
          {
            "key": "task",
            "ref": "assigned"
          }
        ],
        "onError": "halt"
      }
    },
    {
      "type": "logicblock",
      "name": "merge-task",
      "definition": {
        "name": "merge-task",
        "description": "The owner merges the task's pull request, using their own GitHub account. Refused for anyone but the owner. The merge fires GitHub's webhook, which marks the task done.",
        "method": "POST",
        "enabled": true,
        "trigger": {
          "type": "http"
        },
        "inputSchema": {
          "fields": [
            {
              "name": "task_id",
              "source": "body",
              "required": true
            }
          ]
        },
        "steps": [
          {
            "id": "task",
            "type": "get_entity",
            "outputAs": "task",
            "config": {
              "apiName": "task",
              "inputValue": "{{input.task_id}}",
              "required": true
            }
          },
          {
            "id": "not_owner",
            "type": "condition",
            "outputAs": "not_owner",
            "config": {
              "field": "{{context.userId}}",
              "operator": "!=",
              "value": "{{task.owner_id}}"
            },
            "steps": [
              {
                "id": "not_owner_msg",
                "type": "set_variable",
                "outputAs": "refusal",
                "config": {
                  "name": "refusal",
                  "value": "Only the owner of this task can merge it."
                }
              },
              {
                "id": "stop_owner",
                "type": "end",
                "outputAs": "stop_owner",
                "config": {
                  "output": "{{refusal}}"
                }
              }
            ],
            "then": [
              "not_owner_msg",
              "stop_owner"
            ],
            "else": []
          },
          {
            "id": "no_pr",
            "type": "condition",
            "outputAs": "no_pr",
            "config": {
              "field": "{{task.pr_number}}",
              "operator": "=",
              "value": ""
            },
            "steps": [
              {
                "id": "no_pr_msg",
                "type": "set_variable",
                "outputAs": "refusal",
                "config": {
                  "name": "refusal",
                  "value": "This task has no open pull request yet."
                }
              },
              {
                "id": "stop_no_pr",
                "type": "end",
                "outputAs": "stop_no_pr",
                "config": {
                  "output": "{{refusal}}"
                }
              }
            ],
            "then": [
              "no_pr_msg",
              "stop_no_pr"
            ],
            "else": []
          },
          {
            "id": "merge",
            "type": "connector",
            "outputAs": "merge",
            "config": {
              "connectorId": "github:claims",
              "actionId": "merge_pull_request",
              "fieldMappings": [
                {
                  "field": "repo",
                  "value": "{{task.repo}}"
                },
                {
                  "field": "pull_number",
                  "value": "{{task.pr_number}}"
                }
              ]
            }
          }
        ],
        "outputShape": [
          {
            "key": "task_id",
            "ref": "task.id"
          },
          {
            "key": "merge",
            "ref": "merge"
          }
        ],
        "onError": "halt"
      }
    },
    {
      "type": "logicblock",
      "name": "sync-task",
      "definition": {
        "name": "sync-task",
        "description": "Reads the state of the task's coding-agent run (the connector's get_task), records it on the task, and records the pull request once the agent has opened one. Callable by the owner, or by a schedule.",
        "method": "POST",
        "enabled": true,
        "trigger": {
          "type": "http"
        },
        "inputSchema": {
          "fields": [
            {
              "name": "task_id",
              "source": "body",
              "required": true
            }
          ]
        },
        "steps": [
          {
            "id": "task",
            "type": "get_entity",
            "outputAs": "task",
            "config": {
              "apiName": "task",
              "inputValue": "{{input.task_id}}",
              "required": true
            }
          },
          {
            "id": "no_agent_run",
            "type": "condition",
            "outputAs": "no_agent_run",
            "config": {
              "field": "{{task.agent_task_id}}",
              "operator": "=",
              "value": ""
            },
            "steps": [
              {
                "id": "no_run_msg",
                "type": "set_variable",
                "outputAs": "refusal",
                "config": {
                  "name": "refusal",
                  "value": "This task has no coding-agent run to check."
                }
              },
              {
                "id": "stop_no_run",
                "type": "end",
                "outputAs": "stop_no_run",
                "config": {
                  "output": "{{refusal}}"
                }
              }
            ],
            "then": [
              "no_run_msg",
              "stop_no_run"
            ],
            "else": []
          },
          {
            "id": "poll",
            "type": "connector",
            "outputAs": "poll",
            "config": {
              "connectorId": "{{task.coding_agent}}",
              "actionId": "get_task",
              "fieldMappings": [
                {
                  "field": "repo",
                  "value": "{{task.repo}}"
                },
                {
                  "field": "task_id",
                  "value": "{{task.agent_task_id}}"
                }
              ]
            }
          },
          {
            "id": "state",
            "type": "formula",
            "outputAs": "state",
            "config": {
              "expressions": [
                {
                  "name": "value",
                  "formula": "IF(ISBLANK(poll.conclusion), poll.status, poll.conclusion)",
                  "type": "string"
                }
              ]
            }
          },
          {
            "id": "save",
            "type": "update_entity",
            "outputAs": "save",
            "config": {
              "apiName": "task",
              "inputValue": "{{task.id}}",
              "fields": [
                {
                  "field": "agent_status",
                  "value": "{{state.value}}"
                }
              ]
            }
          },
          {
            "id": "has_pr",
            "type": "condition",
            "outputAs": "has_pr",
            "config": {
              "field": "{{poll.pr_url}}",
              "operator": "!=",
              "value": ""
            },
            "steps": [
              {
                "id": "save_pr",
                "type": "update_entity",
                "outputAs": "save_pr",
                "config": {
                  "apiName": "task",
                  "inputValue": "{{task.id}}",
                  "fields": [
                    {
                      "field": "pr_url",
                      "value": "{{poll.pr_url}}"
                    },
                    {
                      "field": "branch",
                      "value": "{{poll.branch}}"
                    },
                    {
                      "field": "pr_state",
                      "value": "open"
                    }
                  ]
                }
              }
            ],
            "then": [
              "save_pr"
            ],
            "else": []
          }
        ],
        "outputShape": [
          {
            "key": "task_id",
            "ref": "task.id"
          },
          {
            "key": "agent_status",
            "ref": "state.value"
          },
          {
            "key": "pr_url",
            "ref": "poll.pr_url"
          }
        ],
        "onError": "halt"
      }
    },
    {
      "type": "event",
      "name": "start-sdlc-developer-on-submit",
      "definition": {
        "entity_name": "task",
        "event_name": "start-sdlc-developer-on-submit",
        "event_type": "agent_crew",
        "event_stage": "after",
        "event_optype": "update",
        "enabled": true,
        "entry_conditions": [
          {
            "field": "status",
            "operator": "=",
            "value": "submitted"
          },
          {
            "field": "role",
            "operator": "=",
            "value": "developer"
          },
          {
            "field": "coding_agent",
            "operator": "=",
            "value": ""
          }
        ],
        "destination_config": {
          "crew_slug": "sdlc-developer",
          "field_mapping": {
            "task_id": "source.id",
            "role": "source.role",
            "repo": "source.repo",
            "story_id": "source.story_id",
            "prompt": "source.prompt",
            "base_branch": "source.base_branch"
          }
        }
      }
    },
    {
      "type": "event",
      "name": "start-sdlc-product-owner-on-submit",
      "definition": {
        "entity_name": "task",
        "event_name": "start-sdlc-product-owner-on-submit",
        "event_type": "agent_crew",
        "event_stage": "after",
        "event_optype": "update",
        "enabled": true,
        "entry_conditions": [
          {
            "field": "status",
            "operator": "=",
            "value": "submitted"
          },
          {
            "field": "role",
            "operator": "=",
            "value": "product_owner"
          }
        ],
        "destination_config": {
          "crew_slug": "sdlc-product-owner",
          "field_mapping": {
            "task_id": "source.id",
            "role": "source.role",
            "repo": "source.repo",
            "story_id": "source.story_id",
            "prompt": "source.prompt",
            "base_branch": "source.base_branch"
          }
        }
      }
    },
    {
      "type": "event",
      "name": "start-sdlc-scrum-master-on-submit",
      "definition": {
        "entity_name": "task",
        "event_name": "start-sdlc-scrum-master-on-submit",
        "event_type": "agent_crew",
        "event_stage": "after",
        "event_optype": "update",
        "enabled": true,
        "entry_conditions": [
          {
            "field": "status",
            "operator": "=",
            "value": "submitted"
          },
          {
            "field": "role",
            "operator": "=",
            "value": "scrum_master"
          }
        ],
        "destination_config": {
          "crew_slug": "sdlc-scrum-master",
          "field_mapping": {
            "task_id": "source.id",
            "role": "source.role",
            "repo": "source.repo",
            "story_id": "source.story_id",
            "prompt": "source.prompt",
            "base_branch": "source.base_branch"
          }
        }
      }
    },
    {
      "type": "event",
      "name": "start-sdlc-reviewer-on-submit",
      "definition": {
        "entity_name": "task",
        "event_name": "start-sdlc-reviewer-on-submit",
        "event_type": "agent_crew",
        "event_stage": "after",
        "event_optype": "update",
        "enabled": true,
        "entry_conditions": [
          {
            "field": "status",
            "operator": "=",
            "value": "submitted"
          },
          {
            "field": "role",
            "operator": "=",
            "value": "reviewer"
          }
        ],
        "destination_config": {
          "crew_slug": "sdlc-reviewer",
          "field_mapping": {
            "task_id": "source.id",
            "role": "source.role",
            "repo": "source.repo",
            "story_id": "source.story_id",
            "prompt": "source.prompt",
            "base_branch": "source.base_branch"
          }
        }
      }
    },
    {
      "type": "event",
      "name": "start-sdlc-reviewer-when-in-review",
      "definition": {
        "entity_name": "task",
        "event_name": "start-sdlc-reviewer-when-in-review",
        "event_type": "agent_crew",
        "event_stage": "after",
        "event_optype": "update",
        "enabled": true,
        "entry_conditions": [
          {
            "field": "status",
            "operator": "=",
            "value": "in_review"
          },
          {
            "field": "reviewed",
            "operator": "!=",
            "value": "yes"
          },
          {
            "field": "role",
            "operator": "=",
            "value": "developer"
          }
        ],
        "destination_config": {
          "crew_slug": "sdlc-reviewer",
          "field_mapping": {
            "task_id": "source.id",
            "repo": "source.repo",
            "story_id": "source.story_id",
            "pr_url": "source.pr_url",
            "pr_number": "source.pr_number",
            "branch": "source.branch"
          }
        }
      }
    },
    {
      "type": "event",
      "name": "start-sdlc-reviewer-when-agent-pr-opens",
      "definition": {
        "entity_name": "task",
        "event_name": "start-sdlc-reviewer-when-agent-pr-opens",
        "event_type": "agent_crew",
        "event_stage": "after",
        "event_optype": "update",
        "enabled": true,
        "entry_conditions": [
          {
            "field": "pr_state",
            "operator": "=",
            "value": "open"
          },
          {
            "field": "coding_agent",
            "operator": "!=",
            "value": ""
          },
          {
            "field": "reviewed",
            "operator": "!=",
            "value": "yes"
          },
          {
            "field": "role",
            "operator": "=",
            "value": "developer"
          }
        ],
        "destination_config": {
          "crew_slug": "sdlc-reviewer",
          "field_mapping": {
            "task_id": "source.id",
            "repo": "source.repo",
            "story_id": "source.story_id",
            "pr_url": "source.pr_url",
            "pr_number": "source.pr_number",
            "branch": "source.work_branch"
          }
        }
      }
    },
    {
      "type": "event",
      "name": "task-done-when-pr-merged",
      "definition": {
        "entity_name": "task",
        "event_name": "task-done-when-pr-merged",
        "event_type": "app.entity",
        "event_stage": "after",
        "event_optype": "update",
        "enabled": true,
        "entry_conditions": [
          {
            "field": "pr_state",
            "operator": "=",
            "value": "closed"
          },
          {
            "field": "merged",
            "operator": "=",
            "value": true
          },
          {
            "field": "status",
            "operator": "!=",
            "value": "done"
          }
        ],
        "destination_config": {
          "target_entity": "task",
          "operation": "update",
          "target_id_source_field": "source.id",
          "field_mapping": {
            "status": "done"
          }
        }
      }
    },
    {
      "type": "agent",
      "name": "sdlc-developer",
      "definition": {
        "name": "sdlc-developer",
        "graph": {
          "nodes": [
            {
              "id": "a1",
              "kind": "agent",
              "data": {
                "name": "sdlc-developer",
                "role": "worker",
                "systemPrompt": "You are the developer agent of an SDLC board. You change code in one repository through GitHub tools, on a branch of your own, and open a pull request. A human reviews and merges it; you never merge.\n\nYour input is a task: task_id, role, repo and prompt. The prompt is the instruction the task's human owner approved: follow it exactly, it overrides anything else.\nKeep the task record up to date with the update-task tool (data.write on task, id = task_id): set status to \"running\" when you start, and at the end set status and put a short summary of what you did in result.\nStatus values: running while you work, in_review when you opened a pull request, done when the work needs no pull request, failed when you stopped. Never set a status other than these.\nDo not invent results. If a tool fails, report the failure in the task result and set status to failed.\n\nYou have six tool turns in total, and a turn may hold several independent tool calls, so batch them. Everything you do on GitHub is done as the person who started this run, with their own account. Follow this plan:\n\n- Turn 1, in one turn: read the task (read-task tool, filter id = task_id) AND set its status to \"running\" (update-task tool). If the task's status is not \"submitted\", stop and say so.\n\n- Turn 2, in one turn: read every file you need (the README, the source file(s) the prompt points to, and their test file) with the read-file tool, AND get the sha of the task's base_branch with the get-ref tool (branch = base_branch).\n\n- Turn 3: create the branch from that sha with the create-branch tool. The branch name is exactly the task_id, nothing before or after it (the tool adds the refs/heads/ part): GitHub reports the pull request's branch back, and that is how the task is found.\n\n- Turn 4: commit the source change with the commit tool (content is base64; pass the file's sha from the read-file result when replacing a file). Turn 5: commit the test change the same way. Commit one file per turn: commits to one branch must not run at the same time.\n\n- Turn 6, in one turn: open the pull request with the open-pull-request tool (branch = the task_id, base = the task's base_branch) AND update the task with the update-task tool: branch = the task_id, status \"in_review\", and a one-paragraph result. Then reply with a short summary of what you changed.\n\nDo not spend a turn on anything else. If the change needs more than this plan allows, stop after the branch exists, set the task to \"failed\" and say what remains.",
                "tools": [
                  {
                    "manifestKind": "data.write",
                    "enabled": true,
                    "requiresApproval": false,
                    "params": {
                      "entity": "task",
                      "operation": "update",
                      "idField": "id"
                    }
                  },
                  {
                    "manifestKind": "data.read",
                    "enabled": true,
                    "requiresApproval": false,
                    "params": {
                      "entity": "task",
                      "filters": [],
                      "limit": 50
                    }
                  },
                  {
                    "manifestKind": "connector.invoke",
                    "enabled": true,
                    "requiresApproval": false,
                    "params": {
                      "connectorId": "github",
                      "instanceKey": "github:claims",
                      "actionId": "get_ref",
                      "fieldMapping": {
                        "repo": "claimcheck-test-app",
                        "ref": "heads/{{args.branch}}"
                      }
                    }
                  },
                  {
                    "manifestKind": "connector.invoke",
                    "enabled": true,
                    "requiresApproval": false,
                    "params": {
                      "connectorId": "github",
                      "instanceKey": "github:claims",
                      "actionId": "get_file_contents",
                      "fieldMapping": {
                        "repo": "claimcheck-test-app",
                        "path": "{{args.path}}",
                        "ref": "{{args.ref}}"
                      }
                    }
                  },
                  {
                    "manifestKind": "connector.invoke",
                    "enabled": true,
                    "requiresApproval": false,
                    "params": {
                      "connectorId": "github",
                      "instanceKey": "github:claims",
                      "actionId": "create_branch",
                      "fieldMapping": {
                        "repo": "claimcheck-test-app",
                        "ref": "refs/heads/{{args.branch}}",
                        "sha": "{{args.sha}}"
                      }
                    }
                  },
                  {
                    "manifestKind": "connector.invoke",
                    "enabled": true,
                    "requiresApproval": false,
                    "params": {
                      "connectorId": "github",
                      "instanceKey": "github:claims",
                      "actionId": "commit_files",
                      "fieldMapping": {
                        "repo": "claimcheck-test-app",
                        "path": "{{args.path}}",
                        "branch": "{{args.branch}}",
                        "message": "{{args.message}}",
                        "content": "{{args.content}}",
                        "sha": "{{args.sha}}"
                      }
                    }
                  },
                  {
                    "manifestKind": "connector.invoke",
                    "enabled": true,
                    "requiresApproval": false,
                    "params": {
                      "connectorId": "github",
                      "instanceKey": "github:claims",
                      "actionId": "create_pull_request",
                      "fieldMapping": {
                        "repo": "claimcheck-test-app",
                        "title": "{{args.title}}",
                        "head": "{{args.branch}}",
                        "base": "{{args.base}}",
                        "body": "{{args.body}}"
                      }
                    }
                  },
                  {
                    "manifestKind": "human.askQuestion",
                    "enabled": true,
                    "requiresApproval": true,
                    "params": {}
                  }
                ],
                "temperature": 0
              }
            }
          ],
          "edges": []
        }
      }
    },
    {
      "type": "agent",
      "name": "sdlc-product-owner",
      "definition": {
        "name": "sdlc-product-owner",
        "graph": {
          "nodes": [
            {
              "id": "a1",
              "kind": "agent",
              "data": {
                "name": "sdlc-product-owner",
                "role": "worker",
                "systemPrompt": "You are the product owner agent of an SDLC board. You write acceptance criteria; you do not write code.\n\nYour input is a task: task_id, role, repo and prompt. The prompt is the instruction the task's human owner approved: follow it exactly, it overrides anything else.\nYou plan; you do not change anything on your own. Read what you need with the read tools. Your final message is your result. To record it, propose changes with the update tool: at the end, one change to this task (id = task_id) with status \"done\" and your result in \"result\"; and, only if the prompt asks for triage, a change to a task's \"category\" and \"priority\". The owner approves each change before it is saved.\nDo not invent results. If a tool fails, say so in your result.\n\nRead the story with the read-story tool (filter by the task's story_id) if you need more than the prompt gives. Put your criteria, out-of-scope list and open questions in your result. The story itself is changed only through the approval-gated story tool.",
                "tools": [
                  {
                    "manifestKind": "data.write",
                    "enabled": true,
                    "requiresApproval": true,
                    "params": {
                      "entity": "task",
                      "operation": "update",
                      "idField": "id"
                    }
                  },
                  {
                    "manifestKind": "data.read",
                    "enabled": true,
                    "requiresApproval": false,
                    "params": {
                      "entity": "task",
                      "filters": [],
                      "limit": 50
                    }
                  },
                  {
                    "manifestKind": "data.read",
                    "enabled": true,
                    "requiresApproval": false,
                    "params": {
                      "entity": "story",
                      "filters": [],
                      "limit": 50
                    }
                  },
                  {
                    "manifestKind": "data.write",
                    "enabled": true,
                    "requiresApproval": true,
                    "params": {
                      "entity": "story",
                      "operation": "update",
                      "idField": "id"
                    }
                  }
                ]
              }
            }
          ],
          "edges": []
        }
      }
    },
    {
      "type": "agent",
      "name": "sdlc-scrum-master",
      "definition": {
        "name": "sdlc-scrum-master",
        "graph": {
          "nodes": [
            {
              "id": "a1",
              "kind": "agent",
              "data": {
                "name": "sdlc-scrum-master",
                "role": "worker",
                "systemPrompt": "You are the scrum master agent of an SDLC board. You plan and triage work; you do not write code and you do not create tasks.\n\nYour input is a task: task_id, role, repo and prompt. The prompt is the instruction the task's human owner approved: follow it exactly, it overrides anything else.\nYou plan; you do not change anything on your own. Read what you need with the read tools. Your final message is your result. To record it, propose changes with the update tool: at the end, one change to this task (id = task_id) with status \"done\" and your result in \"result\"; and, only if the prompt asks for triage, a change to a task's \"category\" and \"priority\". The owner approves each change before it is saved.\nDo not invent results. If a tool fails, say so in your result.\n\nRead the story and the tasks it already has with the read tools. Put the proposed task list in your result as a table (order, title, role, depends on, risk).",
                "tools": [
                  {
                    "manifestKind": "data.write",
                    "enabled": true,
                    "requiresApproval": true,
                    "params": {
                      "entity": "task",
                      "operation": "update",
                      "idField": "id"
                    }
                  },
                  {
                    "manifestKind": "data.read",
                    "enabled": true,
                    "requiresApproval": false,
                    "params": {
                      "entity": "story",
                      "filters": [],
                      "limit": 50
                    }
                  },
                  {
                    "manifestKind": "data.read",
                    "enabled": true,
                    "requiresApproval": false,
                    "params": {
                      "entity": "task",
                      "filters": [],
                      "limit": 50
                    }
                  }
                ]
              }
            }
          ],
          "edges": []
        }
      }
    },
    {
      "type": "agent",
      "name": "sdlc-reviewer",
      "definition": {
        "name": "sdlc-reviewer",
        "graph": {
          "nodes": [
            {
              "id": "a1",
              "kind": "agent",
              "data": {
                "name": "sdlc-reviewer",
                "role": "worker",
                "systemPrompt": "You are the reviewer agent of an SDLC board. You review a pull request and leave your review as a comment. You act as the project's review bot, not as any person. You never approve, never request changes and never merge: a human decides.\n\nYour input is a task: task_id, repo, pr_number, pr_url and branch. FIRST set the task's \"reviewed\" field to \"yes\" with the update-task tool (id = task_id), so the board does not start a second review. Then read the task and its story with the read tools, read the pull request and its changed files with the PR tools, and read a changed file in full when a diff is not enough.\n\nReview against the story's acceptance criteria, as the task's prompt describes if it has one. Post one review with the post-review tool (it can only leave a comment): a short summary, then specific points naming the file and line. Then put the same summary in the task's review_summary with the update-task tool. Do not change the task's status.\n\nDo not invent findings. If a tool fails, say so in review_summary.",
                "tools": [
                  {
                    "manifestKind": "data.write",
                    "enabled": true,
                    "requiresApproval": false,
                    "params": {
                      "entity": "task",
                      "operation": "update",
                      "idField": "id"
                    }
                  },
                  {
                    "manifestKind": "data.read",
                    "enabled": true,
                    "requiresApproval": false,
                    "params": {
                      "entity": "task",
                      "filters": [],
                      "limit": 50
                    }
                  },
                  {
                    "manifestKind": "data.read",
                    "enabled": true,
                    "requiresApproval": false,
                    "params": {
                      "entity": "story",
                      "filters": [],
                      "limit": 50
                    }
                  },
                  {
                    "manifestKind": "connector.invoke",
                    "enabled": true,
                    "requiresApproval": false,
                    "params": {
                      "connectorId": "github",
                      "instanceKey": "github:service",
                      "actionId": "get_pull_request",
                      "fieldMapping": {
                        "repo": "claimcheck-test-app",
                        "pull_number": "{{args.pull_number}}"
                      }
                    }
                  },
                  {
                    "manifestKind": "connector.invoke",
                    "enabled": true,
                    "requiresApproval": false,
                    "params": {
                      "connectorId": "github",
                      "instanceKey": "github:service",
                      "actionId": "list_pull_request_files",
                      "fieldMapping": {
                        "repo": "claimcheck-test-app",
                        "pull_number": "{{args.pull_number}}"
                      }
                    }
                  },
                  {
                    "manifestKind": "connector.invoke",
                    "enabled": true,
                    "requiresApproval": false,
                    "params": {
                      "connectorId": "github",
                      "instanceKey": "github:service",
                      "actionId": "get_file_contents",
                      "fieldMapping": {
                        "repo": "claimcheck-test-app",
                        "path": "{{args.path}}",
                        "ref": "{{args.ref}}"
                      }
                    }
                  },
                  {
                    "manifestKind": "connector.invoke",
                    "enabled": true,
                    "requiresApproval": false,
                    "params": {
                      "connectorId": "github",
                      "instanceKey": "github:service",
                      "actionId": "submit_review",
                      "fieldMapping": {
                        "repo": "claimcheck-test-app",
                        "pull_number": "{{args.pull_number}}",
                        "event": "COMMENT",
                        "body": "{{args.body}}"
                      }
                    }
                  }
                ]
              }
            }
          ],
          "edges": []
        }
      }
    }
  ]
}
