{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://snehankekre.com/quickstarted/task-schema.json",
  "title": "quickstarted task",
  "description": "A goal an agent should reach using only the target project's documentation, plus a machine-checkable success assertion.",
  "type": "object",
  "required": [
    "name",
    "goal",
    "docs",
    "success"
  ],
  "additionalProperties": false,
  "properties": {
    "name": {
      "type": "string",
      "description": "Identifier used in output paths and reports."
    },
    "goal": {
      "type": "string",
      "description": "The only instruction the agent receives. Describe an outcome in the words a user would use; naming the API that produces it hands over the answer."
    },
    "image": {
      "type": "string",
      "description": "Container image for the docker backend. The default python:3.12-slim has no Node, Go, or Rust."
    },
    "docs": {
      "type": "object",
      "additionalProperties": false,
      "oneOf": [
        {
          "required": [
            "path"
          ],
          "not": {
            "required": [
              "entrypoint"
            ]
          }
        },
        {
          "required": [
            "entrypoint"
          ],
          "not": {
            "required": [
              "path"
            ]
          }
        }
      ],
      "properties": {
        "path": {
          "type": "array",
          "minItems": 1,
          "items": {
            "type": "string",
            "format": "uri",
            "pattern": "^https?://"
          },
          "description": "The documented route, in the order the project puts it. A quickstart is rarely one page: an install step and a first application often live apart, and a task naming only the second is not testing the quickstart."
        },
        "entrypoint": {
          "type": "string",
          "format": "uri",
          "pattern": "^https?://",
          "description": "The single-page case, equivalent to a one-item 'path'."
        },
        "allow": {
          "type": "array",
          "items": {
            "type": "string",
            "pattern": "^[A-Za-z0-9.-]+$"
          },
          "description": "Additional documentation hosts. Readable only through read_docs; the shell cannot reach them, which is what keeps the record of pages read complete."
        }
      }
    },
    "network": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "allow": {
          "type": "array",
          "items": {
            "type": "string",
            "pattern": "^[A-Za-z0-9.-]+$"
          },
          "description": "Added to the default registry list."
        },
        "only": {
          "type": "array",
          "items": {
            "type": "string",
            "pattern": "^[A-Za-z0-9.-]+$"
          },
          "description": "Replaces the default registry list."
        }
      }
    },
    "setup": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Commands run before the agent starts. The agent is told these ran, so it will not rebuild what they created."
    },
    "replay": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "The literal commands the documentation tells a reader to type. Runs with no model and no API key, and stops at the first failure."
    },
    "success": {
      "type": "object",
      "additionalProperties": false,
      "description": "What decides the verdict. The harness owns the mechanism; every criterion is yours.",
      "anyOf": [
        {
          "required": [
            "script"
          ],
          "not": {
            "required": [
              "file"
            ]
          }
        },
        {
          "required": [
            "file"
          ],
          "not": {
            "required": [
              "script"
            ]
          }
        }
      ],
      "properties": {
        "script": {
          "type": "string",
          "description": "Shell run after the agent stops. Exit 0 is a pass."
        },
        "file": {
          "type": "string",
          "description": "Path to a shell file, relative to the task file. Read at load time and never written into the workspace, so the agent cannot read its own success criteria."
        },
        "serve": {
          "type": "string",
          "description": "A long-running command to background before checking. $QS_PORT is a free port chosen for this task. Starting a server proves nothing on its own, so a task with serve must also assert something."
        },
        "expect_output": {
          "type": "object",
          "additionalProperties": false,
          "minProperties": 1,
          "description": "Assert on what the run printed, for the many quickstarts whose promise is a value on a terminal rather than a file on disk. Asserting a file instead means inventing one the documentation never asked for. This reads the agent's own transcript, so where the documentation also produces durable state, assert on that state as well.",
          "properties": {
            "contains": {
              "oneOf": [
                {
                  "type": "string"
                },
                {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              ],
              "description": "Literal text the run must have printed."
            },
            "matches": {
              "oneOf": [
                {
                  "type": "string"
                },
                {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              ],
              "description": "Extended regular expression the run's output must match."
            }
          }
        },
        "wait_http": {
          "type": "object",
          "additionalProperties": false,
          "minProperties": 1,
          "description": "Poll until the endpoint answers as this task says it should, keeping the last error and dumping the server log on failure.",
          "properties": {
            "path": {
              "type": "string",
              "description": "Resolved against http://127.0.0.1:$QS_PORT."
            },
            "url": {
              "type": "string",
              "description": "A full URL instead."
            },
            "status": {
              "type": "integer",
              "default": 200
            },
            "contains": {
              "oneOf": [
                {
                  "type": "string"
                },
                {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              ],
              "description": "Literal text the body must contain."
            },
            "matches": {
              "oneOf": [
                {
                  "type": "string"
                },
                {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              ],
              "description": "Extended regular expression the body must match."
            },
            "json": {
              "type": "object",
              "description": "Key/value pairs the body must carry. Matched by regular expression, not a JSON parse."
            },
            "timeout": {
              "type": "integer",
              "default": 40
            }
          }
        }
      }
    },
    "budgets": {
      "type": "object",
      "additionalProperties": false,
      "description": "Limits on the agent phase. A task that routinely exhausts its budget is excluded from pass rates rather than counted as a failure, so too small a budget quietly removes it from results.",
      "properties": {
        "max_turns": {
          "type": "integer",
          "default": 20
        },
        "max_seconds": {
          "type": "integer",
          "default": 480,
          "description": "Wall clock for the agent phase."
        },
        "max_command_seconds": {
          "type": "integer",
          "default": 300
        },
        "max_output_chars": {
          "type": "integer",
          "default": 20000,
          "description": "Per command. The head and the tail are kept."
        },
        "max_tokens": {
          "type": "integer",
          "default": 0,
          "description": "Billable tokens, cache included. 0 means unlimited."
        }
      }
    }
  }
}
