{
  "server": "saram",
  "instructions": "Saram behavioural QA: query the artifact of a run in which synthetic personas drove a real\nbrowser through this product.\n\nA run produces two kinds of output and they are not interchangeable:\n\n- Issues (`get_issues`, `get_issue`) are defects, gated on reproduction. An issue with\n  `may_file_ticket_alone: true` reproduced without the persona layer and is safe to act on by itself.\n  One with `false` did not: report it with its evidence, do not file it as a confirmed bug.\n- Verdicts (`get_verdicts`) and opportunities (`get_opportunities`) are persona feedback: subjective,\n  evidence-linked, scored by agreement across personas. They are advisory. They never file a ticket on\n  their own and must be attributed to the persona, not stated as fact.\n\nAn issue's `description` is objective; its `persona_rationale` is the persona's voice. Keep them apart.\n\nStart at `list_runs`, then `get_run` for one run, then `get_issues` to triage and `get_issue` for the repro\nsteps and evidence of one. `get_session_trace` shows what a persona said and what the page did, step by\nstep; `get_screenshot` fetches a `screenshot_ref` from any of the above.\n\nA third record sits beside those two and is neither (`get_tester_errors`): a finding the harness attributed to the persona's own action, not to the product. It is kept and quoted because it says why a session stalled, and it is never a defect - it is not counted in issues_total and never files a ticket.\n\nStarting a run is billable and takes minutes: a cohort of personas drives real\nbrowsers through the product, with real model calls. So the order is validate_spec (free, instant, starts\nnothing, and its cost_ceiling is the most the run can cost), then start_run, then wait_for_run until it\nanswers done, then the read tools above.\n\nThose browsers run in Saram's cloud, not on the user's machine, so the target has to be a URL the internet can\nreach: for an app on localhost, open a tunnel to it or use a preview URL (https://docs.saram.sh/connect-agent).\n\nA run is a person's. Start one because someone asked you to, in the session where they asked, and stay with it\nuntil it finishes - never on a schedule, never unattended, and never in a retry loop. When a run is billed to a\nChatGPT subscription login (get_provider_status), that is not a preference: the login serves runs a person\nstarts, and one run at a time is what start_run will do for you without being told twice.",
  "tools": [
    {
      "name": "list_runs",
      "description": "List this deployment's runs, newest first, one row each: status, target, cohort packs, session counts, issue and opportunity counts, and cost. Start here; get_run has the rest of one run. About 400 characters a run.",
      "inputSchema": {
        "properties": {
          "limit": {
            "default": 20,
            "description": "How many runs to return, newest first, at most 50.",
            "maximum": 50,
            "minimum": 1,
            "title": "Limit",
            "type": "integer"
          },
          "status": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Only runs in this status, e.g. running, completed, failed, cancelled.",
            "title": "Status"
          },
          "target": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Entry URL or target name, matched exactly.",
            "title": "Target"
          }
        },
        "title": "list_runsArguments",
        "type": "object"
      },
      "outputSchema": {
        "$defs": {
          "RunRow": {
            "additionalProperties": false,
            "description": "One run as the MCP `list_runs` answers it: which run, how it went and what it found, and nothing else.\n\n`RunSummary` less what `get_run` is for: what picks a run out of a list, small enough that fifty rows fit in\nwhat a coding agent accepts from one tool. No route answers this - over HTTP the list is `RunList`.",
            "properties": {
              "run_id": {
                "title": "Run Id",
                "type": "string"
              },
              "status": {
                "description": "As `RunSummary.status`: only `completed` is results-ready, and `get_run` says why a run `failed`.",
                "title": "Status",
                "type": "string"
              },
              "target": {
                "title": "Target",
                "type": "string"
              },
              "target_name": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Target Name"
              },
              "caller": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "As `RunSummary.caller`: who started it, in words.",
                "title": "Caller"
              },
              "packs": {
                "items": {
                  "type": "string"
                },
                "title": "Packs",
                "type": "array"
              },
              "session_counts": {
                "additionalProperties": {
                  "type": "integer"
                },
                "title": "Session Counts",
                "type": "object"
              },
              "issues_total": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "`summary.issues_total`, once the artifact is assembled; absent until then.",
                "title": "Issues Total"
              },
              "issues_reproducible": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "`summary.issues_reproducible`: the issues that may file a ticket alone.",
                "title": "Issues Reproducible"
              },
              "opportunities_total": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Opportunities Total"
              },
              "billable_usd": {
                "anyOf": [
                  {
                    "type": "number"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "As `RunSummary.billable_usd`: what this run costs you, to the cent - Saram's charge, and what it has cost\nso far while the run is open. Absent means nobody has priced your tenant, which is not the same as\nfree.",
                "title": "Billable Usd"
              },
              "cost_usd": {
                "anyOf": [
                  {
                    "type": "number"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "As `RunSummary.cost_usd`: what this run's model calls cost - your provider's bill on your own keys, Saram's\nown cost on Saram's models - and NOT what Saram charges you. Absent means nothing priced the model, which is\nnot the same as free.",
                "title": "Cost Usd"
              },
              "created_at": {
                "format": "date-time",
                "title": "Created At",
                "type": "string"
              },
              "finished_at": {
                "anyOf": [
                  {
                    "format": "date-time",
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Finished At"
              }
            },
            "required": [
              "run_id",
              "status",
              "target",
              "created_at"
            ],
            "title": "RunRow",
            "type": "object"
          }
        },
        "additionalProperties": false,
        "description": "The MCP `list_runs` answer: `RunList` in rows an agent can take fifty of.",
        "properties": {
          "runs": {
            "items": {
              "$ref": "#/$defs/RunRow"
            },
            "title": "Runs",
            "type": "array"
          }
        },
        "required": [
          "runs"
        ],
        "title": "RunIndex",
        "type": "object"
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "get_run",
      "description": "One run in full: its status and why, the target, the budgets, the files it hands its personas, the artifact summary once assembled, and one row per persona session with its result, cost and trace refs. While the run is still open this also carries the live workflow query and each unfinished session's heartbeat_step, which is the only thing that says where a session in flight has got to. About 1,000 characters a session.",
      "inputSchema": {
        "properties": {
          "run_id": {
            "description": "Run id, as returned by list_runs.",
            "title": "Run Id",
            "type": "string"
          }
        },
        "required": [
          "run_id"
        ],
        "title": "get_runArguments",
        "type": "object"
      },
      "outputSchema": {
        "$defs": {
          "JudgeDecision": {
            "description": "The decision the judge returned, before the gate discarded it. The judge's and not the persona's: no diary line at the steps it cites supports it. Named apart from `verdict.decision` so that nothing reading a row by field name takes it for the persona's.",
            "enum": [
              "positive",
              "negative",
              "undecided"
            ],
            "title": "JudgeDecision",
            "type": "string"
          },
          "LiveStatus": {
            "additionalProperties": false,
            "description": "The run's state as its workflow reports it while the run is open. Absent on a closed run.",
            "properties": {
              "status": {
                "title": "Status",
                "type": "string"
              },
              "paused": {
                "default": false,
                "title": "Paused",
                "type": "boolean"
              },
              "paused_reason": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "Why: `signal` for a human pause, `budget_cost` when `budget.total_cost_usd` was reached. Either way\nonly a `resume` releases the run.",
                "title": "Paused Reason"
              },
              "sessions": {
                "additionalProperties": {
                  "type": "integer"
                },
                "description": "Session count per state while the run is open: pending (the run has not started it yet), running,\nwaiting_for_seat, and each artifact result. A session the run has started is counted in running once it has a\nbrowser seat and in waiting_for_seat while it is queued for one - taken from each session's own\n`waiting_for_seat`, so the two add up to the sessions in flight. A session whose live state cannot be read\nis counted running.",
                "title": "Sessions",
                "type": "object"
              }
            },
            "required": [
              "status"
            ],
            "title": "LiveStatus",
            "type": "object"
          },
          "Reproduction": {
            "additionalProperties": false,
            "description": "The counts behind `confirmation_rate`, so a reader can see its denominator instead of trusting it. Funnel stages and readings of one set of verdicts, not a partition: `candidates` are the per-session findings the judge raised, merging folds them into `merged`, reproduction ruled on `evaluated` of those and confirmed `confirmed`. The two overlaps are deliberate. A `flaky` finding is in `evaluated` (the experiment ran) and in `inconclusive` (it settled nothing). `excluded` is a subset of the unconfirmed - the ones dropped for having no persona voice left once the replay did not confirm them - and they are exactly the findings this artifact no longer carries, so `merged - excluded` is `issues_total`.",
            "properties": {
              "candidates": {
                "title": "Candidates",
                "type": "integer"
              },
              "merged": {
                "title": "Merged",
                "type": "integer"
              },
              "evaluated": {
                "title": "Evaluated",
                "type": "integer"
              },
              "confirmed": {
                "title": "Confirmed",
                "type": "integer"
              },
              "inconclusive": {
                "title": "Inconclusive",
                "type": "integer"
              },
              "excluded": {
                "title": "Excluded",
                "type": "integer"
              }
            },
            "required": [
              "candidates",
              "merged",
              "evaluated",
              "confirmed",
              "inconclusive",
              "excluded"
            ],
            "title": "Reproduction",
            "type": "object"
          },
          "RunFile": {
            "additionalProperties": false,
            "description": "One file a run hands its personas: the run spec's `files[]` entry as the run was started with it.\n\nServed on `RunDetail` so the run page can list a run's handouts without the spec document itself, which also\ncarries its providers, connectors and seed-account refs. Read from the run's own row, never from the store:\nthe spec is immutable once the run is created, and the run row keeps the copy that was validated.",
            "properties": {
              "ref": {
                "description": "`saram-file://<tenant_id>/<file_id>`, as `FileRecord.ref`. The file may since have been deleted or have\nexpired: this is what the run was handed, not what the tenant still has.",
                "title": "Ref",
                "type": "string"
              },
              "name": {
                "description": "What the persona calls it, and the filename the target receives. Unique within the run.",
                "title": "Name",
                "type": "string"
              },
              "description": {
                "description": "What the persona was told about it, verbatim.",
                "title": "Description",
                "type": "string"
              },
              "assign": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/RunFileAssign"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "Which sessions carry it. Absent means every session."
              }
            },
            "required": [
              "ref",
              "name",
              "description"
            ],
            "title": "RunFile",
            "type": "object"
          },
          "RunFileAssign": {
            "additionalProperties": false,
            "description": "Which of a run's sessions carry a file: those matching every list given (`files[].assign` in the run spec).",
            "properties": {
              "personas": {
                "anyOf": [
                  {
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "Persona addresses, as the spec's `personas` names them. Absent means any persona.",
                "title": "Personas"
              },
              "missions": {
                "anyOf": [
                  {
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "Mission ids. Absent means any mission.",
                "title": "Missions"
              }
            },
            "title": "RunFileAssign",
            "type": "object"
          },
          "SessionRow": {
            "additionalProperties": false,
            "description": "One session of a run: one persona on one mission, in its own browser. What it ended as and whose fault\nan error was, the tokens, cost and Usage it took, where it has got to while it runs, and the refs to its\nstep-by-step traces and video.",
            "properties": {
              "session_id": {
                "title": "Session Id",
                "type": "string"
              },
              "persona_id": {
                "title": "Persona Id",
                "type": "string"
              },
              "pack": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "The persona's pack as the registry had it when the run was snapshotted - the session's cohort. Absent\nfor a persona that declares none, and for a session recorded before sessions carried one.",
                "title": "Pack"
              },
              "tags": {
                "description": "The persona's tags at snapshot time, beside `pack`.",
                "items": {
                  "type": "string"
                },
                "title": "Tags",
                "type": "array"
              },
              "mission_id": {
                "title": "Mission Id",
                "type": "string"
              },
              "status": {
                "description": "`pending`, `running`, an artifact result, or `cancelled`. Nothing reports `running` today: a session is\nrecorded once, when it ends, so a session in flight still reads `pending` - see `heartbeat_step`, and\n`waiting_for_seat` for one that is queued for a browser seat rather than on a step.",
                "title": "Status",
                "type": "string"
              },
              "result": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "The artifact result once the loop reported; absent before that and on a session that never started.",
                "title": "Result"
              },
              "end_reason": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "How the session ended: the loop's word for an ending the persona reached (`success_criteria_met`,\n`patience_exhausted`, `budget_steps`), and on an `errored` one a sentence that says what kind of failure it\nwas - the target's, the model provider's (with the provider's own message), or Saram's - with `fault` beside\nit.",
                "title": "End Reason"
              },
              "fault": {
                "anyOf": [
                  {
                    "enum": [
                      "none",
                      "saram",
                      "provider",
                      "target"
                    ],
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "Whose failure ended the session. `none` for every ending the persona or the loop reached, and for a\nsession a person cancelled; on an `errored` one, `saram` (the harness: the pool, a setup step, a lost\nworker), `provider` (the model API: an HTTP error or a transport failure after the retries, or a reply the\nloop could not act on) or `target` (the product: its entry page did not load, or its reset hook refused the\nsession). What decides whether an errored session is charged. `null` while the session is open, and on a\nsession recorded before this was classified - never read it as `none`.",
                "title": "Fault"
              },
              "steps": {
                "title": "Steps",
                "type": "integer"
              },
              "end_step": {
                "title": "End Step",
                "type": "integer"
              },
              "abandon_step": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Abandon Step"
              },
              "tokens_in": {
                "description": "The whole prompt plus the reply: `tokens_in` already includes both cache halves below.",
                "title": "Tokens In",
                "type": "integer"
              },
              "tokens_out": {
                "title": "Tokens Out",
                "type": "integer"
              },
              "tokens_cache_read": {
                "default": 0,
                "description": "Of `tokens_in`, read from the prompt cache - billed at a tenth of the input rate on Anthropic.",
                "title": "Tokens Cache Read",
                "type": "integer"
              },
              "tokens_cache_write": {
                "default": 0,
                "description": "Of `tokens_in`, written to the prompt cache.",
                "title": "Tokens Cache Write",
                "type": "integer"
              },
              "model": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Model"
              },
              "cost_usd": {
                "anyOf": [
                  {
                    "type": "number"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "What this session's model calls cost. Absent when nothing prices its model; never 0 to mean \"unknown\".",
                "title": "Cost Usd"
              },
              "price_source": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "Which price table produced `cost_usd`: this deployment's own, `env` (a bare model id's row) or `env/<kind>`\n(a row for the model on one provider kind), else `genai-prices/<version>/<provider>`.",
                "title": "Price Source"
              },
              "wall_clock_s": {
                "title": "Wall Clock S",
                "type": "number"
              },
              "uncertain_steps": {
                "anyOf": [
                  {
                    "items": {
                      "type": "integer"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "The steps of this session whose effect on the product nothing can attest: the call was dispatched, the\nresponse was lost, and the resume reconciled against the pool's step id rather than re-issuing it. `null`\nmeans nobody looked - a session recorded before the harness produced this, or one whose activity was lost\nbefore it or its checkpoint could report - and `[]` means the loop checked and every step is attested.\nNever read `null` as \"none\": it is the same three-way reading as `artifact.outcomes[].uncertain_steps`.",
                "title": "Uncertain Steps"
              },
              "resumed_at_steps": {
                "anyOf": [
                  {
                    "items": {
                      "type": "integer"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "The first step of each resumed browser execution, one per resume: the re-orienting `look` the loop\ntakes when its activity is retried. `null` and `[]` read as above. A browser checkpoint carries a url and\na storage state and nothing else, so each of these is where a new execution picked the experiment up, not\nwhere the same page carried on.",
                "title": "Resumed At Steps"
              },
              "verdict_decision": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "The judge's verdict decision once it has read the session: positive, negative, or undecided.",
                "title": "Verdict Decision"
              },
              "outcome_read": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "What the judge read the session's outcome as, independent of what the loop reported.",
                "title": "Outcome Read"
              },
              "observer_error": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "Why nobody read this session, when nobody did, in Saram's words: the judge failure that left the session read\nas `unclear` - the model provider's status and own message when the provider refused, the `providers` entry's\nrefusal when it did not resolve, Saram's sentence otherwise. Absent on every session a judge pass read,\nincluding one it found nothing in - which is the only way a reader tells a clean session from one no model ever\nsaw.",
                "title": "Observer Error"
              },
              "verdict_discarded": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/VerdictDiscarded"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "NOT A VERDICT. The answer the judge gave about this session that the quote gate threw away, and why: set only\nwhen `verdict_decision` is absent on a session a judge read. Absent when the judge returned no verdict, which is\nthe difference it is here for - both leave no decision. The decision is the judge's with no line of the persona's\ndiary behind it, so it is never the persona's verdict and is never counted as one. Absent on a session observed\nbefore the control plane recorded it, whose gate ledger is the only record."
              },
              "heartbeat_step": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "LIVE. The `step_id` the persona last reported, read live: a session is recorded once, when it ends, so\nthis is the only field that says where a session in flight has got to. Best effort - absent on a finished\nsession, and on a running one whose live state cannot be read. Compare it with the steps page's\n`last_step_id` to see how far the readable trace lags the browser: the trace records a step after the browser\nanswers, so a step in flight shows here first.",
                "title": "Heartbeat Step"
              },
              "last_heartbeat_at": {
                "anyOf": [
                  {
                    "format": "date-time",
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "When that heartbeat reached the server: how stale `heartbeat_step` is. Absent whenever it is.",
                "title": "Last Heartbeat At"
              },
              "heartbeat_usage": {
                "anyOf": [
                  {
                    "type": "number"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "LIVE. This session's Usage so far, to 0.001, from the step times on its last heartbeat: persona steps\nweighted by how long each one took, measured the way the session's Usage will be when it ends. A report, not a\ncharge. Best effort, as `heartbeat_step` - and also absent for a session on a worker that does not report step\ntimes.",
                "title": "Heartbeat Usage"
              },
              "usage": {
                "anyOf": [
                  {
                    "type": "number"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "This session's Usage, to 0.001: the credit ledger's figure once it is settled, measured from its recorded\nstep times before that, and on a session in flight its last heartbeat (`heartbeat_usage`). Absent on a session\nthat has not taken a step, and on one recorded without step times.",
                "title": "Usage"
              },
              "usage_usd": {
                "anyOf": [
                  {
                    "type": "number"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "What this session's Usage costs you, net of any refund (`refunded_usd`). Absent when nothing prices it: a\nplan billed in steps.",
                "title": "Usage Usd"
              },
              "refunded_usd": {
                "anyOf": [
                  {
                    "type": "number"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "What was given back for this session because it failed on Saram's side (fault `saram`): its whole debit. 0\notherwise. Absent wherever `usage_usd` is.",
                "title": "Refunded Usd"
              },
              "waiting_for_seat": {
                "anyOf": [
                  {
                    "type": "boolean"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "LIVE. `true` while this session is queued for a browser seat: the run has started it, but every seat in the\nbrowser pool is taken, so it has no browser and takes no steps until one frees - a run with more sessions than\nthe pool has seats shows this on the rest, and they start in turn without spending an attempt. `false` while\nthe session is not waiting: setting up, on a seat, or paused. Read live, best effort as `heartbeat_step` -\nabsent on a finished session, on one the run has not started yet (its `max_concurrent_sessions` holds it\nback), and on one whose live state cannot be read. A session resuming after a\npause or a pool failure queues the same way, so it can be `true` beside a `heartbeat_step`.",
                "title": "Waiting For Seat"
              },
              "waiting_since": {
                "anyOf": [
                  {
                    "format": "date-time",
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "When the session was queued for the seat it is waiting for: how long it has waited. Absent whenever\n`waiting_for_seat` is not `true`.",
                "title": "Waiting Since"
              },
              "workflow_id": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Workflow Id"
              },
              "browser_session_id": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Browser Session Id"
              },
              "agent_trace_ref": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Agent Trace Ref"
              },
              "surface_trace_ref": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Surface Trace Ref"
              },
              "video_ref": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Video Ref"
              },
              "video_url": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Video Url"
              },
              "recorded_under_mask": {
                "default": false,
                "description": "Whether the browser pool recorded this session under its mask: every field the persona typed into, every\none-time-code field and every `data-saram-mask` element painted over in every screenshot and video frame it\nstored. Only such a session of yours can be shared by link while `ShareList.mask_required` is `true`: a share of\nany other is refused with a 409, so read the two together before offering one. `false` for every session\nrecorded before the pool masked, and by a pool that does not.",
                "title": "Recorded Under Mask",
                "type": "boolean"
              },
              "created_at": {
                "format": "date-time",
                "title": "Created At",
                "type": "string"
              },
              "finished_at": {
                "anyOf": [
                  {
                    "format": "date-time",
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Finished At"
              }
            },
            "required": [
              "session_id",
              "persona_id",
              "mission_id",
              "status",
              "steps",
              "end_step",
              "tokens_in",
              "tokens_out",
              "wall_clock_s",
              "created_at"
            ],
            "title": "SessionRow",
            "type": "object"
          },
          "Summary": {
            "additionalProperties": false,
            "description": "Counts over the run. The result buckets (completed, abandoned, blocked, errored, budget_exhausted) partition `sessions`: one bucket per value of outcomes[].result, every session in exactly one, so they sum to `sessions`. `verdicts` has the same denominator.",
            "properties": {
              "personas_run": {
                "title": "Personas Run",
                "type": "integer"
              },
              "missions": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Missions"
              },
              "sessions": {
                "title": "Sessions",
                "type": "integer"
              },
              "completed": {
                "title": "Completed",
                "type": "integer"
              },
              "abandoned": {
                "title": "Abandoned",
                "type": "integer"
              },
              "blocked": {
                "title": "Blocked",
                "type": "integer"
              },
              "errored": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Errored"
              },
              "budget_exhausted": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": 0,
                "title": "Budget Exhausted"
              },
              "issues_total": {
                "title": "Issues Total",
                "type": "integer"
              },
              "issues_reproducible": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Issues Reproducible"
              },
              "issues_marker_matched": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Issues Marker Matched"
              },
              "tester_errors": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Tester Errors"
              },
              "issues_new_vs_baseline": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Issues New Vs Baseline"
              },
              "issues_resolved_vs_baseline": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Issues Resolved Vs Baseline"
              },
              "issues_persisting_vs_baseline": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Issues Persisting Vs Baseline"
              },
              "opportunities_total": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Opportunities Total"
              },
              "verdicts": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/Verdicts"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "confirmation_rate": {
                "anyOf": [
                  {
                    "maximum": 1.0,
                    "minimum": 0.0,
                    "type": "number"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Confirmation Rate"
              },
              "reproduction": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/Reproduction"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "gate_discards": {
                "anyOf": [
                  {
                    "minimum": 0,
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": 0,
                "title": "Gate Discards"
              },
              "path_diversity": {
                "anyOf": [
                  {
                    "maximum": 1.0,
                    "minimum": 0.0,
                    "type": "number"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Path Diversity"
              },
              "cost_usd": {
                "anyOf": [
                  {
                    "type": "number"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Cost Usd"
              },
              "actor_cost_usd": {
                "anyOf": [
                  {
                    "minimum": 0.0,
                    "type": "number"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Actor Cost Usd"
              },
              "judge_cost_usd": {
                "anyOf": [
                  {
                    "minimum": 0.0,
                    "type": "number"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Judge Cost Usd"
              },
              "wall_clock_s": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Wall Clock S"
              }
            },
            "required": [
              "personas_run",
              "sessions",
              "completed",
              "abandoned",
              "blocked",
              "issues_total"
            ],
            "title": "Summary",
            "type": "object"
          },
          "VerdictDiscarded": {
            "additionalProperties": false,
            "description": "NOT A VERDICT. The answer a judge gave about this session that the verbatim-quote gate threw away because it could not ground itself in the persona's words: none of its quotes is in the diary at the steps it cites, or none of the steps it cites is a step of the session. Present only on a row a judge read that has no `verdict`, so never beside `verdict` or `observer_error`. It is what tells a judge that answered and was not grounded apart from a persona who gave no answer: both rows carry no `verdict` and both count `undecided` in `summary.verdicts`, which counts grounded verdicts alone, and only this one says an answer was given. The decision is the judge's reading with nothing in the diary to support it, so a consumer never shows it as the persona's verdict, never counts it and never acts on it; what it points a reader at is the judge's citations. Nothing the judge quoted is carried here, because none of it is verifiably the persona's: the records, the dropped quotes among them, are in the session's gate ledger beside its observation, and the discard is counted in `summary.gate_discards`. Absent on a row with no `verdict` means nothing was discarded: the judge returned no answer, no judge read the session (`observer_error`), or no question was in play (`verdict_prompt_source` absent). An artifact written before 0.6.3 carries none either, and `contracts_version` is what tells those apart.",
            "properties": {
              "judge_decision": {
                "$ref": "#/$defs/JudgeDecision"
              },
              "gate_reason": {
                "title": "Gate Reason",
                "type": "string"
              }
            },
            "required": [
              "judge_decision",
              "gate_reason"
            ],
            "title": "VerdictDiscarded",
            "type": "object"
          },
          "Verdicts": {
            "additionalProperties": false,
            "description": "Cohort-level read of persona verdicts. Subjective, derived from outcomes[].verdict. positive + negative + undecided = sessions: a session with no grounded verdict is undecided, not absent. That includes a session whose judge answered and had the answer discarded by the quote gate: its row names it in `outcomes[].verdict_discarded`, and it is still counted here as undecided, because this tally counts only verdicts grounded in the persona's words.",
            "properties": {
              "positive": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Positive"
              },
              "negative": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Negative"
              },
              "undecided": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Undecided"
              },
              "top_reasons": {
                "anyOf": [
                  {
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Top Reasons"
              }
            },
            "title": "Verdicts",
            "type": "object"
          }
        },
        "additionalProperties": false,
        "description": "One run in full: everything `RunSummary` carries, plus its budgets, the files it hands its personas, its\nlive state while it is open, and every session.",
        "properties": {
          "run_id": {
            "title": "Run Id",
            "type": "string"
          },
          "status": {
            "description": "`pending`, `running`, `observing`, then one terminal value. `completed` means at least one session\nreached an ending its persona got to (`completed`, `abandoned`, `blocked`, `budget_exhausted`), so there\nare results to read. `failed` covers both a run that broke - a rejected spec, a reset hook or the\nobserver - and a run whose every session `errored`, which came back with no persona outcome at all;\n`status_reason` says which. `cancelled` is a `cancel` signal. Poll until the value is terminal, and treat\nonly `completed` as results-ready.",
            "title": "Status",
            "type": "string"
          },
          "status_reason": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Why a run ended `failed` or `cancelled`, in a sentence for the caller: the spec's refusal or the target's\nreset hook's, quoted; that Saram could not start the run, or could not finish judging its sessions; that every\nsession errored; or `cancelled by signal`. Absent on a `completed` run.",
            "title": "Status Reason"
          },
          "target": {
            "title": "Target",
            "type": "string"
          },
          "target_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Target Name"
          },
          "baseline_run_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Baseline Run Id"
          },
          "tenant_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Who owns this run, and so who may read it: every route filters by the tenant the request's key belongs\nto, and a run outside it is a 404 rather than a 403. Always your own tenant, which is what makes it safe to\npublish - a response can only ever carry the id you are already authenticated as. Absent on runs created\nbefore runs recorded their tenant; those are readable by this deployment's own fleet tenant and by nobody\nelse.",
            "title": "Tenant Id"
          },
          "created_by": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Which credential started it: `key_...` for an API key, `operator` for a break-glass entry or a run an\noperator started in-process. The id of the key rather than its name, so rotating a key leaves the runs it\nstarted still pointing at it.",
            "title": "Created By"
          },
          "caller": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Who started this run, in words: the caller name the API key gate identified at `POST /runs` - the\noperator's own name for a break-glass entry, a name like `acme/ci` for an API key. Absent when nobody was\nidentified, which is the gate being off, a run an operator started in-process, or a run created before runs\nrecorded their caller; the three are deliberately indistinguishable rather than dressed up with an invented\nname. Attribution, beside the authorization `tenant_id` carries.",
            "title": "Caller"
          },
          "confirmation_rate": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Confirmed issues over the issues reproduction ruled on. Available as soon as reproduction finishes, which\nis before `summary.confirmation_rate` exists; the two are the same number computed from the same lineage.",
            "title": "Confirmation Rate"
          },
          "repro_attempts": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Repro Attempts"
          },
          "repro_gate_discards": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "How many things reproduction's gate threw away, of two kinds: claim-check quotes the cited evidence does\nnot contain, each of which left its issue `ungrounded`, and replay attempts whose path diverged\nbefore the judged step, each of which ruled on nothing and left its issue `skipped`. Read beside\n`confirmation_rate`: that says how much of what the judge said survived the replay, this says how often the\nreplay's own checker cited loosely or its path stopped being walkable. Zero is the expected value. Absent\nuntil reproduction has run, and on runs reproduced before this was recorded.",
            "title": "Repro Gate Discards"
          },
          "repro_gate_ref": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "The records behind `repro_gate_discards`, fetched through `GET /api/v1/blobs`: `{\"run_id\", \"discards\"}`,\none discard each with its `session_id`, `kind`, `reason` and a `payload` naming the `issue_id`. A\n`claim_quote` payload carries the `quote`; a `path_diverged` one carries the `attempt`, the\n`attempt_session_id`, the original `step_id` it diverged at (null for the entry url), that step's `action`\nand `expect`, and the `url` the replay was on instead. Written even when empty, so an absent ref is\nreproduction that has not run, never \"nothing thrown away\".",
            "title": "Repro Gate Ref"
          },
          "billed_steps": {
            "default": 0,
            "description": "What this run is charged for: the steps its personas took - one browser action each - never the\nreproduction replay's, which are included at no charge. LIVE: a session is recorded when\nit ends, so while the run is open this is the finished sessions' counts plus each unfinished\nsession's last `heartbeat_step`, and it climbs until the run does end.",
            "title": "Billed Steps",
            "type": "integer"
          },
          "billable_usd": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "What this run costs you, to the cent: `billed_steps` at your plan's step price - or your own agreed\nprice - in force when the run ran. This is Saram's charge; `cost_usd` beside it is model spend and is not.\nLIVE with `billed_steps` while the run is open, so it is what the run has cost you so far. Absent means\nnobody has priced your tenant, which is not the same as free - never read a missing price as $0. Your plan's\nincluded steps and monthly fee are not in it: they are properties of a month, and the invoice line with the\nallowance applied is this run's `billable_usd` on `GET /api/v1/usage`.",
            "title": "Billable Usd"
          },
          "cost_usd": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "What this run's model calls cost: `actor_cost_usd` and `judge_cost_usd` together. NOT what Saram charges\nyou - `billable_usd` is that: on your own keys this is your provider's bill, and on Saram's models it is Saram's\nown cost, shown so your Saram usage and your provider's invoice add up. Written when the run finishes. Absent\nmeans nothing priced the models, which is not the same as free. On a run finished before the judge was priced\nit is the persona loop's alone, and `judge_cost_usd` is absent beside it.",
            "title": "Cost Usd"
          },
          "actor_cost_usd": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Of `cost_usd`, the persona loop's model calls: the sum of the priced sessions' `cost_usd`. Absent when no\nsession's model had a price.",
            "title": "Actor Cost Usd"
          },
          "judge_cost_usd": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Of `cost_usd`, the observer's model calls: the judge's reading of each session, attribution, the merge,\nand the reproduction's claim check and tie-break - a failed attempt included, because it was paid for. On\ntoday's models this is often the larger half. A retest you ask for later is not in it. Absent when none of\nthose calls had a price, and on a run finished before they were priced.",
            "title": "Judge Cost Usd"
          },
          "usage": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "This run's Usage: persona steps weighted by how long each took, summed over its sessions, to 0.001. LIVE\nwhile `usage_state` is `live`: settled sessions read what the ledger debited, and a session in flight its last\nheartbeat. Final once `usage_state` is `settled` or `refunded`, when it is the credit ledger's own figure.\nAbsent on a finished run none of whose sessions was measured, which is not the same as none used.",
            "title": "Usage"
          },
          "usage_usd": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "What this run's Usage costs you in dollars, at your plan's Usage price when each session ran: charged from\nyour prepaid balance, refunds already taken off - so a run that failed on Saram's side reads 0 and the money\ngiven back is `refunded_usd`. Moves with `usage` while the run is live. This is Saram's charge on a plan that\nprices Usage (Free, Pro); your model provider's bill is never in it. Absent when nothing prices this run's\nUsage - a plan billed in steps, where `billable_usd` is the charge - which is not the same as free.",
            "title": "Usage Usd"
          },
          "refunded_usd": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Of this run's Usage, the dollars given back because a session failed on Saram's side (the pool, a lost\nworker, a setup step in Saram's code: fault `saram`). Not in `usage_usd`. Absent wherever `usage_usd` is.",
            "title": "Refunded Usd"
          },
          "held_usd": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "What this run still reserves from your balance: the most its spec's budgets can use (the Usage ceiling),\nless each finished session's share. Released when the run's last session is settled. Absent when the run\nholds nothing - it has finished, or the run gate did not hold for it.",
            "title": "Held Usd"
          },
          "usage_state": {
            "anyOf": [
              {
                "enum": [
                  "live",
                  "held",
                  "settled",
                  "refunded"
                ],
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "How final `usage` and `usage_usd` are. `held`: the run is open and holds `held_usd`, and no step is\nmeasured yet. `live`: the run is open, or has just finished and is being settled - the figures are so far and\nstill rise. `settled`: every session is settled and the figures are the credit ledger's. `refunded`: settled,\nand every dollar of its Usage was given back because it failed on Saram's side.",
            "title": "Usage State"
          },
          "sessions_per_hour": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Sessions that ran a step, per hour of the execution phase (run start to last session finish).",
            "title": "Sessions Per Hour"
          },
          "artifact_ref": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Artifact Ref"
          },
          "summary": {
            "anyOf": [
              {
                "$ref": "#/$defs/Summary"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "The artifact's `summary` block, once the artifact is assembled."
          },
          "session_counts": {
            "additionalProperties": {
              "type": "integer"
            },
            "description": "Session count per status for this run: pending, running, the artifact results, or cancelled.",
            "title": "Session Counts",
            "type": "object"
          },
          "packs": {
            "description": "The distinct persona packs this run's sessions were drawn from, sorted: the cohorts it covers. A run whose\npersonas have no pack lists none rather than an empty cohort.",
            "items": {
              "type": "string"
            },
            "title": "Packs",
            "type": "array"
          },
          "created_at": {
            "format": "date-time",
            "title": "Created At",
            "type": "string"
          },
          "started_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Started At"
          },
          "finished_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Finished At"
          },
          "evidence_expires_at": {
            "description": "When this run's step-by-step evidence is deleted: `created_at` plus the days its plan keeps evidence - 14 on\nFree, 90 on Pro, this deployment's default on a plan that sets none - or the days its plan kept when the run\nwas created, whichever is longer, so a plan change never moves this date earlier and an upgrade moves it later.\nThe screenshots, the video, the agent and surface step-event streams, the checkpoints and the per-session\nobservation documents go at this date, in every bucket they live in. Earlier when they went earlier: a run\nwhose recordings the storage had already removed is found by the retention sweep, which takes the rest of its\nevidence and records when, and that moment is this date. The run's *record* is kept and has no\nexpiry: this response, its issues, its verdicts, its opportunities, the spec it ran and the assembled\nartifact. A reader that wants a screenshot after that date must keep the bytes, not the ref.",
            "format": "date-time",
            "title": "Evidence Expires At",
            "type": "string"
          },
          "evidence_expired": {
            "default": false,
            "description": "Whether that date has passed. `GET /api/v1/blobs` on this run's session evidence answers 404 once it\nhas, and that 404 means \"expired\", not \"never existed\" - which is why the date is published beside it.",
            "title": "Evidence Expired",
            "type": "boolean"
          },
          "workflow_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Workflow Id"
          },
          "budgets": {
            "additionalProperties": true,
            "title": "Budgets",
            "type": "object"
          },
          "run_spec_ref": {
            "title": "Run Spec Ref",
            "type": "string"
          },
          "files": {
            "description": "The files the run hands its personas, in the spec's order: its `files[]`, without the rest of the spec.\nEmpty for a run that names none.",
            "items": {
              "$ref": "#/$defs/RunFile"
            },
            "title": "Files",
            "type": "array"
          },
          "live": {
            "anyOf": [
              {
                "$ref": "#/$defs/LiveStatus"
              },
              {
                "type": "null"
              }
            ],
            "default": null
          },
          "sessions": {
            "items": {
              "$ref": "#/$defs/SessionRow"
            },
            "title": "Sessions",
            "type": "array"
          }
        },
        "required": [
          "run_id",
          "status",
          "target",
          "created_at",
          "evidence_expires_at",
          "run_spec_ref"
        ],
        "title": "RunDetail",
        "type": "object"
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "get_issues",
      "description": "A run's issues as triage rows, most severe first: title, type, severity, route, confidence, who it affected and whether it reproduced. Issues are defects, gated on reproduction: a row with may_file_ticket_alone=true reproduced without the persona layer. Empty with the run's status - not an error - until the artifact is assembled. Call get_issue for one issue in full: its description, repro steps and evidence refs. About 550 characters an issue.",
      "inputSchema": {
        "properties": {
          "run_id": {
            "description": "Run id.",
            "title": "Run Id",
            "type": "string"
          },
          "min_severity": {
            "default": "low",
            "description": "Lowest severity to return.",
            "enum": [
              "low",
              "medium",
              "high",
              "critical"
            ],
            "title": "Min Severity",
            "type": "string"
          },
          "reproducible_only": {
            "default": false,
            "description": "Only issues that survived reproduction, i.e. that may file a ticket alone.",
            "title": "Reproducible Only",
            "type": "boolean"
          }
        },
        "required": [
          "run_id"
        ],
        "title": "get_issuesArguments",
        "type": "object"
      },
      "outputSchema": {
        "$defs": {
          "IssueRow": {
            "additionalProperties": false,
            "description": "One issue as a triage row: enough to rank the run's issues and choose one, not enough to act on one.\n\nThe row the local MCP server's `get_issues` answers too, so a row reads the same whichever server a model\nasked. The whole issue - description, repro steps, preconditions, evidence - is `IssueDetail`, one call to\n`get_issue` away.",
            "properties": {
              "id": {
                "title": "Id",
                "type": "string"
              },
              "title": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Title"
              },
              "type": {
                "title": "Type",
                "type": "string"
              },
              "severity": {
                "title": "Severity",
                "type": "string"
              },
              "reproducible": {
                "title": "Reproducible",
                "type": "boolean"
              },
              "reproduction_result": {
                "description": "`reproduction.result`: reproduced, not_reproduced, flaky, or skipped when it was never attempted.",
                "title": "Reproduction Result",
                "type": "string"
              },
              "may_file_ticket_alone": {
                "description": "Whether this issue may be filed as a ticket on its own, computed as `IssueReading.may_file_ticket_alone`\nis. A row carries no persona voice, so it carries no advisory; `get_issue` has both.",
                "title": "May File Ticket Alone",
                "type": "boolean"
              },
              "route": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Route"
              },
              "affected_sessions": {
                "items": {
                  "type": "string"
                },
                "title": "Affected Sessions",
                "type": "array"
              },
              "affected_count": {
                "title": "Affected Count",
                "type": "integer"
              },
              "affected_fraction": {
                "anyOf": [
                  {
                    "type": "number"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "Of the sessions that reached the issue's location: `len(affected_sessions) / len(reached_sessions)`.",
                "title": "Affected Fraction"
              },
              "reached_sessions": {
                "anyOf": [
                  {
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "The sessions that reached the issue's location, every affected session among them: the denominator of\n`affected_fraction`, so a triage pass can check it and rank issues by how many sessions got there. Absent on\nan artifact before contracts 0.6.2, which did not publish it.",
                "title": "Reached Sessions"
              },
              "first_seen_step": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "First Seen Step"
              },
              "confidence": {
                "title": "Confidence",
                "type": "number"
              },
              "baseline_status": {
                "title": "Baseline Status",
                "type": "string"
              },
              "has_repro_steps": {
                "title": "Has Repro Steps",
                "type": "boolean"
              },
              "connector_refs": {
                "anyOf": [
                  {
                    "additionalProperties": {
                      "type": "string"
                    },
                    "type": "object"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "Where a connector already filed this issue (`github_issue` -> URL), present once one has.",
                "title": "Connector Refs"
              }
            },
            "required": [
              "id",
              "type",
              "severity",
              "reproducible",
              "reproduction_result",
              "may_file_ticket_alone",
              "affected_sessions",
              "affected_count",
              "confidence",
              "baseline_status",
              "has_repro_steps"
            ],
            "title": "IssueRow",
            "type": "object"
          }
        },
        "additionalProperties": false,
        "description": "The MCP `get_issues` answer: the run's issues as triage rows, filtered and most severe first.\n\nNo route answers this - `GET /runs/{id}/issues` is `IssueList`, whole issues for a page that shows them.\nA whole issue is a few thousand characters and a run can have twenty, more than a coding agent accepts from\none tool; a row is a few hundred.",
        "properties": {
          "run_id": {
            "title": "Run Id",
            "type": "string"
          },
          "status": {
            "description": "The run's status. An empty `issues` is only a result once this is terminal.",
            "title": "Status",
            "type": "string"
          },
          "issues_total": {
            "description": "The run's issues before the filter, so a narrowed answer says how much it left out.",
            "title": "Issues Total",
            "type": "integer"
          },
          "issues": {
            "items": {
              "$ref": "#/$defs/IssueRow"
            },
            "title": "Issues",
            "type": "array"
          }
        },
        "required": [
          "run_id",
          "status",
          "issues_total",
          "issues"
        ],
        "title": "IssueIndex",
        "type": "object"
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "get_issue",
      "description": "One issue in full: the contract object verbatim, its reproduction result, deterministic persona-free repro steps, preconditions, the reading that says whether it may file a ticket alone, and the evidence refs get_screenshot accepts. Fields named in subjective_fields (persona_rationale) are the persona's voice, not observed behaviour.",
      "inputSchema": {
        "properties": {
          "run_id": {
            "description": "Run id.",
            "title": "Run Id",
            "type": "string"
          },
          "issue_id": {
            "description": "Issue id, as returned by get_issues.",
            "title": "Issue Id",
            "type": "string"
          }
        },
        "required": [
          "run_id",
          "issue_id"
        ],
        "title": "get_issueArguments",
        "type": "object"
      },
      "outputSchema": {
        "$defs": {
          "A": {
            "description": "The first of the two statements that conflict: what the product said (`text`), and the session and step where it said it.",
            "properties": {
              "step_id": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Step Id"
              },
              "session_id": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Session Id"
              },
              "text": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Text"
              }
            },
            "title": "A",
            "type": "object"
          },
          "ActionStep": {
            "additionalProperties": false,
            "description": "The step the claim is about and the replay is judged at - the action that failed, not the look that saw it or the retry after it. Written by reproduction, which picks it (`_judged_path`); absent when nothing replayed this issue.",
            "properties": {
              "session_id": {
                "title": "Session Id",
                "type": "string"
              },
              "step_id": {
                "title": "Step Id",
                "type": "integer"
              }
            },
            "required": [
              "session_id",
              "step_id"
            ],
            "title": "ActionStep",
            "type": "object"
          },
          "Attribution": {
            "description": "Whose failure this is, asked before an issue is a product finding: could the persona's own action explain it? `product`: no - the failure is the product's to own. `tester`: yes - a mistyped credential, the wrong element, placeholder text read as a value; the record moves to `tester_errors[]` with a `tester_error` block, is never a ticket and never counted as an issue. `undetermined`: the harness could not tell (a persona handed stale credentials by the mission author typed exactly what it was told; an attribution call that failed), and a human must; it caps `status` at `marker_matched`. Absent means never asked - every issue before this release - and a reader treats absent as `undetermined`. A classification: nothing acts on it directly, the producer derives `status` from it, so a later member (`setup`) is additive and an older reader buckets it as `unknown`.",
            "examples": [
              "product",
              "tester",
              "undetermined"
            ],
            "title": "Attribution",
            "type": "string",
            "x-classification": true
          },
          "B": {
            "description": "The second of the two statements that conflict: what the product said (`text`), and the session and step where it said it.",
            "properties": {
              "step_id": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Step Id"
              },
              "session_id": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Session Id"
              },
              "text": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Text"
              }
            },
            "title": "B",
            "type": "object"
          },
          "BaselineStatus": {
            "description": "How this finding compares with the run named as the baseline. `new`: the baseline reached the same page and did not have it (for an issue, only one that is `reproducible`). `persisting`: the baseline has it too. `unknown`: nothing could say - no baseline was compared, the baseline never reached the page, or an issue the baseline does not have was not verified. `resolved` is never stamped on a finding of this run: a baseline issue this run no longer has is counted in `summary.issues_resolved_vs_baseline`. More values may be added; read one you do not know as `unknown`.",
            "examples": [
              "new",
              "persisting",
              "resolved",
              "unknown"
            ],
            "title": "BaselineStatus",
            "type": "string",
            "x-classification": true
          },
          "Check": {
            "additionalProperties": false,
            "description": "The claim check: one narrow question asked of the evidence at the cited steps after a replay matched a marker - does this evidence show the product doing what the title says? Descriptive: nothing acts on it directly; the producer derives `status` from `verdict` and `attribution`. Absent when the check never ran, which is every issue before this release and every issue no marker matched.",
            "properties": {
              "verdict": {
                "$ref": "#/$defs/Verdict1"
              },
              "quote": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Quote"
              },
              "quote_step": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/QuoteStep"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "reason": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Reason"
              },
              "checked_by": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/CheckedBy"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              }
            },
            "required": [
              "verdict"
            ],
            "title": "Check",
            "type": "object"
          },
          "CheckedBy": {
            "description": "Which instrument answered: `judge` for the observer's checker model call under its own instructions, `code` for a mechanical rule, `manual` for a human. A classification.",
            "examples": [
              "judge",
              "code",
              "manual"
            ],
            "title": "CheckedBy",
            "type": "string",
            "x-classification": true
          },
          "Claim": {
            "additionalProperties": false,
            "description": "What this issue asserts, and what checked it. A replay matching a failure marker at a step does not say the marker was the thing the title is about, and in practice it often is not; since 0.5 that match is `verification.status: marker_matched`, and `verified` is reserved for a claim a second check (`check`) found supported by a verbatim quote from the cited steps and whose failure is the product's (`attribution`). An issue with no `claim` block, or with none of `verification`, is `unverified`: never read the absence of this object as a confirmation.",
            "properties": {
              "action_step": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/ActionStep"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "expected": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Expected"
              },
              "observed": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Observed"
              },
              "verification": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/Verification"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "attribution": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/Attribution"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "check": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/Check"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              }
            },
            "title": "Claim",
            "type": "object"
          },
          "Contradiction": {
            "additionalProperties": false,
            "description": "Populated for type=contradiction: the two statements/surfaces that conflict, each pinned to a step.",
            "properties": {
              "a": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/A"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "b": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/B"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              }
            },
            "title": "Contradiction",
            "type": "object"
          },
          "DecidedBy": {
            "description": "`code`: a mechanical comparison decided it (the credential check). `judge`: the observer's attribution model call under its own instructions decided it. A classification.",
            "examples": [
              "code",
              "judge"
            ],
            "title": "DecidedBy",
            "type": "string",
            "x-classification": true
          },
          "Evidence1": {
            "additionalProperties": false,
            "description": "What the issue rests on: the steps where sessions met it (`step_refs`) and, when the run kept them, screenshots, a clip of the session's video, a snapshot of the page, and the network requests and console messages around it.",
            "properties": {
              "step_refs": {
                "items": {
                  "$ref": "#/$defs/StepRef"
                },
                "minItems": 1,
                "title": "Step Refs",
                "type": "array"
              },
              "screenshots": {
                "anyOf": [
                  {
                    "items": {
                      "format": "uri",
                      "minLength": 1,
                      "type": "string"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Screenshots"
              },
              "video": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/Video"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "dom_snapshot": {
                "anyOf": [
                  {
                    "format": "uri",
                    "minLength": 1,
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Dom Snapshot"
              },
              "network": {
                "anyOf": [
                  {
                    "items": {
                      "additionalProperties": true,
                      "type": "object"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Network"
              },
              "console": {
                "anyOf": [
                  {
                    "items": {
                      "additionalProperties": true,
                      "type": "object"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Console"
              }
            },
            "required": [
              "step_refs"
            ],
            "title": "Evidence1",
            "type": "object"
          },
          "EvidenceRefs": {
            "additionalProperties": false,
            "description": "The store URIs on one issue, gathered where a consumer that wants to *fetch* evidence looks for them.\n\n`screenshots` are what the MCP `get_screenshot` tool accepts. The other two are not - it serves images\nonly - and are fetched over `GET /api/v1/blobs?ref=` with the same key, which is why they are named here\nrather than left for a caller to dig out of `issue.evidence`.",
            "properties": {
              "screenshots": {
                "items": {
                  "type": "string"
                },
                "title": "Screenshots",
                "type": "array"
              },
              "dom_snapshot": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Dom Snapshot"
              },
              "video": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Video"
              }
            },
            "title": "EvidenceRefs",
            "type": "object"
          },
          "Issue": {
            "additionalProperties": false,
            "description": "A product defect the run found: what the product did wrong, where, in how many of the sessions that got there, with the evidence and the steps that reproduce it. `claim.verification.status` says what checked it, and only a `verified` issue - `reproducible: true` - may be filed as a ticket on its own. `description` is objective; `persona_rationale` is the persona's voice.",
            "properties": {
              "id": {
                "title": "Id",
                "type": "string"
              },
              "type": {
                "$ref": "#/$defs/Type"
              },
              "severity": {
                "$ref": "#/$defs/Severity"
              },
              "reproducible": {
                "title": "Reproducible",
                "type": "boolean"
              },
              "reproduction": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/Reproduction1"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "persona_specific": {
                "anyOf": [
                  {
                    "type": "boolean"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Persona Specific"
              },
              "claim": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/Claim"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "tester_error": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/TesterError"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "affected_sessions": {
                "items": {
                  "type": "string"
                },
                "title": "Affected Sessions",
                "type": "array"
              },
              "affected_personas": {
                "anyOf": [
                  {
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Affected Personas"
              },
              "affected_count": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Affected Count"
              },
              "affected_fraction": {
                "anyOf": [
                  {
                    "maximum": 1.0,
                    "minimum": 0.0,
                    "type": "number"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Affected Fraction"
              },
              "reached_sessions": {
                "anyOf": [
                  {
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Reached Sessions"
              },
              "first_seen_step": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "First Seen Step"
              },
              "location": {
                "$ref": "#/$defs/Location1"
              },
              "title": {
                "anyOf": [
                  {
                    "maxLength": 120,
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Title"
              },
              "description": {
                "title": "Description",
                "type": "string"
              },
              "persona_rationale": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Persona Rationale"
              },
              "contradiction": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/Contradiction"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "evidence": {
                "$ref": "#/$defs/Evidence1"
              },
              "repro_steps": {
                "anyOf": [
                  {
                    "items": {
                      "$ref": "#/$defs/ReproStep"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Repro Steps"
              },
              "preconditions": {
                "anyOf": [
                  {
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Preconditions"
              },
              "baseline_status": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/BaselineStatus"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "confidence": {
                "maximum": 1.0,
                "minimum": 0.0,
                "title": "Confidence",
                "type": "number"
              },
              "suggested_owner": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Suggested Owner"
              },
              "connector_refs": {
                "anyOf": [
                  {
                    "additionalProperties": {
                      "type": "string"
                    },
                    "type": "object"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Connector Refs"
              }
            },
            "required": [
              "id",
              "type",
              "severity",
              "reproducible",
              "affected_sessions",
              "location",
              "description",
              "evidence",
              "confidence"
            ],
            "title": "Issue",
            "type": "object"
          },
          "IssueReading": {
            "additionalProperties": false,
            "description": "How one issue of `IssueList.issues` may be acted on: whether it may be filed as a ticket on its own, and\nwhich of its fields are the persona's voice rather than observed behaviour.",
            "properties": {
              "issue_id": {
                "title": "Issue Id",
                "type": "string"
              },
              "may_file_ticket_alone": {
                "description": "True only when reproduction cleared this issue to be filed as a confirmed bug on its own. False means\nreport it with its evidence and mark it unconfirmed.",
                "title": "May File Ticket Alone",
                "type": "boolean"
              },
              "subjective_fields": {
                "description": "Fields on the issue that are the persona's voice rather than observed behaviour. Quote, never state.",
                "items": {
                  "type": "string"
                },
                "title": "Subjective Fields",
                "type": "array"
              },
              "advisory": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "How to read those fields, present only when there are any.",
                "title": "Advisory"
              }
            },
            "required": [
              "issue_id",
              "may_file_ticket_alone",
              "subjective_fields"
            ],
            "title": "IssueReading",
            "type": "object"
          },
          "Kind1": {
            "description": "`mistyped_credential`: typed an email or password that differs from the one the account gave it (decided by code, comparing the typed text with the account; no secret enters a prompt or this document). `mistyped_input`: typed something other than what the brief or the screen asked for. `wrong_element`: acted on an element other than the one the title is about, or a non-interactive one. `placeholder_as_value`: read placeholder text as a pre-filled value and tried to clear or fix it. `ignored_instruction`: skipped something the screen visibly asked for first. `other`: something else the persona did that explains it. Not a tester error, by rule: a persona misreading the product's intent when a reasonable customer would misread it the same way - that is `confusion`, and it is the product's. A classification: a later kind is additive.",
            "examples": [
              "mistyped_credential",
              "mistyped_input",
              "wrong_element",
              "placeholder_as_value",
              "ignored_instruction",
              "other"
            ],
            "title": "Kind1",
            "type": "string",
            "x-classification": true
          },
          "Location1": {
            "additionalProperties": false,
            "description": "Where the defect is: the page (`route`), the element in words (`element_description`) or as a selector (`selector`), and the screen size it was seen at (`viewport`, WIDTHxHEIGHT).",
            "properties": {
              "route": {
                "title": "Route",
                "type": "string"
              },
              "selector": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Selector"
              },
              "element_description": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Element Description"
              },
              "viewport": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Viewport"
              }
            },
            "required": [
              "route"
            ],
            "title": "Location1",
            "type": "object"
          },
          "Method": {
            "description": "Which check ran, when one did; absent when `status` is `unverified`. `marker`: a replay compared the failure marker at `action_step` - name it in `markers`. A marker matching is evidence the step failed again, not that it failed for the reason the title gives, so on its own it produces `marker_matched`, and `verified` only with `check.verdict: supported` and `attribution: product`. `assertion`: a state assertion checked the claim itself (reserved; not produced yet). `manual`: a human checked it. A classification: nothing acts on it - every surface shows `status` - so a reader on an older release buckets an instrument it does not know as `unknown`, and adding one is additive.",
            "examples": [
              "marker",
              "assertion",
              "manual"
            ],
            "title": "Method",
            "type": "string",
            "x-classification": true
          },
          "QuoteStep": {
            "additionalProperties": false,
            "description": "The step whose ledger block the quote came from.",
            "properties": {
              "session_id": {
                "title": "Session Id",
                "type": "string"
              },
              "step_id": {
                "title": "Step Id",
                "type": "integer"
              }
            },
            "required": [
              "session_id",
              "step_id"
            ],
            "title": "QuoteStep",
            "type": "object"
          },
          "ReproStep": {
            "description": "One step of a replay a person or a coding agent can run without the persona, in order from an `open` of the entry page. `action` is a browser action such as `click`, `type`, `key`, `scroll`, `back`, `reload`, `upload` or `wait`; `target` is what it acts on (the address for `open`, `x,y` screen coordinates for a `click`); `value` is what it types, presses or scrolls by; `expect` is what should hold after it, such as the address the page should be on.",
            "properties": {
              "action": {
                "title": "Action",
                "type": "string"
              },
              "target": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Target"
              },
              "value": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Value"
              },
              "replace": {
                "anyOf": [
                  {
                    "type": "boolean"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Replace"
              },
              "expect": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Expect"
              }
            },
            "required": [
              "action"
            ],
            "title": "ReproStep",
            "type": "object"
          },
          "Reproduction1": {
            "additionalProperties": false,
            "description": "Whether Saram replayed the issue without the persona, and what the replay showed: whether one was `attempted`, its `result`, how many `attempts` it took, and `notes` on it. A replay that saw the failure again is not on its own enough to file a ticket; `reproducible` says whether this issue may be.",
            "properties": {
              "attempted": {
                "anyOf": [
                  {
                    "type": "boolean"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Attempted"
              },
              "result": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/Result1"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "attempts": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Attempts"
              },
              "notes": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Notes"
              }
            },
            "title": "Reproduction1",
            "type": "object"
          },
          "Result1": {
            "description": "What the replay showed. `reproduced`: the failure happened again. `not_reproduced`: it did not. `flaky`: the attempts disagreed. `skipped`: no replay ran. More values may be added; read one you do not know as unknown.",
            "examples": [
              "reproduced",
              "not_reproduced",
              "flaky",
              "skipped"
            ],
            "title": "Result1",
            "type": "string",
            "x-classification": true
          },
          "Severity": {
            "description": "How much the defect costs the people who meet it, from least to most: `low`, `medium`, `high`, `critical`. Rated by the judge from the sessions, and lowered one step when the replay did not reproduce the issue and it stands as persona-specific friction. The GitHub connector files only issues at or above its `min_severity_for_issue`.",
            "enum": [
              "low",
              "medium",
              "high",
              "critical"
            ],
            "title": "Severity",
            "type": "string"
          },
          "Status": {
            "description": "Best-supported first. `verified`: a replay matched the failure marker at `action_step` AND the claim check answered `supported` with a verbatim quote from the cited steps (`check`) AND the failure is attributed to the product (`attribution`) - or `method` is `assertion` or `manual` and the same two conditions hold. This and only this may file a ticket alone, and `reproducible` is true exactly when this reads `verified`. `marker_matched` (0.5): a replay matched the failure marker at `action_step`; the claim as titled is unchecked, unsupported, or the check could not quote. The step failed again; whether it failed for the stated reason is not established, so it never files alone. `inconclusive`: a check ran and settled nothing (the marker did not appear again, the two attempts disagreed) - not a refutation. `unverified`: nothing checked it, because there was nothing to check, reproduction never ruled, or the finding is a tester error. A wrong total or a select that resets is `unverified` by construction, not disproven. Default and safe reading when the block is absent. This enum drives an action - it is the ticket-filing gate, like `issue.severity` - so it stays closed for readers too and adding a member is a breaking change: a gate must refuse a value it cannot interpret rather than guess at one, which is why `marker_matched` made 0.5 a new family.",
            "enum": [
              "verified",
              "marker_matched",
              "inconclusive",
              "unverified"
            ],
            "title": "Status",
            "type": "string"
          },
          "StepRef": {
            "description": "One step of one session: the session's id and the step's number within it. A finding points at the steps it rests on, and that session's step events say what happened at each.",
            "properties": {
              "session_id": {
                "title": "Session Id",
                "type": "string"
              },
              "step_id": {
                "title": "Step Id",
                "type": "integer"
              }
            },
            "required": [
              "session_id",
              "step_id"
            ],
            "title": "StepRef",
            "type": "object"
          },
          "TesterError": {
            "additionalProperties": false,
            "description": "What the persona did that explains this failure. Present exactly when `claim.attribution` is `tester`, and such an issue is in `tester_errors[]`, never in `issues[]`. The quote is the evidence line that shows the persona's action - the input echo, the step head with its args, a diary line, a browser line - copied exactly from the ledger and gated the same way a diary quote is; a `tester` answer whose quote does not gate falls back to `undetermined` and this block is not written.",
            "properties": {
              "kind": {
                "$ref": "#/$defs/Kind1"
              },
              "quote": {
                "title": "Quote",
                "type": "string"
              },
              "quote_step": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/QuoteStep"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "decided_by": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/DecidedBy"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "reason": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Reason"
              }
            },
            "required": [
              "kind",
              "quote"
            ],
            "title": "TesterError",
            "type": "object"
          },
          "Type": {
            "description": "Defect classes only. A moment that worked well is an opportunity of kind `delight`, never an issue: it has no failure for reproduction to replay.",
            "examples": [
              "blocker",
              "friction",
              "confusion",
              "bug",
              "contradiction"
            ],
            "title": "Type",
            "type": "string",
            "x-classification": true
          },
          "Verdict1": {
            "description": "`supported`: the checker answered yes with a quote that gates against the ledger text of the cited steps - the only verdict that lets `status` read `verified`. `unsupported`: the checker answered no; the title claims more than the quoted line shows. `ungrounded`: the checker answered yes but its quote is not in the ledger, so the answer is discarded the way an ungrounded diary quote is and recorded in the gate ledger. `skipped`: no marker matched, or the check did not run. A classification: a later verdict is additive.",
            "examples": [
              "supported",
              "unsupported",
              "ungrounded",
              "skipped"
            ],
            "title": "Verdict1",
            "type": "string",
            "x-classification": true
          },
          "Verification": {
            "additionalProperties": false,
            "description": "Whether the claim was checked, and by what. The two are separate questions and were conflated once already: `status` is the answer a reader acts on, `method` only says which instrument produced it. Consumers show `status`.",
            "properties": {
              "status": {
                "$ref": "#/$defs/Status"
              },
              "method": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/Method"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "markers": {
                "anyOf": [
                  {
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Markers"
              },
              "note": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Note"
              }
            },
            "required": [
              "status"
            ],
            "title": "Verification",
            "type": "object"
          },
          "Video": {
            "description": "A clip of a session's video that shows the issue: where the video is stored (`ref`), and where the clip starts and ends, in seconds from the start of the recording.",
            "properties": {
              "ref": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Ref"
              },
              "ts_start_s": {
                "anyOf": [
                  {
                    "type": "number"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Ts Start S"
              },
              "ts_end_s": {
                "anyOf": [
                  {
                    "type": "number"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Ts End S"
              }
            },
            "title": "Video",
            "type": "object"
          }
        },
        "additionalProperties": false,
        "description": "One issue with its reading and its evidence refs beside it: `GET /runs/{id}/issues/{issue_id}` and the MCP\n`get_issue` answer.\n\n`issue` is the contract object verbatim and `reading` is the entry `IssueList.reading[]` carries for the same\nissue, computed by the same function, so a surface that shows one issue - an issue page, or an agent that\ncalls `get_issue` first - reads whether it may be filed alone, and how to read its persona-voiced fields,\nwithout a second call and cannot disagree with the list about them.",
        "properties": {
          "run_id": {
            "title": "Run Id",
            "type": "string"
          },
          "issue": {
            "$ref": "#/$defs/Issue"
          },
          "reading": {
            "$ref": "#/$defs/IssueReading"
          },
          "evidence_refs": {
            "$ref": "#/$defs/EvidenceRefs"
          }
        },
        "required": [
          "run_id",
          "issue",
          "reading",
          "evidence_refs"
        ],
        "title": "IssueDetail",
        "type": "object"
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "get_verdicts",
      "description": "ADVISORY. Each persona's own verdict at the end of its session, with the diary quotes it is grounded in, plus the cohort rollup. Subjective feedback: attribute it to the persona and never file it as a defect. Each row names the question its verdict answers (verdict_prompt) and whose it was (verdict_prompt_source: the run's mission or the persona pack); a row with neither had no question in play. A row with no verdict says why when there is a reason: verdict_discarded is an answer the judge gave that could not be grounded in the persona's diary, so it was thrown away - never the persona's verdict and never counted, which is why the rollup has the session undecided - and observer_error is a session no judge read. About 600 characters a session.",
      "inputSchema": {
        "properties": {
          "run_id": {
            "description": "Run id.",
            "title": "Run Id",
            "type": "string"
          }
        },
        "required": [
          "run_id"
        ],
        "title": "get_verdictsArguments",
        "type": "object"
      },
      "outputSchema": {
        "$defs": {
          "Decision": {
            "description": "Would keep paying / would recommend / would text 'it works' = positive",
            "enum": [
              "positive",
              "negative",
              "undecided"
            ],
            "title": "Decision",
            "type": "string"
          },
          "JudgeDecision": {
            "description": "The decision the judge returned, before the gate discarded it. The judge's and not the persona's: no diary line at the steps it cites supports it. Named apart from `verdict.decision` so that nothing reading a row by field name takes it for the persona's.",
            "enum": [
              "positive",
              "negative",
              "undecided"
            ],
            "title": "JudgeDecision",
            "type": "string"
          },
          "Verdict": {
            "additionalProperties": false,
            "description": "SUBJECTIVE. The persona's answer at session end to the run's verdict prompt - the run spec's `mission.verdict_prompt` when it has one, `persona.verdict_prompt` otherwise - grounded in diary quotes. A persona pack's question is about the product the pack was written for, so a run that points a pack at another target sets the mission's and the pack's is not applicable for that run. Which of the two this answers, and its text, are on the row: `outcomes[].verdict_prompt_source` and `outcomes[].verdict_prompt`, which sit there rather than here so that a session with no verdict still names the question that went unanswered. Consumers must tag it as subjective. `decision_text`, `reason` and `quotes` are quotation, verbatim from the diary at `step_refs`; `reason_paraphrase` is the only field written in the judge's voice, and a consumer that shows it must label it as the judge's, never as the persona's words.",
            "properties": {
              "decision": {
                "$ref": "#/$defs/Decision"
              },
              "decision_text": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Decision Text"
              },
              "reason": {
                "title": "Reason",
                "type": "string"
              },
              "reason_paraphrase": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Reason Paraphrase"
              },
              "quotes": {
                "anyOf": [
                  {
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Quotes"
              },
              "step_refs": {
                "items": {
                  "type": "integer"
                },
                "minItems": 1,
                "title": "Step Refs",
                "type": "array"
              }
            },
            "required": [
              "decision",
              "reason",
              "step_refs"
            ],
            "title": "Verdict",
            "type": "object"
          },
          "VerdictDiscarded": {
            "additionalProperties": false,
            "description": "NOT A VERDICT. The answer a judge gave about this session that the verbatim-quote gate threw away because it could not ground itself in the persona's words: none of its quotes is in the diary at the steps it cites, or none of the steps it cites is a step of the session. Present only on a row a judge read that has no `verdict`, so never beside `verdict` or `observer_error`. It is what tells a judge that answered and was not grounded apart from a persona who gave no answer: both rows carry no `verdict` and both count `undecided` in `summary.verdicts`, which counts grounded verdicts alone, and only this one says an answer was given. The decision is the judge's reading with nothing in the diary to support it, so a consumer never shows it as the persona's verdict, never counts it and never acts on it; what it points a reader at is the judge's citations. Nothing the judge quoted is carried here, because none of it is verifiably the persona's: the records, the dropped quotes among them, are in the session's gate ledger beside its observation, and the discard is counted in `summary.gate_discards`. Absent on a row with no `verdict` means nothing was discarded: the judge returned no answer, no judge read the session (`observer_error`), or no question was in play (`verdict_prompt_source` absent). An artifact written before 0.6.3 carries none either, and `contracts_version` is what tells those apart.",
            "properties": {
              "judge_decision": {
                "$ref": "#/$defs/JudgeDecision"
              },
              "gate_reason": {
                "title": "Gate Reason",
                "type": "string"
              }
            },
            "required": [
              "judge_decision",
              "gate_reason"
            ],
            "title": "VerdictDiscarded",
            "type": "object"
          },
          "VerdictPromptSource": {
            "description": "Whose question `verdict` answers: the run spec's `mission.verdict_prompt`, or the persona pack's `persona.verdict_prompt` when the mission carried none. `mission` is also the record that the pack's own question was not applicable for this run, which is what makes an absent `verdict` beside it readable: the diary did not answer a question about the product actually under test, which is a run-spec problem, not a judge that could not ground itself (that one names its discarded answer in `verdict_discarded`). Absent means no verdict question was in play: neither the mission nor the pack carried one, so `verdict` is absent by construction and `summary.verdicts` counts the session `undecided` without that being anything the judge failed at. An artifact written before this release carries none either, and `contracts_version` is what tells the two apart. Absent beside `observer_error` is neither: no judge ran, so nobody resolved which question was in play.",
            "examples": [
              "mission",
              "persona"
            ],
            "title": "VerdictPromptSource",
            "type": "string",
            "x-classification": true
          },
          "VerdictRow": {
            "additionalProperties": false,
            "description": "SUBJECTIVE: one session's verdict as the artifact's `outcomes[]` carries it. Persona feedback, never a\nticket on its own.",
            "properties": {
              "session_id": {
                "title": "Session Id",
                "type": "string"
              },
              "persona_id": {
                "title": "Persona Id",
                "type": "string"
              },
              "mission_id": {
                "title": "Mission Id",
                "type": "string"
              },
              "result": {
                "title": "Result",
                "type": "string"
              },
              "verdict_prompt": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "The verdict question this session was asked, verbatim: the one `verdict` answers. Absent when no question\nwas in play for the session, and on every row of an artifact written before the outcome carried it (0.4.1).",
                "title": "Verdict Prompt"
              },
              "verdict_prompt_source": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/VerdictPromptSource"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "Whose question `verdict` answers: the run spec's `mission.verdict_prompt`, or the persona pack's when the\nmission carried none. Present exactly when `verdict_prompt` is; `mission` beside an absent\n`verdict` is a persona who was asked the run's question and did not answer it - unless `verdict_discarded`\nsays the judge answered for them and could not ground the answer."
              },
              "verdict": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/Verdict"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "verdict_discarded": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/VerdictDiscarded"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "NOT A VERDICT. The answer the judge gave that the quote gate threw away because none of it could be grounded in\nthe persona's diary at the steps it cited, and why (`artifact.outcomes[].verdict_discarded`). Set only on a row\nwith no `verdict`; absent there means nothing was discarded - the judge returned no answer, or no judge read the\nsession (`observer_error`). Never show `judge_decision` as the persona's verdict and never count it: the cohort's\n`summary` counts this session `undecided`. Absent on every row of an artifact written before contracts 0.6.3."
              },
              "observer_error": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "Why no judge read this session, when none did, in Saram's words (`artifact.outcomes[].observer_error`): its\njudge pass failed after its retries, so the absent `verdict` is a question nobody asked a model, not one the\npersona left unanswered. The model provider's status and own message when the provider refused, the `providers`\nentry's refusal when it did not resolve, Saram's sentence otherwise. Absent on every session a judge read, and\non every row of an artifact written before contracts 0.6.1; one written before 0.6.3 carries the failure as it\nwas recorded.",
                "title": "Observer Error"
              }
            },
            "required": [
              "session_id",
              "persona_id",
              "mission_id",
              "result"
            ],
            "title": "VerdictRow",
            "type": "object"
          },
          "Verdicts": {
            "additionalProperties": false,
            "description": "Cohort-level read of persona verdicts. Subjective, derived from outcomes[].verdict. positive + negative + undecided = sessions: a session with no grounded verdict is undecided, not absent. That includes a session whose judge answered and had the answer discarded by the quote gate: its row names it in `outcomes[].verdict_discarded`, and it is still counted here as undecided, because this tally counts only verdicts grounded in the persona's words.",
            "properties": {
              "positive": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Positive"
              },
              "negative": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Negative"
              },
              "undecided": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Undecided"
              },
              "top_reasons": {
                "anyOf": [
                  {
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Top Reasons"
              }
            },
            "title": "Verdicts",
            "type": "object"
          }
        },
        "additionalProperties": false,
        "description": "A run's persona verdicts: each session's answer to the run's verdict question, grounded in the persona's\nown diary quotes, and the cohort's tally. Persona feedback, never a ticket on its own.",
        "properties": {
          "run_id": {
            "title": "Run Id",
            "type": "string"
          },
          "status": {
            "description": "The run's status. An empty `verdicts` is only a result once this is terminal.",
            "title": "Status",
            "type": "string"
          },
          "summary": {
            "anyOf": [
              {
                "$ref": "#/$defs/Verdicts"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "The artifact's cohort read (`summary.verdicts`)."
          },
          "verdicts": {
            "items": {
              "$ref": "#/$defs/VerdictRow"
            },
            "title": "Verdicts",
            "type": "array"
          },
          "advisory": {
            "description": "How to read this whole answer: persona feedback, not defects. Never a ticket on its own.",
            "title": "Advisory",
            "type": "string"
          },
          "subjective_fields": {
            "description": "Fields on each row that are the persona's opinion - for a verdict, the verdict itself.",
            "items": {
              "type": "string"
            },
            "title": "Subjective Fields",
            "type": "array"
          }
        },
        "required": [
          "run_id",
          "status",
          "verdicts",
          "advisory",
          "subjective_fields"
        ],
        "title": "VerdictList",
        "type": "object"
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "get_opportunities",
      "description": "ADVISORY. Behaviour-grounded persona feedback that is not a defect: unmet expectations, missing capabilities, value and trust gaps, delight. Scored by consensus (the fraction of sessions that reached the area and raised it), never by reproduction, and never a ticket on their own. Returned in display order; `display.shown` are the records that clear the cohort floors (40% consensus across at least 2 sessions, or solicited by the mission) and `display.folded` are single-persona notes - the same split GitHub and the dashboard show. About 1,300 characters an opportunity; min_consensus narrows a long list.",
      "inputSchema": {
        "properties": {
          "run_id": {
            "description": "Run id.",
            "title": "Run Id",
            "type": "string"
          },
          "min_consensus": {
            "default": 0.0,
            "description": "Lowest consensus to return, 0 to 1. 0.5 means half the sessions raised it.",
            "maximum": 1,
            "minimum": 0,
            "title": "Min Consensus",
            "type": "number"
          }
        },
        "required": [
          "run_id"
        ],
        "title": "get_opportunitiesArguments",
        "type": "object"
      },
      "outputSchema": {
        "$defs": {
          "BaselineStatus": {
            "description": "How this finding compares with the run named as the baseline. `new`: the baseline reached the same page and did not have it (for an issue, only one that is `reproducible`). `persisting`: the baseline has it too. `unknown`: nothing could say - no baseline was compared, the baseline never reached the page, or an issue the baseline does not have was not verified. `resolved` is never stamped on a finding of this run: a baseline issue this run no longer has is counted in `summary.issues_resolved_vs_baseline`. More values may be added; read one you do not know as `unknown`.",
            "examples": [
              "new",
              "persisting",
              "resolved",
              "unknown"
            ],
            "title": "BaselineStatus",
            "type": "string",
            "x-classification": true
          },
          "DisplaySplit": {
            "additionalProperties": false,
            "description": "The cohort floors, and which of the run's opportunities clear them.\n\nThe split is the whole run's, so every surface - here, on GitHub, in the MCP answer - leads with the same\nrecord. Nothing is dropped: `folded` is a single-persona note, one click away.",
            "properties": {
              "note": {
                "title": "Note",
                "type": "string"
              },
              "min_consensus": {
                "title": "Min Consensus",
                "type": "number"
              },
              "min_affected_sessions": {
                "title": "Min Affected Sessions",
                "type": "integer"
              },
              "strong_consensus": {
                "title": "Strong Consensus",
                "type": "number"
              },
              "shown": {
                "items": {
                  "type": "string"
                },
                "title": "Shown",
                "type": "array"
              },
              "folded": {
                "items": {
                  "type": "string"
                },
                "title": "Folded",
                "type": "array"
              }
            },
            "required": [
              "note",
              "min_consensus",
              "min_affected_sessions",
              "strong_consensus",
              "shown",
              "folded"
            ],
            "title": "DisplaySplit",
            "type": "object"
          },
          "Evidence": {
            "additionalProperties": false,
            "description": "What the feedback rests on: the steps where personas raised it (`step_refs`), lines from their diaries at those steps, each checked to appear there verbatim (`quotes`), and screenshots of what they were looking at.",
            "properties": {
              "step_refs": {
                "items": {
                  "$ref": "#/$defs/StepRef"
                },
                "minItems": 1,
                "title": "Step Refs",
                "type": "array"
              },
              "quotes": {
                "anyOf": [
                  {
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Quotes"
              },
              "screenshots": {
                "anyOf": [
                  {
                    "items": {
                      "format": "uri",
                      "minLength": 1,
                      "type": "string"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Screenshots"
              }
            },
            "required": [
              "step_refs"
            ],
            "title": "Evidence",
            "type": "object"
          },
          "Kind": {
            "description": "What sort of feedback this is. `unmet_expectation`: the product did not do what the personas expected of it. `missing_capability`: something they looked for is not there. `value_gap`: they judged what it offers not worth what it asks of them. `trust_gap`: something made them hesitate to trust it. `delight`: a moment that worked well. More kinds may be added; read one you do not know as unknown.",
            "examples": [
              "unmet_expectation",
              "missing_capability",
              "value_gap",
              "trust_gap",
              "delight"
            ],
            "title": "Kind",
            "type": "string",
            "x-classification": true
          },
          "Location": {
            "description": "Where in the product the feedback is about: the page (`route`) and, when the personas pointed at one, the element in words (`element_description`).",
            "properties": {
              "route": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Route"
              },
              "element_description": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Element Description"
              }
            },
            "title": "Location",
            "type": "object"
          },
          "Opportunity": {
            "additionalProperties": false,
            "description": "Behavior-grounded persona feedback that is not a defect: an unmet expectation, a missing capability, a value or trust gap, or a moment of delight. Scored by consensus across the sessions that reached the area, never by reproduction, and never a ticket on its own. `description` is the objective account; `persona_rationale` and `suggested_change` are opinion.",
            "properties": {
              "id": {
                "title": "Id",
                "type": "string"
              },
              "kind": {
                "$ref": "#/$defs/Kind"
              },
              "title": {
                "anyOf": [
                  {
                    "maxLength": 120,
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Title"
              },
              "description": {
                "title": "Description",
                "type": "string"
              },
              "persona_rationale": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Persona Rationale"
              },
              "suggested_change": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Suggested Change"
              },
              "affected_sessions": {
                "items": {
                  "type": "string"
                },
                "title": "Affected Sessions",
                "type": "array"
              },
              "affected_personas": {
                "anyOf": [
                  {
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Affected Personas"
              },
              "reached_sessions": {
                "anyOf": [
                  {
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Reached Sessions"
              },
              "consensus": {
                "maximum": 1.0,
                "minimum": 0.0,
                "title": "Consensus",
                "type": "number"
              },
              "solicited_by": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Solicited By"
              },
              "location": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/Location"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "evidence": {
                "$ref": "#/$defs/Evidence"
              },
              "baseline_status": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/BaselineStatus"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "connector_refs": {
                "anyOf": [
                  {
                    "additionalProperties": {
                      "type": "string"
                    },
                    "type": "object"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Connector Refs"
              }
            },
            "required": [
              "id",
              "kind",
              "description",
              "affected_sessions",
              "consensus",
              "evidence"
            ],
            "title": "Opportunity",
            "type": "object"
          },
          "StepRef": {
            "description": "One step of one session: the session's id and the step's number within it. A finding points at the steps it rests on, and that session's step events say what happened at each.",
            "properties": {
              "session_id": {
                "title": "Session Id",
                "type": "string"
              },
              "step_id": {
                "title": "Step Id",
                "type": "integer"
              }
            },
            "required": [
              "session_id",
              "step_id"
            ],
            "title": "StepRef",
            "type": "object"
          }
        },
        "additionalProperties": false,
        "description": "A run's opportunities: behaviour-grounded persona feedback that is not a defect - unmet expectations,\nmissing capabilities, value gaps, delight - scored by how many personas share it. Never a ticket on its own.",
        "properties": {
          "run_id": {
            "title": "Run Id",
            "type": "string"
          },
          "status": {
            "description": "The run's status. An empty `opportunities` is only a result once this is terminal.",
            "title": "Status",
            "type": "string"
          },
          "opportunities": {
            "items": {
              "$ref": "#/$defs/Opportunity"
            },
            "title": "Opportunities",
            "type": "array"
          },
          "advisory": {
            "description": "How to read this whole answer: behaviour-grounded persona feedback, consensus-scored, never a ticket on its\nown.",
            "title": "Advisory",
            "type": "string"
          },
          "subjective_fields": {
            "description": "Fields on each opportunity that are opinion. `description` is deliberately not among them.",
            "items": {
              "type": "string"
            },
            "title": "Subjective Fields",
            "type": "array"
          },
          "display": {
            "$ref": "#/$defs/DisplaySplit",
            "description": "The run's shown/folded split, present even when the run raised nothing: the floors are the answer to\n\"why is this list short\", and they are the same whether or not anything cleared them."
          }
        },
        "required": [
          "run_id",
          "status",
          "opportunities",
          "advisory",
          "subjective_fields",
          "display"
        ],
        "title": "OpportunityList",
        "type": "object"
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "get_tester_errors",
      "description": "HARNESS-ATTRIBUTED, NEVER A DEFECT. Findings this run attributed to the persona's own action rather than to the product: a mistyped credential, the wrong element, placeholder text read as a value. They carry the same shape as an issue - same id, same evidence, same screenshots - and they are not issues: not in issues_total, not reproduced, never a ticket. Read them for why a session stalled and for what to fix in the test setup. Each one quotes the recorded line that shows what the persona did. Each record is the whole issue-shaped object; reading[] gives its label, what the persona did in the words the dashboard and the PR comment use. Empty with the run's status until the artifact is assembled; classified=false means the run's artifact predates the classification, not that it found none. About 2,500 characters a record.",
      "inputSchema": {
        "properties": {
          "run_id": {
            "description": "Run id.",
            "title": "Run Id",
            "type": "string"
          }
        },
        "required": [
          "run_id"
        ],
        "title": "get_tester_errorsArguments",
        "type": "object"
      },
      "outputSchema": {
        "$defs": {
          "A": {
            "description": "The first of the two statements that conflict: what the product said (`text`), and the session and step where it said it.",
            "properties": {
              "step_id": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Step Id"
              },
              "session_id": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Session Id"
              },
              "text": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Text"
              }
            },
            "title": "A",
            "type": "object"
          },
          "ActionStep": {
            "additionalProperties": false,
            "description": "The step the claim is about and the replay is judged at - the action that failed, not the look that saw it or the retry after it. Written by reproduction, which picks it (`_judged_path`); absent when nothing replayed this issue.",
            "properties": {
              "session_id": {
                "title": "Session Id",
                "type": "string"
              },
              "step_id": {
                "title": "Step Id",
                "type": "integer"
              }
            },
            "required": [
              "session_id",
              "step_id"
            ],
            "title": "ActionStep",
            "type": "object"
          },
          "Attribution": {
            "description": "Whose failure this is, asked before an issue is a product finding: could the persona's own action explain it? `product`: no - the failure is the product's to own. `tester`: yes - a mistyped credential, the wrong element, placeholder text read as a value; the record moves to `tester_errors[]` with a `tester_error` block, is never a ticket and never counted as an issue. `undetermined`: the harness could not tell (a persona handed stale credentials by the mission author typed exactly what it was told; an attribution call that failed), and a human must; it caps `status` at `marker_matched`. Absent means never asked - every issue before this release - and a reader treats absent as `undetermined`. A classification: nothing acts on it directly, the producer derives `status` from it, so a later member (`setup`) is additive and an older reader buckets it as `unknown`.",
            "examples": [
              "product",
              "tester",
              "undetermined"
            ],
            "title": "Attribution",
            "type": "string",
            "x-classification": true
          },
          "B": {
            "description": "The second of the two statements that conflict: what the product said (`text`), and the session and step where it said it.",
            "properties": {
              "step_id": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Step Id"
              },
              "session_id": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Session Id"
              },
              "text": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Text"
              }
            },
            "title": "B",
            "type": "object"
          },
          "BaselineStatus": {
            "description": "How this finding compares with the run named as the baseline. `new`: the baseline reached the same page and did not have it (for an issue, only one that is `reproducible`). `persisting`: the baseline has it too. `unknown`: nothing could say - no baseline was compared, the baseline never reached the page, or an issue the baseline does not have was not verified. `resolved` is never stamped on a finding of this run: a baseline issue this run no longer has is counted in `summary.issues_resolved_vs_baseline`. More values may be added; read one you do not know as `unknown`.",
            "examples": [
              "new",
              "persisting",
              "resolved",
              "unknown"
            ],
            "title": "BaselineStatus",
            "type": "string",
            "x-classification": true
          },
          "Check": {
            "additionalProperties": false,
            "description": "The claim check: one narrow question asked of the evidence at the cited steps after a replay matched a marker - does this evidence show the product doing what the title says? Descriptive: nothing acts on it directly; the producer derives `status` from `verdict` and `attribution`. Absent when the check never ran, which is every issue before this release and every issue no marker matched.",
            "properties": {
              "verdict": {
                "$ref": "#/$defs/Verdict1"
              },
              "quote": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Quote"
              },
              "quote_step": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/QuoteStep"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "reason": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Reason"
              },
              "checked_by": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/CheckedBy"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              }
            },
            "required": [
              "verdict"
            ],
            "title": "Check",
            "type": "object"
          },
          "CheckedBy": {
            "description": "Which instrument answered: `judge` for the observer's checker model call under its own instructions, `code` for a mechanical rule, `manual` for a human. A classification.",
            "examples": [
              "judge",
              "code",
              "manual"
            ],
            "title": "CheckedBy",
            "type": "string",
            "x-classification": true
          },
          "Claim": {
            "additionalProperties": false,
            "description": "What this issue asserts, and what checked it. A replay matching a failure marker at a step does not say the marker was the thing the title is about, and in practice it often is not; since 0.5 that match is `verification.status: marker_matched`, and `verified` is reserved for a claim a second check (`check`) found supported by a verbatim quote from the cited steps and whose failure is the product's (`attribution`). An issue with no `claim` block, or with none of `verification`, is `unverified`: never read the absence of this object as a confirmation.",
            "properties": {
              "action_step": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/ActionStep"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "expected": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Expected"
              },
              "observed": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Observed"
              },
              "verification": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/Verification"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "attribution": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/Attribution"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "check": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/Check"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              }
            },
            "title": "Claim",
            "type": "object"
          },
          "Contradiction": {
            "additionalProperties": false,
            "description": "Populated for type=contradiction: the two statements/surfaces that conflict, each pinned to a step.",
            "properties": {
              "a": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/A"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "b": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/B"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              }
            },
            "title": "Contradiction",
            "type": "object"
          },
          "DecidedBy": {
            "description": "`code`: a mechanical comparison decided it (the credential check). `judge`: the observer's attribution model call under its own instructions decided it. A classification.",
            "examples": [
              "code",
              "judge"
            ],
            "title": "DecidedBy",
            "type": "string",
            "x-classification": true
          },
          "Evidence1": {
            "additionalProperties": false,
            "description": "What the issue rests on: the steps where sessions met it (`step_refs`) and, when the run kept them, screenshots, a clip of the session's video, a snapshot of the page, and the network requests and console messages around it.",
            "properties": {
              "step_refs": {
                "items": {
                  "$ref": "#/$defs/StepRef"
                },
                "minItems": 1,
                "title": "Step Refs",
                "type": "array"
              },
              "screenshots": {
                "anyOf": [
                  {
                    "items": {
                      "format": "uri",
                      "minLength": 1,
                      "type": "string"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Screenshots"
              },
              "video": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/Video"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "dom_snapshot": {
                "anyOf": [
                  {
                    "format": "uri",
                    "minLength": 1,
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Dom Snapshot"
              },
              "network": {
                "anyOf": [
                  {
                    "items": {
                      "additionalProperties": true,
                      "type": "object"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Network"
              },
              "console": {
                "anyOf": [
                  {
                    "items": {
                      "additionalProperties": true,
                      "type": "object"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Console"
              }
            },
            "required": [
              "step_refs"
            ],
            "title": "Evidence1",
            "type": "object"
          },
          "Issue": {
            "additionalProperties": false,
            "description": "A product defect the run found: what the product did wrong, where, in how many of the sessions that got there, with the evidence and the steps that reproduce it. `claim.verification.status` says what checked it, and only a `verified` issue - `reproducible: true` - may be filed as a ticket on its own. `description` is objective; `persona_rationale` is the persona's voice.",
            "properties": {
              "id": {
                "title": "Id",
                "type": "string"
              },
              "type": {
                "$ref": "#/$defs/Type"
              },
              "severity": {
                "$ref": "#/$defs/Severity"
              },
              "reproducible": {
                "title": "Reproducible",
                "type": "boolean"
              },
              "reproduction": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/Reproduction1"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "persona_specific": {
                "anyOf": [
                  {
                    "type": "boolean"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Persona Specific"
              },
              "claim": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/Claim"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "tester_error": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/TesterError"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "affected_sessions": {
                "items": {
                  "type": "string"
                },
                "title": "Affected Sessions",
                "type": "array"
              },
              "affected_personas": {
                "anyOf": [
                  {
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Affected Personas"
              },
              "affected_count": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Affected Count"
              },
              "affected_fraction": {
                "anyOf": [
                  {
                    "maximum": 1.0,
                    "minimum": 0.0,
                    "type": "number"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Affected Fraction"
              },
              "reached_sessions": {
                "anyOf": [
                  {
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Reached Sessions"
              },
              "first_seen_step": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "First Seen Step"
              },
              "location": {
                "$ref": "#/$defs/Location1"
              },
              "title": {
                "anyOf": [
                  {
                    "maxLength": 120,
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Title"
              },
              "description": {
                "title": "Description",
                "type": "string"
              },
              "persona_rationale": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Persona Rationale"
              },
              "contradiction": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/Contradiction"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "evidence": {
                "$ref": "#/$defs/Evidence1"
              },
              "repro_steps": {
                "anyOf": [
                  {
                    "items": {
                      "$ref": "#/$defs/ReproStep"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Repro Steps"
              },
              "preconditions": {
                "anyOf": [
                  {
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Preconditions"
              },
              "baseline_status": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/BaselineStatus"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "confidence": {
                "maximum": 1.0,
                "minimum": 0.0,
                "title": "Confidence",
                "type": "number"
              },
              "suggested_owner": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Suggested Owner"
              },
              "connector_refs": {
                "anyOf": [
                  {
                    "additionalProperties": {
                      "type": "string"
                    },
                    "type": "object"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Connector Refs"
              }
            },
            "required": [
              "id",
              "type",
              "severity",
              "reproducible",
              "affected_sessions",
              "location",
              "description",
              "evidence",
              "confidence"
            ],
            "title": "Issue",
            "type": "object"
          },
          "Kind1": {
            "description": "`mistyped_credential`: typed an email or password that differs from the one the account gave it (decided by code, comparing the typed text with the account; no secret enters a prompt or this document). `mistyped_input`: typed something other than what the brief or the screen asked for. `wrong_element`: acted on an element other than the one the title is about, or a non-interactive one. `placeholder_as_value`: read placeholder text as a pre-filled value and tried to clear or fix it. `ignored_instruction`: skipped something the screen visibly asked for first. `other`: something else the persona did that explains it. Not a tester error, by rule: a persona misreading the product's intent when a reasonable customer would misread it the same way - that is `confusion`, and it is the product's. A classification: a later kind is additive.",
            "examples": [
              "mistyped_credential",
              "mistyped_input",
              "wrong_element",
              "placeholder_as_value",
              "ignored_instruction",
              "other"
            ],
            "title": "Kind1",
            "type": "string",
            "x-classification": true
          },
          "Location1": {
            "additionalProperties": false,
            "description": "Where the defect is: the page (`route`), the element in words (`element_description`) or as a selector (`selector`), and the screen size it was seen at (`viewport`, WIDTHxHEIGHT).",
            "properties": {
              "route": {
                "title": "Route",
                "type": "string"
              },
              "selector": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Selector"
              },
              "element_description": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Element Description"
              },
              "viewport": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Viewport"
              }
            },
            "required": [
              "route"
            ],
            "title": "Location1",
            "type": "object"
          },
          "Method": {
            "description": "Which check ran, when one did; absent when `status` is `unverified`. `marker`: a replay compared the failure marker at `action_step` - name it in `markers`. A marker matching is evidence the step failed again, not that it failed for the reason the title gives, so on its own it produces `marker_matched`, and `verified` only with `check.verdict: supported` and `attribution: product`. `assertion`: a state assertion checked the claim itself (reserved; not produced yet). `manual`: a human checked it. A classification: nothing acts on it - every surface shows `status` - so a reader on an older release buckets an instrument it does not know as `unknown`, and adding one is additive.",
            "examples": [
              "marker",
              "assertion",
              "manual"
            ],
            "title": "Method",
            "type": "string",
            "x-classification": true
          },
          "QuoteStep": {
            "additionalProperties": false,
            "description": "The step whose ledger block the quote came from.",
            "properties": {
              "session_id": {
                "title": "Session Id",
                "type": "string"
              },
              "step_id": {
                "title": "Step Id",
                "type": "integer"
              }
            },
            "required": [
              "session_id",
              "step_id"
            ],
            "title": "QuoteStep",
            "type": "object"
          },
          "ReproStep": {
            "description": "One step of a replay a person or a coding agent can run without the persona, in order from an `open` of the entry page. `action` is a browser action such as `click`, `type`, `key`, `scroll`, `back`, `reload`, `upload` or `wait`; `target` is what it acts on (the address for `open`, `x,y` screen coordinates for a `click`); `value` is what it types, presses or scrolls by; `expect` is what should hold after it, such as the address the page should be on.",
            "properties": {
              "action": {
                "title": "Action",
                "type": "string"
              },
              "target": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Target"
              },
              "value": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Value"
              },
              "replace": {
                "anyOf": [
                  {
                    "type": "boolean"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Replace"
              },
              "expect": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Expect"
              }
            },
            "required": [
              "action"
            ],
            "title": "ReproStep",
            "type": "object"
          },
          "Reproduction1": {
            "additionalProperties": false,
            "description": "Whether Saram replayed the issue without the persona, and what the replay showed: whether one was `attempted`, its `result`, how many `attempts` it took, and `notes` on it. A replay that saw the failure again is not on its own enough to file a ticket; `reproducible` says whether this issue may be.",
            "properties": {
              "attempted": {
                "anyOf": [
                  {
                    "type": "boolean"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Attempted"
              },
              "result": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/Result1"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "attempts": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Attempts"
              },
              "notes": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Notes"
              }
            },
            "title": "Reproduction1",
            "type": "object"
          },
          "Result1": {
            "description": "What the replay showed. `reproduced`: the failure happened again. `not_reproduced`: it did not. `flaky`: the attempts disagreed. `skipped`: no replay ran. More values may be added; read one you do not know as unknown.",
            "examples": [
              "reproduced",
              "not_reproduced",
              "flaky",
              "skipped"
            ],
            "title": "Result1",
            "type": "string",
            "x-classification": true
          },
          "Severity": {
            "description": "How much the defect costs the people who meet it, from least to most: `low`, `medium`, `high`, `critical`. Rated by the judge from the sessions, and lowered one step when the replay did not reproduce the issue and it stands as persona-specific friction. The GitHub connector files only issues at or above its `min_severity_for_issue`.",
            "enum": [
              "low",
              "medium",
              "high",
              "critical"
            ],
            "title": "Severity",
            "type": "string"
          },
          "Status": {
            "description": "Best-supported first. `verified`: a replay matched the failure marker at `action_step` AND the claim check answered `supported` with a verbatim quote from the cited steps (`check`) AND the failure is attributed to the product (`attribution`) - or `method` is `assertion` or `manual` and the same two conditions hold. This and only this may file a ticket alone, and `reproducible` is true exactly when this reads `verified`. `marker_matched` (0.5): a replay matched the failure marker at `action_step`; the claim as titled is unchecked, unsupported, or the check could not quote. The step failed again; whether it failed for the stated reason is not established, so it never files alone. `inconclusive`: a check ran and settled nothing (the marker did not appear again, the two attempts disagreed) - not a refutation. `unverified`: nothing checked it, because there was nothing to check, reproduction never ruled, or the finding is a tester error. A wrong total or a select that resets is `unverified` by construction, not disproven. Default and safe reading when the block is absent. This enum drives an action - it is the ticket-filing gate, like `issue.severity` - so it stays closed for readers too and adding a member is a breaking change: a gate must refuse a value it cannot interpret rather than guess at one, which is why `marker_matched` made 0.5 a new family.",
            "enum": [
              "verified",
              "marker_matched",
              "inconclusive",
              "unverified"
            ],
            "title": "Status",
            "type": "string"
          },
          "StepRef": {
            "description": "One step of one session: the session's id and the step's number within it. A finding points at the steps it rests on, and that session's step events say what happened at each.",
            "properties": {
              "session_id": {
                "title": "Session Id",
                "type": "string"
              },
              "step_id": {
                "title": "Step Id",
                "type": "integer"
              }
            },
            "required": [
              "session_id",
              "step_id"
            ],
            "title": "StepRef",
            "type": "object"
          },
          "TesterError": {
            "additionalProperties": false,
            "description": "What the persona did that explains this failure. Present exactly when `claim.attribution` is `tester`, and such an issue is in `tester_errors[]`, never in `issues[]`. The quote is the evidence line that shows the persona's action - the input echo, the step head with its args, a diary line, a browser line - copied exactly from the ledger and gated the same way a diary quote is; a `tester` answer whose quote does not gate falls back to `undetermined` and this block is not written.",
            "properties": {
              "kind": {
                "$ref": "#/$defs/Kind1"
              },
              "quote": {
                "title": "Quote",
                "type": "string"
              },
              "quote_step": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/QuoteStep"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "decided_by": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/DecidedBy"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "reason": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Reason"
              }
            },
            "required": [
              "kind",
              "quote"
            ],
            "title": "TesterError",
            "type": "object"
          },
          "TesterErrorReading": {
            "additionalProperties": false,
            "description": "How one record of `TesterErrorList.tester_errors` reads: what the persona did, and that it is never a\nticket.",
            "properties": {
              "tester_error_id": {
                "title": "Tester Error Id",
                "type": "string"
              },
              "label": {
                "description": "What the persona did, in the words every surface uses: the sentence the local MCP server, the GitHub PR\ncomment and the dashboard all show for this kind.",
                "title": "Label",
                "type": "string"
              },
              "may_file_ticket_alone": {
                "const": false,
                "default": false,
                "description": "False for every record in this array, by construction. Stated rather than left to be inferred from an\nabsent field, because a reader that has to work it out is a reader that might not.",
                "title": "May File Ticket Alone",
                "type": "boolean"
              }
            },
            "required": [
              "tester_error_id",
              "label"
            ],
            "title": "TesterErrorReading",
            "type": "object"
          },
          "Type": {
            "description": "Defect classes only. A moment that worked well is an opportunity of kind `delight`, never an issue: it has no failure for reproduction to replay.",
            "examples": [
              "blocker",
              "friction",
              "confusion",
              "bug",
              "contradiction"
            ],
            "title": "Type",
            "type": "string",
            "x-classification": true
          },
          "Verdict1": {
            "description": "`supported`: the checker answered yes with a quote that gates against the ledger text of the cited steps - the only verdict that lets `status` read `verified`. `unsupported`: the checker answered no; the title claims more than the quoted line shows. `ungrounded`: the checker answered yes but its quote is not in the ledger, so the answer is discarded the way an ungrounded diary quote is and recorded in the gate ledger. `skipped`: no marker matched, or the check did not run. A classification: a later verdict is additive.",
            "examples": [
              "supported",
              "unsupported",
              "ungrounded",
              "skipped"
            ],
            "title": "Verdict1",
            "type": "string",
            "x-classification": true
          },
          "Verification": {
            "additionalProperties": false,
            "description": "Whether the claim was checked, and by what. The two are separate questions and were conflated once already: `status` is the answer a reader acts on, `method` only says which instrument produced it. Consumers show `status`.",
            "properties": {
              "status": {
                "$ref": "#/$defs/Status"
              },
              "method": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/Method"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "markers": {
                "anyOf": [
                  {
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Markers"
              },
              "note": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Note"
              }
            },
            "required": [
              "status"
            ],
            "title": "Verification",
            "type": "object"
          },
          "Video": {
            "description": "A clip of a session's video that shows the issue: where the video is stored (`ref`), and where the clip starts and ends, in seconds from the start of the recording.",
            "properties": {
              "ref": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Ref"
              },
              "ts_start_s": {
                "anyOf": [
                  {
                    "type": "number"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Ts Start S"
              },
              "ts_end_s": {
                "anyOf": [
                  {
                    "type": "number"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Ts End S"
              }
            },
            "title": "Video",
            "type": "object"
          }
        },
        "additionalProperties": false,
        "description": "The run's tester errors: findings the harness attributed to its own persona, never to the product.\n\nIssue-shaped - the same id, evidence and screenshots an issue carries - and never an issue: not in\n`IssueList`, not in `issues_total`, not reproduced, never a ticket. They are served so a customer can see\nwhy a session stalled on their own test setup (a mistyped credential, the wrong element) instead of reading\nthe stall as the product's.",
        "properties": {
          "run_id": {
            "title": "Run Id",
            "type": "string"
          },
          "status": {
            "description": "The run's status. An empty `tester_errors` is only a result once this is terminal.",
            "title": "Status",
            "type": "string"
          },
          "classified": {
            "description": "Whether the run's artifact sorts findings by whose failure they are at all. Empty with this false is an\nartifact from before the classification (contracts 0.5), or no artifact yet: nobody asked. Empty with this\ntrue is a run that asked and found none.",
            "title": "Classified",
            "type": "boolean"
          },
          "tester_errors": {
            "description": "The artifact's `tester_errors[]`, each the contract's own issue object with its `tester_error` block.",
            "items": {
              "$ref": "#/$defs/Issue"
            },
            "title": "Tester Errors",
            "type": "array"
          },
          "reading": {
            "description": "One entry per record, in the same order and naming its `tester_error_id`.",
            "items": {
              "$ref": "#/$defs/TesterErrorReading"
            },
            "title": "Reading",
            "type": "array"
          },
          "advisory": {
            "description": "How to read this whole answer: harness-attributed, never a defect. What it may and may not be used\nfor.",
            "title": "Advisory",
            "type": "string"
          },
          "subjective_fields": {
            "description": "Fields on each record that are somebody's words rather than an observation: the persona's narration, and\n`tester_error.reason`, the harness's sentence about its own persona.",
            "items": {
              "type": "string"
            },
            "title": "Subjective Fields",
            "type": "array"
          }
        },
        "required": [
          "run_id",
          "status",
          "classified",
          "tester_errors",
          "reading",
          "advisory",
          "subjective_fields"
        ],
        "title": "TesterErrorList",
        "type": "object"
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "diff_vs_baseline",
      "description": "What changed since the run's baseline (its spec's baseline_run_id), as the run's artifact measured it: issues new on this run, issues of the baseline this run resolved, issues that persist, and the persona sessions that regressed - completed on the baseline and not now. Only a reproduction-gated issue is ever new or resolved; a persisting one may not be, so may_file_ticket_alone on each row still decides what may be filed. A regressed session is a signal, never a defect: it is reportable only through its new_issue_ids. Resolved rows are the baseline's issues, read whole with get_issue on baseline_run_id; every row carries the evidence refs get_screenshot and get_evidence_url accept. Refused, with the reason, for a run that named no baseline or was never compared with it, and until the run's artifact is assembled. About 450 characters an issue.",
      "inputSchema": {
        "properties": {
          "run_id": {
            "description": "Run id; its baseline is the one its spec named.",
            "title": "Run Id",
            "type": "string"
          }
        },
        "required": [
          "run_id"
        ],
        "title": "diff_vs_baselineArguments",
        "type": "object"
      },
      "outputSchema": {
        "$defs": {
          "DiffIssueRow": {
            "additionalProperties": false,
            "description": "One issue on the baseline diff: what picks it out, whether it may be filed, and its evidence refs.\n\nNarrower than `IssueRow`, because one diff carries two runs' issues and still has to fit one tool result: the\nsession lists go and the count stays, and `baseline_status` is the list the row is in. The refs ride on the\nrow because the diff is where an agent decides which change to look at first, and a screenshot is what it\nlooks at; `get_issue` has the rest.",
            "properties": {
              "id": {
                "title": "Id",
                "type": "string"
              },
              "title": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Title"
              },
              "type": {
                "title": "Type",
                "type": "string"
              },
              "severity": {
                "title": "Severity",
                "type": "string"
              },
              "route": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Route"
              },
              "may_file_ticket_alone": {
                "description": "Whether this issue may be filed as a ticket on its own, computed as `IssueReading.may_file_ticket_alone`\nis.",
                "title": "May File Ticket Alone",
                "type": "boolean"
              },
              "reproduction_result": {
                "title": "Reproduction Result",
                "type": "string"
              },
              "affected_count": {
                "title": "Affected Count",
                "type": "integer"
              },
              "confidence": {
                "title": "Confidence",
                "type": "number"
              },
              "evidence_refs": {
                "$ref": "#/$defs/EvidenceRefs"
              }
            },
            "required": [
              "id",
              "type",
              "severity",
              "may_file_ticket_alone",
              "reproduction_result",
              "affected_count",
              "confidence",
              "evidence_refs"
            ],
            "title": "DiffIssueRow",
            "type": "object"
          },
          "EvidenceRefs": {
            "additionalProperties": false,
            "description": "The store URIs on one issue, gathered where a consumer that wants to *fetch* evidence looks for them.\n\n`screenshots` are what the MCP `get_screenshot` tool accepts. The other two are not - it serves images\nonly - and are fetched over `GET /api/v1/blobs?ref=` with the same key, which is why they are named here\nrather than left for a caller to dig out of `issue.evidence`.",
            "properties": {
              "screenshots": {
                "items": {
                  "type": "string"
                },
                "title": "Screenshots",
                "type": "array"
              },
              "dom_snapshot": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Dom Snapshot"
              },
              "video": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Video"
              }
            },
            "title": "EvidenceRefs",
            "type": "object"
          },
          "RegressedSession": {
            "additionalProperties": false,
            "description": "One persona x mission that completed on the baseline and did not now (`outcomes[].regression`).",
            "properties": {
              "session_id": {
                "title": "Session Id",
                "type": "string"
              },
              "persona_id": {
                "title": "Persona Id",
                "type": "string"
              },
              "mission_id": {
                "title": "Mission Id",
                "type": "string"
              },
              "result": {
                "description": "What the session ended as on this run: anything but `completed`.",
                "title": "Result",
                "type": "string"
              },
              "baseline_result": {
                "description": "What the same persona x mission ended as on the baseline: `completed`, by definition of a regression.",
                "title": "Baseline Result",
                "type": "string"
              },
              "end_step": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "End Step"
              },
              "end_reason": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "End Reason"
              },
              "issue_ids": {
                "description": "Every issue this run raised in the session.",
                "items": {
                  "type": "string"
                },
                "title": "Issue Ids",
                "type": "array"
              },
              "new_issue_ids": {
                "description": "The ones the baseline did not have. Each is reproduction-gated, since only such an issue is ever `new`, and\nthey are the only part of this regression that may be reported; an empty list is a persona that did\nnot finish this time, with nothing yet to say why.",
                "items": {
                  "type": "string"
                },
                "title": "New Issue Ids",
                "type": "array"
              },
              "video": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "The session's recording on this run, for `get_evidence_url`. Absent when none was recorded.",
                "title": "Video"
              },
              "baseline_session_id": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "The baseline session this one was paired with - the n-th of the same persona x mission - so\n`get_session_trace` on `baseline_run_id` shows the run that finished.",
                "title": "Baseline Session Id"
              },
              "baseline_video": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "That baseline session's recording, for `get_evidence_url`.",
                "title": "Baseline Video"
              }
            },
            "required": [
              "session_id",
              "persona_id",
              "mission_id",
              "result",
              "baseline_result",
              "issue_ids",
              "new_issue_ids"
            ],
            "title": "RegressedSession",
            "type": "object"
          }
        },
        "additionalProperties": false,
        "description": "The MCP `diff_vs_baseline` answer: this run against its `baseline_run_id`, as the artifact measured it.\n\nRead from the artifact and never recomputed: `new` and `persisting` are this run's issues by\n`baseline_status`, `resolved` are the baseline's issues the artifact names in `baseline_resolved_issue_ids`,\nand the three counts are the artifact's own `summary.issues_*_vs_baseline`. No route answers this.",
        "properties": {
          "run_id": {
            "title": "Run Id",
            "type": "string"
          },
          "baseline_run_id": {
            "title": "Baseline Run Id",
            "type": "string"
          },
          "issues_new": {
            "title": "Issues New",
            "type": "integer"
          },
          "issues_resolved": {
            "title": "Issues Resolved",
            "type": "integer"
          },
          "issues_persisting": {
            "title": "Issues Persisting",
            "type": "integer"
          },
          "new": {
            "description": "This run's issues the baseline did not have, on routes it reached. Only a reproduction-gated issue is ever\nnew, so every row here may file a ticket alone.",
            "items": {
              "$ref": "#/$defs/DiffIssueRow"
            },
            "title": "New",
            "type": "array"
          },
          "resolved": {
            "description": "The baseline's issues this run no longer has, with the baseline's ids and evidence: read one whole with\n`get_issue` on `baseline_run_id`. Only an issue that reproduced there, on a route this run reached, counts.",
            "items": {
              "$ref": "#/$defs/DiffIssueRow"
            },
            "title": "Resolved",
            "type": "array"
          },
          "persisting": {
            "description": "This run's issues the baseline had too, reproduced or not: persisting is a fact about both runs' findings,\nnot a new claim about the product, so `may_file_ticket_alone` on each row still decides what may be filed.",
            "items": {
              "$ref": "#/$defs/DiffIssueRow"
            },
            "title": "Persisting",
            "type": "array"
          },
          "regressed_sessions": {
            "items": {
              "$ref": "#/$defs/RegressedSession"
            },
            "title": "Regressed Sessions",
            "type": "array"
          },
          "advisory": {
            "description": "How to read `regressed_sessions`: a signal, never a defect on its own.",
            "title": "Advisory",
            "type": "string"
          },
          "note": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Present when the artifact counts resolved issues it does not all name: one written before contracts 0.6.2,\nor a baseline assembled again since.",
            "title": "Note"
          }
        },
        "required": [
          "run_id",
          "baseline_run_id",
          "issues_new",
          "issues_resolved",
          "issues_persisting",
          "new",
          "resolved",
          "persisting",
          "regressed_sessions",
          "advisory"
        ],
        "title": "BaselineDiffView",
        "type": "object"
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "get_session_trace",
      "description": "One session's step-by-step ledger: the persona's diary line beside the surface the browser saw - url, inputs dispatched, network failures, console errors, screenshot_ref. Step ids match the step_refs on issues and verdicts. Readable while the session is still running, in which case `status` says `in_flight`, more steps are coming, and the session video is not stored yet. By default each step is its diary: the action, what the persona said and perceived, the url and the screenshot_ref it was looking at - about 300 characters a step, which is what 'what was it thinking' needs. detail='full' adds the surface block - inputs dispatched, network failures, console errors, timing, tokens, video refs - at about 1,500 a step. With no window this returns the last 60 steps of a longer session; page back with from_step/to_step (inclusive). No answer is more than 25,000 characters: a window that would be is cut to the steps that fit - the latest of an unwindowed ask, the first of an explicit one - and from_step/to_step are the steps that came back, so the next page starts at to_step + 1. `total_steps` is the whole trace.",
      "inputSchema": {
        "properties": {
          "run_id": {
            "description": "Run id.",
            "title": "Run Id",
            "type": "string"
          },
          "session_id": {
            "description": "Session id, as returned by get_run.",
            "title": "Session Id",
            "type": "string"
          },
          "from_step": {
            "anyOf": [
              {
                "minimum": 0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "First step id to return, inclusive.",
            "title": "From Step"
          },
          "to_step": {
            "anyOf": [
              {
                "minimum": 0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Last step id to return, inclusive.",
            "title": "To Step"
          },
          "detail": {
            "default": "diary",
            "description": "diary: per step, the action, the persona's words, the url and the screenshot_ref. full: every field, the surface block included, at several times the size.",
            "enum": [
              "diary",
              "full"
            ],
            "title": "Detail",
            "type": "string"
          }
        },
        "required": [
          "run_id",
          "session_id"
        ],
        "title": "get_session_traceArguments",
        "type": "object"
      },
      "outputSchema": {
        "$defs": {
          "AlignedStep": {
            "additionalProperties": false,
            "description": "One step, both halves: what the persona decided (the agent half) and what the browser did (the surface\nhalf), joined on `step_id`. A half that is missing is flagged (`has_agent`, `has_surface`), not invented; the\ntrailing `abandon`/`complete` step is agent-only by design (`terminal`).\n\nOn an `in_flight` page (`StepsPage.status`) both halves are there for every step the browser has answered.\nWhat waits for the session to end is the video, which is stored when the browser closes: a live step carries\nits `video_ref` and window but no `video_url` yet.",
            "properties": {
              "step_id": {
                "title": "Step Id",
                "type": "integer"
              },
              "has_agent": {
                "title": "Has Agent",
                "type": "boolean"
              },
              "has_surface": {
                "title": "Has Surface",
                "type": "boolean"
              },
              "terminal": {
                "title": "Terminal",
                "type": "boolean"
              },
              "ts_agent": {
                "anyOf": [
                  {
                    "format": "date-time",
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Ts Agent"
              },
              "action_type": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Action Type"
              },
              "target_description": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Target Description"
              },
              "action_args": {
                "additionalProperties": true,
                "title": "Action Args",
                "type": "object"
              },
              "narration": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "SUBJECTIVE: the persona's diary line said before this action. Quote it as the persona's, never as a\nfinding.",
                "title": "Narration"
              },
              "reasoning_summary": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Reasoning Summary"
              },
              "perceived_state": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Perceived State"
              },
              "observation_mode": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Observation Mode"
              },
              "tolerance_state": {
                "additionalProperties": true,
                "title": "Tolerance State",
                "type": "object"
              },
              "latency_ms": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Latency Ms"
              },
              "tokens": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/StepTokens"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "model": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Model"
              },
              "end_reason": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "End Reason"
              },
              "result": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "On the terminal step, the artifact result it implies (`completed`, `abandoned`, `budget_exhausted`).",
                "title": "Result"
              },
              "field": {
                "anyOf": [
                  {
                    "additionalProperties": true,
                    "type": "object"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "The focused text-entry control after this step, as the persona was told it (`agent_trace.field`): its\nkind, accessible name, the value it really holds (cut at 200 characters), the value's full length, and the\nhint it shows while empty - never part of the value. A password field, and any field holding one of the\nsession's credentials, is `masked`: the length is there and the value is not. Present on a `click`, `type`\nor `key` step whose focus landed in a text control, on a pool that reports it; absent otherwise. On the\nagent half, so it is readable on an `in_flight` page.",
                "title": "Field"
              },
              "ts_surface": {
                "anyOf": [
                  {
                    "format": "date-time",
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Ts Surface"
              },
              "url": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Url"
              },
              "route": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Route"
              },
              "viewport": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Viewport"
              },
              "inputs": {
                "description": "What the body dispatched at this step (`surface_trace.inputs_dispatched`): pointer and keyboard input,\nwith the selector and text each resolved onto. On a `type` the persona made with the session's credential\n(`action_args.secret`), every keyboard input's `detail.text` and `resolved_text` is `[secret]`, whatever\nthe pool recorded: the password is on no step a reader is served.",
                "items": {
                  "additionalProperties": true,
                  "type": "object"
                },
                "title": "Inputs",
                "type": "array"
              },
              "files_attached": {
                "description": "Files the body handed to the page at this step (`surface_trace.files_attached`): name, size, media\ntype, hash and whether it went through the file picker or straight into a file input - never a path. Empty\non every step but an `upload`.",
                "items": {
                  "additionalProperties": true,
                  "type": "object"
                },
                "title": "Files Attached",
                "type": "array"
              },
              "dialogs": {
                "description": "Native dialogs the page opened, or that were answered, at this step (`surface_trace.dialogs`): type,\nmessage verbatim, and how the step left it - `pending` on the step that opened one, `accept`, `dismiss` or\n`auto_accepted_beforeunload` on the step that answered it. A dialog floats above the page and is in no\nscreenshot, so this is the only place a reader sees it. Empty when the pool saw none.",
                "items": {
                  "additionalProperties": true,
                  "type": "object"
                },
                "title": "Dialogs",
                "type": "array"
              },
              "downloads": {
                "description": "Files the browser saved at this step (`surface_trace.downloads`): filename, media type, size, hash and\nthe stored copy's ref when the pool kept the bytes - never a path. A download moves no pixel and no url, so\nwithout this the step reads as a click that did nothing. Empty when the pool saw none.",
                "items": {
                  "additionalProperties": true,
                  "type": "object"
                },
                "title": "Downloads",
                "type": "array"
              },
              "network_failures": {
                "items": {
                  "additionalProperties": true,
                  "type": "object"
                },
                "title": "Network Failures",
                "type": "array"
              },
              "console_errors": {
                "items": {
                  "additionalProperties": true,
                  "type": "object"
                },
                "title": "Console Errors",
                "type": "array"
              },
              "dom_mutation_count": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Dom Mutation Count"
              },
              "timing": {
                "$ref": "#/$defs/StepTiming"
              },
              "screenshot_ref": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Screenshot Ref"
              },
              "screenshot_url": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Screenshot Url"
              },
              "video_ref": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Video Ref"
              },
              "video_url": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Video Url"
              },
              "ts_start_s": {
                "anyOf": [
                  {
                    "type": "number"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "Where this step starts in the session video, seconds from its first frame.",
                "title": "Ts Start S"
              },
              "ts_end_s": {
                "anyOf": [
                  {
                    "type": "number"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Ts End S"
              }
            },
            "required": [
              "step_id",
              "has_agent",
              "has_surface",
              "terminal"
            ],
            "title": "AlignedStep",
            "type": "object"
          },
          "StepTiming": {
            "additionalProperties": false,
            "description": "How long the browser took over one step, in milliseconds. Each is absent when the browser did not\nmeasure it.",
            "properties": {
              "action_to_settle_ms": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Action To Settle Ms"
              },
              "longest_task_ms": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Longest Task Ms"
              },
              "navigation_ms": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Navigation Ms"
              }
            },
            "title": "StepTiming",
            "type": "object"
          },
          "StepTokens": {
            "additionalProperties": false,
            "description": "The model tokens behind one step's decision: prompt, reply, and the part of the prompt read from the\ncache. Each is absent when the provider did not report it.",
            "properties": {
              "input": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Input"
              },
              "output": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Output"
              },
              "cached": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Cached"
              }
            },
            "title": "StepTokens",
            "type": "object"
          }
        },
        "additionalProperties": false,
        "description": "A session's steps, aligned. Readable while the session runs, not only after it ends.\n\nOne step is written as the persona takes it, so a caller polling `?after=<last_step_id>` tails a live\nsession: each call returns the steps written since the last one, and `status` says whether more are coming.\n`heartbeat_step` beside it is where the browser has got to, which is at or one ahead of `last_step_id` while\na step is in flight.",
        "properties": {
          "run_id": {
            "title": "Run Id",
            "type": "string"
          },
          "session_id": {
            "title": "Session Id",
            "type": "string"
          },
          "persona_id": {
            "title": "Persona Id",
            "type": "string"
          },
          "mission_id": {
            "title": "Mission Id",
            "type": "string"
          },
          "status": {
            "description": "`in_flight` while the session has not been recorded yet: the steps below are what the persona and the\nbrowser have written so far, both halves joined, and more will follow. The one thing a live page lacks is the\nsession video, which is stored when the browser closes: `video_ref` and each step's window are there,\n`video_url` is absent until the page is final. A step whose surface half is missing on a live page is one\nthe browser has not answered yet or a real gap, as on a final one. Otherwise the session's own\nstatus (`completed`, `abandoned`, `blocked`, `errored`, `budget_exhausted`, `cancelled`), and the page is\nfinal.",
            "title": "Status",
            "type": "string"
          },
          "heartbeat_step": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "LIVE. The `step_id` the persona last reported: where the browser has got to, which is at or one ahead of\n`last_step_id` while a step is in flight - a step is written after the browser answers, so a step in flight\nshows here first. Only asked for while `status` is `in_flight`: absent on a final page, and on a live one\nwhose live state cannot be read.",
            "title": "Heartbeat Step"
          },
          "last_heartbeat_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "When that heartbeat reached the server: how stale `heartbeat_step` is. Absent whenever it is.",
            "title": "Last Heartbeat At"
          },
          "waiting_for_seat": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "LIVE. `true` while the session is queued for a browser seat, which is why a live page can have no steps at\nall: every seat in the pool is taken, and it takes no steps until one frees. A session resuming after a pause\nor a pool failure queues the same way, so it can be `true` on a page that has steps. Only asked for while\n`status` is `in_flight`: absent on a final page, and on a live one whose live state cannot be read.",
            "title": "Waiting For Seat"
          },
          "waiting_since": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "When the session was queued for the seat it is waiting for: how long it has waited. Absent whenever\n`waiting_for_seat` is not `true`.",
            "title": "Waiting Since"
          },
          "workflow_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "The session's workflow id, as `SessionRow.workflow_id`: what `heartbeat_step` was read off.",
            "title": "Workflow Id"
          },
          "from_step": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "The inclusive lower bound in effect, whether it came from `from` or `after` + 1.",
            "title": "From Step"
          },
          "to_step": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "To Step"
          },
          "total_steps": {
            "description": "Steps in the whole trace, before the `from`/`to` window - not the count of `steps`.",
            "title": "Total Steps",
            "type": "integer"
          },
          "first_step_id": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "First Step Id"
          },
          "last_step_id": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "The last step in the whole trace. On an `in_flight` page this is what a tailing caller passes back as\n`after` to get the next batch; absent means the session has written no step yet.",
            "title": "Last Step Id"
          },
          "agent_trace_ref": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Agent Trace Ref"
          },
          "surface_trace_ref": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Surface Trace Ref"
          },
          "video_ref": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Video Ref"
          },
          "video_url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Video Url"
          },
          "steps": {
            "items": {
              "$ref": "#/$defs/AlignedStep"
            },
            "title": "Steps",
            "type": "array"
          }
        },
        "required": [
          "run_id",
          "session_id",
          "persona_id",
          "mission_id",
          "status",
          "total_steps",
          "steps"
        ],
        "title": "StepsPage",
        "type": "object"
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "get_screenshot",
      "description": "Fetch one screenshot as an image, by a ref from an issue's evidence or a trace step's screenshot_ref. Refs outside this run's evidence are refused. Images only, at most 2 MiB; anything else - a video, a DOM snapshot, a raw trace - is a call to get_evidence_url, which answers a URL to fetch instead of bytes to read.",
      "inputSchema": {
        "properties": {
          "ref": {
            "description": "Screenshot URI, e.g. s3://bucket/run/session/shots/0012.png.",
            "title": "Ref",
            "type": "string"
          }
        },
        "required": [
          "ref"
        ],
        "title": "get_screenshotArguments",
        "type": "object"
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "get_evidence_url",
      "description": "The URL one piece of evidence is fetched from, with its content type and its exact size in bytes - and none of the bytes. This is the way to a session video, a screenshot too large to look at inline, and anything else get_screenshot refuses: a plain GET on the URL with the same Authorization header you are configured with, which honours Range, and HEAD for the size again without a body. Read size_bytes before you fetch: evidence is megabytes, and a video is not something to read into a conversation. The ref is checked against the evidence store and the run it belongs to before any URL is answered, so a URL from this tool is one your key can fetch. Images and video only - the raw agent and surface traces and the run spec are not served here, and the modelled view of a session is get_session_trace.",
      "inputSchema": {
        "properties": {
          "ref": {
            "description": "Evidence URI, e.g. s3://bucket/run/session/video.webm.",
            "title": "Ref",
            "type": "string"
          }
        },
        "required": [
          "ref"
        ],
        "title": "get_evidence_urlArguments",
        "type": "object"
      },
      "outputSchema": {
        "additionalProperties": false,
        "description": "One evidence blob as something to fetch rather than something fetched: the MCP `get_evidence_url` answer.\n\nNo route returns this - over HTTP the answer *is* the route, `GET /api/v1/blobs?ref=`, and a caller that\ncan read this model can read that URL. It exists for the tool, where handing back a session video inline\nwould be a context window spent on bytes a model cannot look at: the URL, the type, and the size are what\nan agent needs to decide whether to fetch at all, and `HEAD` on the same URL answers the last two again\nwithout a body.\n\n`url` is absolute, because a tool result is read by an agent that has no base to resolve a path against.",
        "properties": {
          "ref": {
            "description": "The store URI asked about, unchanged - what a trace or an issue's evidence recorded.",
            "title": "Ref",
            "type": "string"
          },
          "url": {
            "description": "The absolute `GET /api/v1/blobs?ref=` URL, fetched with the same API key and honouring `Range`.",
            "title": "Url",
            "type": "string"
          },
          "content_type": {
            "description": "What the route will answer as `Content-Type`, from the ref's file suffix.",
            "title": "Content Type",
            "type": "string"
          },
          "size_bytes": {
            "description": "The whole blob's size as the store reports it, so a caller knows the cost before it pays it.",
            "title": "Size Bytes",
            "type": "integer"
          }
        },
        "required": [
          "ref",
          "url",
          "content_type",
          "size_bytes"
        ],
        "title": "EvidenceUrl",
        "type": "object"
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "validate_spec",
      "description": "Check a run spec without starting anything, and see what the run would be. Runs every refusal start_run runs - the contract, the cross-references inside providers, whether each providers entry is on a provider Saram resolves, the single-user rule on a ChatGPT login, whether every awssm:// credential_ref is one your tenant uploaded, whose uploaded files the spec names, whether your caller has a model provider at all, and - for a providers block on your ChatGPT login - whether the gateway holds that login to sign with - then answers the spec as the run would store it and the sessions it would plan, one per persona per mission, plus warnings: a ChatGPT login whose access token has lapsed or whose gateway could not be asked, and a model Saram has no price for, which is new or misspelt. It also answers what the run would cost: cost_ceiling.usd is the most the run can bill on your plan, in dollars, on every plan (0 when your plan bills nothing), with a one-line basis and model_calls, who bills the model calls on your own provider, which are never in the figure. It is a ceiling, not a forecast - every step counted at its most - so when a person asks what a run would cost, answer 'up to $<usd>' from this one call and quote the basis. Where your plan is billed in Usage, usage_ceiling says the most Usage the run can use, the balance available to cover it, and the steps_per_session that would fit; start_run refuses a run it does not cover when enforced is true. Free, instant, and creates nothing: call it before every start_run.",
      "inputSchema": {
        "properties": {
          "spec": {
            "additionalProperties": false,
            "description": "A run spec, the RunSpec contract. Required: target.entry_url, personas (id@version addresses from list_personas), missions (each an id, a goal in words a persona would understand and at least one success_criteria {kind, value}) and budget.steps_per_session. The example is the smallest spec that validates; everything else is optional.",
            "examples": [
              {
                "budget": {
                  "steps_per_session": 30
                },
                "missions": [
                  {
                    "goal": "create an account",
                    "id": "sign_up",
                    "success_criteria": [
                      {
                        "kind": "judge_assertion",
                        "value": "the persona reached a signed-in page"
                      }
                    ]
                  }
                ],
                "personas": [
                  "dana_first_timer@1"
                ],
                "target": {
                  "entry_url": "https://example.com"
                }
              }
            ],
            "properties": {
              "run_id": {
                "description": "Assigned by the control plane if absent",
                "type": "string"
              },
              "trigger": {
                "additionalProperties": false,
                "description": "Who or what started the run, and against which code: a record kept with the run, which changes nothing about what it does but in two places. A run billed to a ChatGPT subscription login is refused when `kind` is `ci`, and `pr` and `commit` are copied onto the artifact's `target`, where `pr` is the pull request the GitHub connector's comment goes on.",
                "properties": {
                  "kind": {
                    "description": "How the run was started: `manual`, by a person at a keyboard; `api`, through the API or a coding agent's MCP tools, which is what `start_run` writes when the spec leaves it out; `ci`, by a CI job with nobody watching. The caller's own claim, recorded as given. `ci` is refused on a run billed to a ChatGPT subscription login, which serves only runs a person starts.",
                    "enum": [
                      "manual",
                      "api",
                      "ci"
                    ],
                    "type": "string"
                  },
                  "actor": {
                    "description": "User, token, or CI job that triggered the run",
                    "type": "string"
                  },
                  "pr": {
                    "type": "integer"
                  },
                  "commit": {
                    "type": "string"
                  },
                  "diff_ref": {
                    "description": "Where the PR diff can be fetched, for diff-aware targeting. Accepted and recorded; nothing reads it yet.",
                    "type": "string"
                  }
                },
                "type": "object"
              },
              "target": {
                "additionalProperties": false,
                "description": "The product under test: the URL every session opens on (a mission's own `entry_url` overrides it), a name for it, the tunnel to it when it runs on your own machine (`tunnel`), and, when it needs them, how a session gets past a gate in front of the whole deployment (`auth`) and how the product's state is reset and its test accounts handed out between sessions (`seed`).",
                "properties": {
                  "entry_url": {
                    "format": "uri",
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "tunnel": {
                    "description": "For a product running on your own machine: the id of the tunnel you opened to it (`tun_...`), which Saram's browsers reach it through and nothing else can. Not available yet: until Saram opens and binds tunnels, every spec naming one is refused with a 422 (`tunnels to your own machine are not available yet`) by `POST /api/v1/runs`, `POST /api/v1/runs/validate` and the `validate_spec` and `start_run` tools, before any run exists. Naming it binds that tunnel to this run, and the spec is refused unless the tunnel is one the tenant starting the run opened, is still open and is bound to no other run, and every URL the run opens on the target - `entry_url`, each mission's `entry_url` and `seed.reset_hook` - is on the tunnel's hostname. Saram puts the tunnel's own door in front of the run, so a spec naming a tunnel carries no `auth` of its own. The tunnel closes when the run ends.",
                    "pattern": "^tun_[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9a-f]{8}$",
                    "type": "string"
                  },
                  "auth": {
                    "additionalProperties": false,
                    "description": "How a session gets past the target's own door: the gate in front of the whole deployment, not the product's sign-in. `auth` and `seed.accounts` are one mechanism seen from two ends and both resolve `secret-store` entries; which end a credential belongs at is decided by whether it is the target's or the persona's. **The target's** (one for the run, the same for every session, nothing a person would be shown): `basic` on a staging host, a deployment-protection bypass `header` on a Vercel or Cloudflare Access preview, a `cookie` the whole run shares. That is this object: the pool receives `secret_ref` as `session_create.target_auth_secret_ref` and applies the entry's `basic` and `headers` to requests to the target's site (`secret-store` `entry.basic`) and its `cookies` to the browser context, and the control plane sends the same entry's `basic`, `headers` and the `cookies` scoped to the hook on its POST to `seed.reset_hook`, which is behind the same door. **The persona's** (one per session, and a persona may be asked to type it): `seed.accounts`, one ref per session, where the pool applies the entry's `cookies` and the prompt renderer reads `email`, `password`, `inbox` and `notes`. A run can carry both - a staging host behind basic auth whose personas each sign in with their own account - and where the two name the same credential the account's wins. Closed by design: credentials arrive only as `secret_ref`, so a field this object does not list is an inline secret and every reader rejects it, not just the producer.",
                    "properties": {
                      "kind": {
                        "description": "What the door is, for a reader of the spec. It selects nothing: the pool applies whatever the entry behind `secret_ref` carries, so `basic` and `header` on one entry are both applied whichever of the two this says. `none` is a target with no gate (`secret_ref` then says nothing and is omitted); `login_flow` is the one kind that is not a gate at all but a form the persona fills in, which is `seed.accounts` and `login_url`.",
                        "enum": [
                          "none",
                          "basic",
                          "cookie",
                          "header",
                          "login_flow"
                        ],
                        "type": "string"
                      },
                      "secret_ref": {
                        "description": "Reference into the secret store; never inline credentials. Carried to the pool as `session_create.target_auth_secret_ref`, once per session, unchanged; the pool resolves it and applies `basic` and `headers` to requests to the target's site (`secret-store` `entry.basic`) and `cookies` on the context. The same ref for every session of the run - a per-session credential is `seed.accounts`, not this.",
                        "type": "string"
                      },
                      "login_url": {
                        "description": "Where the product's own sign-in form is, with `kind: login_flow`. Descriptive only: the persona is given the account behind `seed.accounts` and finds the form itself, which is the behaviour under test, so nothing navigates here.",
                        "format": "uri",
                        "type": "string"
                      }
                    },
                    "type": "object",
                    "x-closed": true
                  },
                  "seed": {
                    "additionalProperties": false,
                    "description": "Environment provisioning. Every e2e product bleeds here; make it explicit. Closed by design (x-closed) for the same reason as `auth`: `accounts` are secret refs and nothing inline may ride beside them.",
                    "properties": {
                      "reset_hook": {
                        "description": "Endpoint to reset state before each session. The control plane POSTs it once per run with `{\"feature_flags\": ...}` (the whole target), before each session with `session_id` added (that session's partition), and before each reproduction replay attempt the same way. It sits behind the target's own door like every other url on the host, so the POST carries the entry behind `auth.secret_ref`: its `basic` as HTTP basic auth, its `headers` verbatim and, as one `Cookie` header, those of its `cookies` the browser would send to the hook's url - the same credential the pool opens the browser's door with. A cookie goes when its scope matches the hook as Playwright scopes it in the pool: a `url` names its host alone, that url's path up to its last `/`, and https only; otherwise `domain` (with a leading dot, every subdomain too) and `path` (a prefix ending at a `/`) do, and `secure` keeps it off plain http. A cookie scoped to another host or path is not sent, and neither is any cookie when `headers` already name `Cookie`. A hook that answers anything but 2xx fails the reset.",
                        "format": "uri",
                        "type": "string"
                      },
                      "accounts": {
                        "description": "Pool of test account secret_refs, one per session, handed out in session order; sessions past the end of the list get none and the persona is told they have no login. The other half of `auth` (see its description for the split): the persona's credential rather than the target's. Each ref reaches the pool as `session_create.auth_secret_ref`, which applies the entry's `cookies` (and `basic`/`headers`, for the rare account that overrides the target's own), while the renderer tells the persona which credentials they 'have' (email, password) and which they do not (inbox access). Never inline.",
                        "items": {
                          "type": "string"
                        },
                        "type": "array"
                      },
                      "feature_flags": {
                        "additionalProperties": {
                          "type": "boolean"
                        },
                        "type": "object"
                      }
                    },
                    "type": "object",
                    "x-closed": true
                  }
                },
                "required": [
                  "entry_url"
                ],
                "type": "object"
              },
              "personas": {
                "items": {
                  "oneOf": [
                    {
                      "description": "persona id@version from the registry, e.g. novice_mobile_skimmer@2",
                      "type": "string"
                    },
                    {
                      "description": "A persona written inline, the Persona contract (get_persona answers one). The contract allows it and a run refuses it: only registry personas are supported, so name each by its id@version address from list_personas.",
                      "type": "object"
                    }
                  ]
                },
                "minItems": 1,
                "type": "array"
              },
              "missions": {
                "items": {
                  "additionalProperties": false,
                  "description": "One thing each persona is sent to do: a goal in the persona's own terms, how to tell it was achieved, and optionally where to start, what the judge should watch for and the question the persona answers at the end.",
                  "properties": {
                    "id": {
                      "pattern": "^[a-z0-9_]+$",
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "entry_url": {
                      "description": "Overrides target.entry_url",
                      "format": "uri",
                      "type": "string"
                    },
                    "goal": {
                      "description": "Stated in terms a persona would understand, e.g. 'buy one of the blue shirts'",
                      "type": "string"
                    },
                    "success_criteria": {
                      "description": "What reaching the goal looks like. The persona is never shown these: it is given `goal` and `brief` and decides for itself when it is finished. Only a `url_matches` criterion is checked by machine; every other kind is context for the judge. See `kind`.",
                      "items": {
                        "additionalProperties": false,
                        "description": "One way to tell the goal was reached: what sort of evidence it is (`kind`) and what to look for (`value`). Only a `url_matches` criterion is checked by machine; see `kind`.",
                        "properties": {
                          "kind": {
                            "description": "What the criterion states and who checks it. `url_matches` is the only kind checked by machine: `value` is part of a URL, e.g. `/order/confirmed`, and when the persona says it is finished the session ends with `end_reason` `success_criteria_met` if `value` occurs in the last URL the browser was on, or `claimed_finished_unverified` if it does not (always, for a mission with no `url_matches` criterion). Nothing checks the other four: the judge that reads each session afterwards is shown every criterion as text, `<kind>=<value>`, beside the mission's goal, and nothing decides whether one was met. `text_visible`: text the page shows once the goal is reached. `element_visible`: an element on that page, in words or as a selector. `network_call`: a request the product makes when the goal is reached, e.g. `POST /api/orders`. `judge_assertion`: a plain-language statement about how the session ended, e.g. `the persona reached a signed-in page`. For a verified ending, give a mission a `url_matches` criterion beside any of these.",
                            "enum": [
                              "url_matches",
                              "text_visible",
                              "element_visible",
                              "network_call",
                              "judge_assertion"
                            ],
                            "type": "string"
                          },
                          "value": {
                            "description": "Read according to `kind`: part of a URL for `url_matches`, otherwise text for the judge.",
                            "type": "string"
                          }
                        },
                        "required": [
                          "kind",
                          "value"
                        ],
                        "type": "object"
                      },
                      "minItems": 1,
                      "type": "array"
                    },
                    "watch_list": {
                      "description": "Specific concerns the judge should look for, e.g. 'is the new discount widget discoverable'. The only place free opinions may be solicited: the persona itself is never asked to critique the product.",
                      "items": {
                        "type": "string"
                      },
                      "type": "array"
                    },
                    "authored_by": {
                      "default": "human",
                      "description": "Who wrote the mission: `human`, the default, for one a person wrote; `doppelganger` and `planner` for one an agent wrote on someone's behalf. A label only: it is kept with the run and changes nothing about how the mission is run.",
                      "enum": [
                        "human",
                        "doppelganger",
                        "planner"
                      ],
                      "type": "string"
                    },
                    "brief": {
                      "description": "Optional context from the mission author to the persona (what they were told before starting)",
                      "type": "string"
                    },
                    "verdict_prompt": {
                      "description": "The question about THIS product that the persona answers in their final diary entry, asked after the persona's own `persona.verdict_prompt`. Set it whenever the personas were written for a different product than the one under test: a roofing persona asked 'is this going to bring you roofing jobs' has nothing to answer with after a shirt-shop session, and the verdict comes back null. When present, this is the question the judge answers and `persona.verdict_prompt` is not applicable for the run.",
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "goal",
                    "success_criteria"
                  ],
                  "type": "object"
                },
                "minItems": 1,
                "type": "array"
              },
              "budget": {
                "additionalProperties": false,
                "description": "How far each session may go and how much the run may spend. A session ends at the first of its caps it reaches: `steps_per_session` steps (`end_reason` `budget_steps`), `tokens_per_session` model tokens billed, which is the prompt not read from cache plus the reply (`budget_tokens`), or `wall_clock_s_per_session` seconds (`budget_wall_clock`). `max_concurrent_sessions` is the most of the run's sessions that run at once, and your plan's own limit bounds it. `total_cost_usd` pauses the run once the sessions it has recorded have cost that much: no further session starts until it is resumed, and the ones already running finish.",
                "properties": {
                  "steps_per_session": {
                    "minimum": 1,
                    "type": "integer"
                  },
                  "tokens_per_session": {
                    "minimum": 1,
                    "type": "integer"
                  },
                  "wall_clock_s_per_session": {
                    "minimum": 1,
                    "type": "integer"
                  },
                  "max_concurrent_sessions": {
                    "minimum": 1,
                    "type": "integer"
                  },
                  "total_cost_usd": {
                    "description": "Soft cap; run pauses when reached",
                    "minimum": 0,
                    "type": "number"
                  }
                },
                "required": [
                  "steps_per_session"
                ],
                "type": "object"
              },
              "baseline_run_id": {
                "description": "Run to diff against, typically the last run on main",
                "type": "string"
              },
              "providers": {
                "additionalProperties": false,
                "description": "Which model, on which provider or router, with which credential, every model call in this run uses. `entries` are named; `default` names the entry every role uses unless `roles` says otherwise; `roles` binds the three callers (the actor loop, the observer's judge for session judgment and run merge, the reproduction model check) to entry names. A role that resolves to no entry is a producer error. When this block is present it wins over `actor.actor_model` and `actor.judge_model`; when absent, those two and the worker's own process credential apply, which is every run written before 0.4.1. Resolved by the worker that makes the call, at the start of its activity, never by the API or a workflow.",
                "properties": {
                  "entries": {
                    "additionalProperties": {
                      "additionalProperties": false,
                      "description": "One model on one provider or router, in pydantic-ai's own terms: `model` is `<provider>:<model>` and the prefix decides how the rest is read. Closed by design: a credential arrives only as `credential_ref`, so a field this object does not list is an inline secret and every reader rejects it.",
                      "properties": {
                        "model": {
                          "description": "pydantic-ai model string: anthropic:claude-opus-5, bedrock:us.anthropic.claude-opus-5, openai:gpt-5.6, openrouter:anthropic/claude-opus-5, azure:<deployment>, google:gemini-3-pro, google-cloud:gemini-3-pro, litellm:<model>.",
                          "pattern": "^[a-z][a-z0-9-]*:.+$",
                          "type": "string"
                        },
                        "credential_ref": {
                          "description": "Reference to the credential, never the credential: `env://NAME`, `awssm://<secret name>`. Absent means the provider's ambient credential (IRSA or instance role on bedrock, Application Default Credentials on google-cloud); for prefixes that have no ambient path, absent is a producer error at resolution. One secret holds one string; what the string is (API key, bearer token, service-account JSON) is fixed by the prefix.",
                          "pattern": "^[a-z][a-z0-9+.-]*://.+$",
                          "type": "string"
                        },
                        "base_url": {
                          "description": "Endpoint override: a gateway (LiteLLM, a private OpenAI-compatible URL), an Azure resource endpoint, a Bedrock VPC endpoint. Absent means the provider's public endpoint.",
                          "format": "uri",
                          "type": "string"
                        },
                        "region": {
                          "description": "bedrock: the AWS region the model is invoked in. Absent means AWS_REGION / AWS_DEFAULT_REGION on the worker.",
                          "type": "string"
                        },
                        "project": {
                          "description": "google-cloud: the GCP project. Absent means the one Application Default Credentials carry.",
                          "type": "string"
                        },
                        "location": {
                          "description": "google-cloud: the Vertex location, e.g. us-central1 or global.",
                          "type": "string"
                        },
                        "api_version": {
                          "description": "azure: the Azure OpenAI api-version. Absent means OPENAI_API_VERSION on the worker.",
                          "type": "string"
                        }
                      },
                      "required": [
                        "model"
                      ],
                      "type": "object",
                      "x-closed": true
                    },
                    "minProperties": 1,
                    "propertyNames": {
                      "pattern": "^[a-z0-9][a-z0-9_-]{0,63}$"
                    },
                    "type": "object"
                  },
                  "default": {
                    "type": "string",
                    "description": "Entry name. Absent with exactly one entry means that entry."
                  },
                  "roles": {
                    "additionalProperties": false,
                    "description": "Which entry, by name, each of the run's three model callers uses: `actor`, the model that plays the persona; `judge`, the judge that reads each session and merges the run's findings; `reproduction`, the model check made when an issue is replayed. A role left out uses `default`.",
                    "properties": {
                      "actor": {
                        "type": "string"
                      },
                      "judge": {
                        "type": "string"
                      },
                      "reproduction": {
                        "type": "string"
                      }
                    },
                    "type": "object"
                  }
                },
                "required": [
                  "entries"
                ],
                "type": "object"
              },
              "actor": {
                "additionalProperties": false,
                "description": "How the persona sees the page and which models drive it: the observation mode, the persona and judge models, when a screenshot is taken, and whether the session is recorded on video.",
                "properties": {
                  "observation_mode": {
                    "default": "vision",
                    "description": "How the persona perceives the page. `vision`, the default and the only mode supported today: the persona sees a screenshot of the page when it chooses to look. `tree`, `dom_diff` and `hybrid` are reserved for modes that read the page's structure instead of, or beside, a screenshot, and nothing runs them yet: a spec naming one is refused with a 422 by `POST /api/v1/runs`, `POST /api/v1/runs/validate` and the `validate_spec` and `start_run` tools, before any run exists.",
                    "enum": [
                      "vision",
                      "tree",
                      "dom_diff",
                      "hybrid"
                    ],
                    "type": "string"
                  },
                  "actor_model": {
                    "description": "Model id for navigation steps",
                    "type": "string"
                  },
                  "judge_model": {
                    "description": "Model id for the observer",
                    "type": "string"
                  },
                  "screenshot_policy": {
                    "default": "on_look",
                    "description": "`on_look`: the persona sees a screenshot only when it chooses to look at the page. `every_step`: a screenshot after every action is also kept in the session's trace for the judge.",
                    "enum": [
                      "on_look",
                      "every_step",
                      "decision_points",
                      "on_escalation"
                    ],
                    "type": "string"
                  },
                  "record_video": {
                    "default": true,
                    "type": "boolean"
                  }
                },
                "type": "object"
              },
              "connectors": {
                "additionalProperties": false,
                "description": "Where the run's results are delivered once its artifact is assembled: `github` files issues and comments on a pull request in your repository, and `webhook` sends one signed POST to a URL of yours. Both are optional; a run with neither is read through the API, the dashboard and the MCP tools.",
                "properties": {
                  "github": {
                    "additionalProperties": false,
                    "description": "Deliver the run to the GitHub repository `repo` (`owner/name`). `create_issues` files a GitHub issue for each issue that reproduced, is not specific to one persona and is rated at least `min_severity_for_issue`; `pr_comment` posts one comment on the run on the pull request named in `trigger.pr`, created once and updated after that. Both are off by default, so a block that turns neither on writes nothing. `check_run` is accepted and nothing posts a check run yet. Delivery writes as Saram's GitHub App where it is installed on `repo`, and otherwise with `token_ref`.",
                    "properties": {
                      "repo": {
                        "type": "string"
                      },
                      "create_issues": {
                        "default": false,
                        "type": "boolean"
                      },
                      "pr_comment": {
                        "default": false,
                        "type": "boolean"
                      },
                      "check_run": {
                        "default": false,
                        "type": "boolean"
                      },
                      "min_severity_for_issue": {
                        "default": "medium",
                        "description": "The least severe issue `create_issues` files, from least to most: `low`, `medium` (the default), `high`, `critical`. An issue rated below it stays in the run's artifact and is not filed.",
                        "enum": [
                          "low",
                          "medium",
                          "high",
                          "critical"
                        ],
                        "type": "string"
                      },
                      "token_ref": {
                        "description": "Reference to a GitHub token that may write issues and comments on `repo`, never the token: `awssm://<secret name>` under the requesting tenant's own prefix (a credential of kind `github`, POST /api/v1/credentials), or `env://NAME` on an ungated local stack. The fallback, not the connector's identity: delivery authenticates as the deployment's GitHub App installation on `repo` and reads this only when the App is not installed there. With neither, every record the delivery would have written fails with that reason; no other credential is tried, so a run never writes to its repo as somebody else. Resolved by the worker that delivers, at delivery.",
                        "pattern": "^[a-z][a-z0-9+.-]*://.+$",
                        "type": "string"
                      }
                    },
                    "type": "object"
                  },
                  "webhook": {
                    "additionalProperties": false,
                    "description": "One signed POST when the run has finished and its artifact is assembled: a small JSON summary (run id, status, session and issue counts, links to the run and the artifact), so a CI job or a deploy pipeline can act on a run without polling. Sent once, after the run's row is closed, by an activity that never fails the run: a receiver that answers 5xx, 429 or nothing is retried a bounded number of times and then given up on, and the outcome is recorded on the run. A run that is cancelled, or that fails before an artifact exists, sends nothing. Closed by design: the signing key arrives only as `secret_ref`, so a field this object does not list is an inline secret and every reader rejects it.",
                    "properties": {
                      "url": {
                        "description": "Where the POST goes. `https` only; the control plane refuses a URL naming a loopback, private, link-local or otherwise non-public address, whether written as one or resolved to one at delivery, unless the deployment allows private hosts (a local stack), and then `http` is accepted for a loopback host too. Redirects are not followed.",
                        "format": "uri",
                        "type": "string"
                      },
                      "secret_ref": {
                        "description": "Reference to the HMAC-SHA256 key the body is signed with, never the key: `awssm://<secret name>` under the requesting tenant's own prefix (a credential of kind `webhook`, POST /api/v1/credentials), or `env://NAME` on an ungated local stack. Resolved by the worker that delivers, at delivery. The signature is `Saram-Signature: t=<unix seconds>,v1=<hex>`, the digest being HMAC-SHA256 over `<t>.<raw body>` - the scheme Stripe uses - so a receiver verifies the raw bytes it received and refuses a timestamp outside its own tolerance.",
                        "pattern": "^[a-z][a-z0-9+.-]*://.+$",
                        "type": "string"
                      }
                    },
                    "required": [
                      "url",
                      "secret_ref"
                    ],
                    "type": "object",
                    "x-closed": true
                  },
                  "webhook_url": {
                    "description": "Never read. Reserved since 0.3 and superseded by `webhook` (0.6.0), which carries the signing key a bare URL has no room for; kept so a spec that sets it still validates.",
                    "format": "uri",
                    "type": "string"
                  }
                },
                "type": "object"
              },
              "files": {
                "description": "Files the personas bring with them. Bytes never travel in the spec: upload each file with POST /api/v1/files first and name it here by the `ref` that answers. A file belongs to the tenant that uploaded it, so any key or member of that tenant may name it in a run and nobody else may.",
                "items": {
                  "additionalProperties": false,
                  "description": "One file a persona brings with them: the uploaded file (`ref`), what the persona and the target call it (`name`), what the persona knows about it (`description`), and which sessions carry it (`assign`).",
                  "properties": {
                    "ref": {
                      "description": "A file uploaded through POST /api/v1/files by the tenant starting the run: saram-file://<tenant_id>/<file_id>, where the owner segment is that tenant's id. A file uploaded before files were a tenant's carries the name of the caller that uploaded it there instead, and only that caller may name it.",
                      "pattern": "^saram-file://[a-z0-9][a-z0-9_-]{0,63}/f_[a-z2-7]{26}$",
                      "type": "string"
                    },
                    "name": {
                      "description": "What the persona calls it and what the target receives as the filename. Unique within the run.",
                      "pattern": "^[A-Za-z0-9][A-Za-z0-9 ._-]{0,127}$",
                      "type": "string"
                    },
                    "description": {
                      "description": "What the persona knows about it, in one sentence, briefed verbatim.",
                      "minLength": 1,
                      "type": "string"
                    },
                    "assign": {
                      "additionalProperties": false,
                      "description": "Which sessions carry it: those matching every list given. Absent means every session.",
                      "properties": {
                        "personas": {
                          "items": {
                            "type": "string"
                          },
                          "minItems": 1,
                          "type": "array"
                        },
                        "missions": {
                          "items": {
                            "pattern": "^[a-z0-9_]+$",
                            "type": "string"
                          },
                          "minItems": 1,
                          "type": "array"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "required": [
                    "ref",
                    "name",
                    "description"
                  ],
                  "type": "object"
                },
                "maxItems": 10,
                "type": "array"
              }
            },
            "required": [
              "target",
              "personas",
              "missions",
              "budget"
            ],
            "title": "RunSpec",
            "type": "object"
          }
        },
        "required": [
          "spec"
        ],
        "title": "validate_specArguments",
        "type": "object"
      },
      "outputSchema": {
        "$defs": {
          "CostCeiling": {
            "additionalProperties": false,
            "description": "What this run would cost: the most it can bill on your plan, as a number and the arithmetic behind it.\n\nA ceiling, not a forecast. Given on every plan and whatever the run gate is doing, because \"what would it\ncost?\" has an answer on every plan: the most Usage the spec's own budgets allow, priced the way your plan\nbills.",
            "properties": {
              "usd": {
                "description": "The most Saram can bill for this run on your plan, in US dollars: a ceiling, since every step is counted at the most it can be charged. A normal run bills well under it (a typical 5-second step is about 1 Usage, against the 3.5 a step can reach). 0 when your plan bills nothing. Model calls on your own provider are never in it: model_calls says who bills those.",
                "title": "Usd",
                "type": "number"
              },
              "billed_in": {
                "description": "What your plan bills: usage (prepaid Usage, the Free and Pro plans), steps (persona steps, the older per-step plans), or nothing.",
                "enum": [
                  "usage",
                  "steps",
                  "nothing"
                ],
                "title": "Billed In",
                "type": "string"
              },
              "plan": {
                "description": "The plan the figure is priced on.",
                "title": "Plan",
                "type": "string"
              },
              "price_usd": {
                "anyOf": [
                  {
                    "type": "number"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "Dollars per unit of billed_in: per Usage, or per persona step. Absent when billed_in is nothing.",
                "title": "Price Usd"
              },
              "sessions": {
                "description": "Sessions the spec plans, one per persona per mission.",
                "title": "Sessions",
                "type": "integer"
              },
              "steps_per_session": {
                "title": "Steps Per Session",
                "type": "integer"
              },
              "wall_clock_s_per_session": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "The spec's own wall-clock budget, which bounds the seconds a session's steps are charged for. Absent when it\nnames none.",
                "title": "Wall Clock S Per Session"
              },
              "usage_per_session": {
                "description": "The most Usage one session can use.",
                "title": "Usage Per Session",
                "type": "number"
              },
              "usage": {
                "description": "The run's Usage ceiling, sessions x usage_per_session, whatever your plan bills in.",
                "title": "Usage",
                "type": "number"
              },
              "basis": {
                "description": "One line saying how usd was reached: the sessions, the steps, the price, and what is included. Quote it with usd.",
                "title": "Basis",
                "type": "string"
              },
              "model_calls": {
                "description": "Who bills the run's model calls, per model role: your own provider key (billed to you by that provider), your ChatGPT login (within that subscription), or Saram's credential (not billed separately). Never part of usd.",
                "title": "Model Calls",
                "type": "string"
              }
            },
            "required": [
              "usd",
              "billed_in",
              "plan",
              "sessions",
              "steps_per_session",
              "usage_per_session",
              "usage",
              "basis",
              "model_calls"
            ],
            "title": "CostCeiling",
            "type": "object"
          },
          "PlannedSession": {
            "additionalProperties": false,
            "description": "One session `POST /runs` would create for this spec, planned the way the run itself plans, so the dry run\ncannot promise sessions the run would not make.",
            "properties": {
              "session_id": {
                "description": "`<run_id>.sess_NN_<persona slug>`. When the spec names no run, the prefix reads `<run_id>` literally -\n`POST /runs` is what mints the run id, and it is part of the session id. That is the same case as\n`SpecValidation.run_id` being absent.",
                "title": "Session Id",
                "type": "string"
              },
              "persona_id": {
                "title": "Persona Id",
                "type": "string"
              },
              "mission_id": {
                "title": "Mission Id",
                "type": "string"
              },
              "entry_url": {
                "description": "The mission's entry URL (or the target's) with this session's partition on it (`?saram_sid=`).",
                "title": "Entry Url",
                "type": "string"
              },
              "account_secret_ref": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "The seed account this session would be handed. Absent when the target's seed accounts ran out, and then the\npersona is told they have no login.",
                "title": "Account Secret Ref"
              }
            },
            "required": [
              "session_id",
              "persona_id",
              "mission_id",
              "entry_url"
            ],
            "title": "PlannedSession",
            "type": "object"
          },
          "UsageCeiling": {
            "additionalProperties": false,
            "description": "The most Usage a run on this spec can use, and the balance `POST /runs` would hold it against.\n\nComputed the way `POST /runs` holds the ceiling, at the same moment's balance, so a run this says is covered\nis one that starts - unless another run takes the balance between the two calls.",
            "properties": {
              "sessions": {
                "description": "Sessions the spec plans, one per persona per mission.",
                "title": "Sessions",
                "type": "integer"
              },
              "steps_per_session": {
                "title": "Steps Per Session",
                "type": "integer"
              },
              "wall_clock_s_per_session": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "The spec's own wall-clock budget. Absent when it names none, and then only the step budget bounds a\nsession: every step is counted at the most seconds a step is charged for.",
                "title": "Wall Clock S Per Session"
              },
              "usage_per_session": {
                "description": "The most Usage one session can use: every step at its most, or the wall clock spent in full.",
                "title": "Usage Per Session",
                "type": "number"
              },
              "usage": {
                "description": "`sessions x usage_per_session`: the run's ceiling, which `POST /runs` holds against the balance.",
                "title": "Usage",
                "type": "number"
              },
              "usage_usd": {
                "description": "The ceiling in dollars, at the plan's price per Usage.",
                "title": "Usage Usd",
                "type": "number"
              },
              "usage_price_usd": {
                "title": "Usage Price Usd",
                "type": "number"
              },
              "available_usd": {
                "description": "The balance less what the tenant's runs in flight already hold.",
                "title": "Available Usd",
                "type": "number"
              },
              "covered": {
                "description": "Whether `available_usd` covers `usage_usd`.",
                "title": "Covered",
                "type": "boolean"
              },
              "steps_per_session_that_fit": {
                "description": "The largest `budget.steps_per_session` that `available_usd` covers with the rest of the spec as written;\n0 when not even one step per session does.",
                "title": "Steps Per Session That Fit",
                "type": "integer"
              },
              "enforced": {
                "description": "Whether `POST /runs` refuses this spec with a 402 when `covered` is false. False while the gate runs in\nshadow, and always for Saram's own fleet.",
                "title": "Enforced",
                "type": "boolean"
              }
            },
            "required": [
              "sessions",
              "steps_per_session",
              "usage_per_session",
              "usage",
              "usage_usd",
              "usage_price_usd",
              "available_usd",
              "covered",
              "steps_per_session_that_fit",
              "enforced"
            ],
            "title": "UsageCeiling",
            "type": "object"
          }
        },
        "additionalProperties": false,
        "description": "`POST /api/v1/runs/validate`: what `POST /runs` would make of this spec, without making it.\n\nEverything `POST /runs` would refuse is the same 422 here: the contract, the cross-references\ninside `providers`, and the caller's own model provider - including a ChatGPT login the gateway no longer\nholds. A 200 means the spec is accepted as written.",
        "properties": {
          "run_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "The run id the spec carries. Absent means `POST /runs` would assign one.",
            "title": "Run Id"
          },
          "spec": {
            "additionalProperties": true,
            "description": "The spec as the run would store it.",
            "title": "Spec",
            "type": "object"
          },
          "sessions": {
            "items": {
              "$ref": "#/$defs/PlannedSession"
            },
            "title": "Sessions",
            "type": "array"
          },
          "warnings": {
            "description": "What the dry run can say about the spec but does not refuse it for, one sentence each, the ones about your ChatGPT login first. Today: providers entries on a ChatGPT login whose access token has lapsed (the gateway refreshes it on the run's first model call, and the run fails if that refresh is refused) or whose gateway this API could not ask; a providers entry whose model this platform's price table has no price for, which is either a model too new for the table or a misspelt id that fails at the run's first model call; and a budget.max_concurrent_sessions above what your plan runs at once, which the run honours by running fewer sessions at a time.",
            "items": {
              "type": "string"
            },
            "title": "Warnings",
            "type": "array"
          },
          "cost_ceiling": {
            "anyOf": [
              {
                "$ref": "#/$defs/CostCeiling"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "What the run would cost: the most it can bill on your plan (usd), with a one-line basis and who bills its model calls. A ceiling, not a forecast. On every plan; absent only for a caller this deployment has no tenant for."
          },
          "usage_ceiling": {
            "anyOf": [
              {
                "$ref": "#/$defs/UsageCeiling"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "The most Usage this run can use and the balance it is held against. Absent when this deployment's run gate is off, or your plan is not billed in Usage."
          }
        },
        "required": [
          "spec",
          "sessions"
        ],
        "title": "SpecValidation",
        "type": "object"
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "start_run",
      "description": "Start a run from a run spec. BILLABLE: this drives real browsers with real model calls and takes minutes - validate_spec first, and only start a run a person has just asked you for. Answers a run_id and a workflow_id and nothing else, so wait_for_run is the next call. The spec is posted exactly as you give it: no providers block is added for you (get_provider_status prints one to paste), no credential_ref is rewritten and no budget is changed. The two fields it fills in when you leave them out are trigger.kind, which becomes 'api', and trigger.actor, which becomes your caller name. Refuses a second run while one you started is still in flight, unless you pass allow_concurrent. A run your plan cannot pay for is refused with an error whose structured content is {detail, usage_ceiling} or {detail, monthly_cap}: usage_ceiling, present when the balance does not cover the run, is the one validate_spec answers; monthly_cap, present when the month has billed your monthly cap, is {period, billed_usd, cap_usd}.",
      "inputSchema": {
        "properties": {
          "spec": {
            "additionalProperties": false,
            "description": "A run spec, the RunSpec contract, as validate_spec accepted it.",
            "examples": [
              {
                "budget": {
                  "steps_per_session": 30
                },
                "missions": [
                  {
                    "goal": "create an account",
                    "id": "sign_up",
                    "success_criteria": [
                      {
                        "kind": "judge_assertion",
                        "value": "the persona reached a signed-in page"
                      }
                    ]
                  }
                ],
                "personas": [
                  "dana_first_timer@1"
                ],
                "target": {
                  "entry_url": "https://example.com"
                }
              }
            ],
            "properties": {
              "run_id": {
                "description": "Assigned by the control plane if absent",
                "type": "string"
              },
              "trigger": {
                "additionalProperties": false,
                "description": "Who or what started the run, and against which code: a record kept with the run, which changes nothing about what it does but in two places. A run billed to a ChatGPT subscription login is refused when `kind` is `ci`, and `pr` and `commit` are copied onto the artifact's `target`, where `pr` is the pull request the GitHub connector's comment goes on.",
                "properties": {
                  "kind": {
                    "description": "How the run was started: `manual`, by a person at a keyboard; `api`, through the API or a coding agent's MCP tools, which is what `start_run` writes when the spec leaves it out; `ci`, by a CI job with nobody watching. The caller's own claim, recorded as given. `ci` is refused on a run billed to a ChatGPT subscription login, which serves only runs a person starts.",
                    "enum": [
                      "manual",
                      "api",
                      "ci"
                    ],
                    "type": "string"
                  },
                  "actor": {
                    "description": "User, token, or CI job that triggered the run",
                    "type": "string"
                  },
                  "pr": {
                    "type": "integer"
                  },
                  "commit": {
                    "type": "string"
                  },
                  "diff_ref": {
                    "description": "Where the PR diff can be fetched, for diff-aware targeting. Accepted and recorded; nothing reads it yet.",
                    "type": "string"
                  }
                },
                "type": "object"
              },
              "target": {
                "additionalProperties": false,
                "description": "The product under test: the URL every session opens on (a mission's own `entry_url` overrides it), a name for it, the tunnel to it when it runs on your own machine (`tunnel`), and, when it needs them, how a session gets past a gate in front of the whole deployment (`auth`) and how the product's state is reset and its test accounts handed out between sessions (`seed`).",
                "properties": {
                  "entry_url": {
                    "format": "uri",
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "tunnel": {
                    "description": "For a product running on your own machine: the id of the tunnel you opened to it (`tun_...`), which Saram's browsers reach it through and nothing else can. Not available yet: until Saram opens and binds tunnels, every spec naming one is refused with a 422 (`tunnels to your own machine are not available yet`) by `POST /api/v1/runs`, `POST /api/v1/runs/validate` and the `validate_spec` and `start_run` tools, before any run exists. Naming it binds that tunnel to this run, and the spec is refused unless the tunnel is one the tenant starting the run opened, is still open and is bound to no other run, and every URL the run opens on the target - `entry_url`, each mission's `entry_url` and `seed.reset_hook` - is on the tunnel's hostname. Saram puts the tunnel's own door in front of the run, so a spec naming a tunnel carries no `auth` of its own. The tunnel closes when the run ends.",
                    "pattern": "^tun_[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9a-f]{8}$",
                    "type": "string"
                  },
                  "auth": {
                    "additionalProperties": false,
                    "description": "How a session gets past the target's own door: the gate in front of the whole deployment, not the product's sign-in. `auth` and `seed.accounts` are one mechanism seen from two ends and both resolve `secret-store` entries; which end a credential belongs at is decided by whether it is the target's or the persona's. **The target's** (one for the run, the same for every session, nothing a person would be shown): `basic` on a staging host, a deployment-protection bypass `header` on a Vercel or Cloudflare Access preview, a `cookie` the whole run shares. That is this object: the pool receives `secret_ref` as `session_create.target_auth_secret_ref` and applies the entry's `basic` and `headers` to requests to the target's site (`secret-store` `entry.basic`) and its `cookies` to the browser context, and the control plane sends the same entry's `basic`, `headers` and the `cookies` scoped to the hook on its POST to `seed.reset_hook`, which is behind the same door. **The persona's** (one per session, and a persona may be asked to type it): `seed.accounts`, one ref per session, where the pool applies the entry's `cookies` and the prompt renderer reads `email`, `password`, `inbox` and `notes`. A run can carry both - a staging host behind basic auth whose personas each sign in with their own account - and where the two name the same credential the account's wins. Closed by design: credentials arrive only as `secret_ref`, so a field this object does not list is an inline secret and every reader rejects it, not just the producer.",
                    "properties": {
                      "kind": {
                        "description": "What the door is, for a reader of the spec. It selects nothing: the pool applies whatever the entry behind `secret_ref` carries, so `basic` and `header` on one entry are both applied whichever of the two this says. `none` is a target with no gate (`secret_ref` then says nothing and is omitted); `login_flow` is the one kind that is not a gate at all but a form the persona fills in, which is `seed.accounts` and `login_url`.",
                        "enum": [
                          "none",
                          "basic",
                          "cookie",
                          "header",
                          "login_flow"
                        ],
                        "type": "string"
                      },
                      "secret_ref": {
                        "description": "Reference into the secret store; never inline credentials. Carried to the pool as `session_create.target_auth_secret_ref`, once per session, unchanged; the pool resolves it and applies `basic` and `headers` to requests to the target's site (`secret-store` `entry.basic`) and `cookies` on the context. The same ref for every session of the run - a per-session credential is `seed.accounts`, not this.",
                        "type": "string"
                      },
                      "login_url": {
                        "description": "Where the product's own sign-in form is, with `kind: login_flow`. Descriptive only: the persona is given the account behind `seed.accounts` and finds the form itself, which is the behaviour under test, so nothing navigates here.",
                        "format": "uri",
                        "type": "string"
                      }
                    },
                    "type": "object",
                    "x-closed": true
                  },
                  "seed": {
                    "additionalProperties": false,
                    "description": "Environment provisioning. Every e2e product bleeds here; make it explicit. Closed by design (x-closed) for the same reason as `auth`: `accounts` are secret refs and nothing inline may ride beside them.",
                    "properties": {
                      "reset_hook": {
                        "description": "Endpoint to reset state before each session. The control plane POSTs it once per run with `{\"feature_flags\": ...}` (the whole target), before each session with `session_id` added (that session's partition), and before each reproduction replay attempt the same way. It sits behind the target's own door like every other url on the host, so the POST carries the entry behind `auth.secret_ref`: its `basic` as HTTP basic auth, its `headers` verbatim and, as one `Cookie` header, those of its `cookies` the browser would send to the hook's url - the same credential the pool opens the browser's door with. A cookie goes when its scope matches the hook as Playwright scopes it in the pool: a `url` names its host alone, that url's path up to its last `/`, and https only; otherwise `domain` (with a leading dot, every subdomain too) and `path` (a prefix ending at a `/`) do, and `secure` keeps it off plain http. A cookie scoped to another host or path is not sent, and neither is any cookie when `headers` already name `Cookie`. A hook that answers anything but 2xx fails the reset.",
                        "format": "uri",
                        "type": "string"
                      },
                      "accounts": {
                        "description": "Pool of test account secret_refs, one per session, handed out in session order; sessions past the end of the list get none and the persona is told they have no login. The other half of `auth` (see its description for the split): the persona's credential rather than the target's. Each ref reaches the pool as `session_create.auth_secret_ref`, which applies the entry's `cookies` (and `basic`/`headers`, for the rare account that overrides the target's own), while the renderer tells the persona which credentials they 'have' (email, password) and which they do not (inbox access). Never inline.",
                        "items": {
                          "type": "string"
                        },
                        "type": "array"
                      },
                      "feature_flags": {
                        "additionalProperties": {
                          "type": "boolean"
                        },
                        "type": "object"
                      }
                    },
                    "type": "object",
                    "x-closed": true
                  }
                },
                "required": [
                  "entry_url"
                ],
                "type": "object"
              },
              "personas": {
                "items": {
                  "oneOf": [
                    {
                      "description": "persona id@version from the registry, e.g. novice_mobile_skimmer@2",
                      "type": "string"
                    },
                    {
                      "description": "A persona written inline, the Persona contract (get_persona answers one). The contract allows it and a run refuses it: only registry personas are supported, so name each by its id@version address from list_personas.",
                      "type": "object"
                    }
                  ]
                },
                "minItems": 1,
                "type": "array"
              },
              "missions": {
                "items": {
                  "additionalProperties": false,
                  "description": "One thing each persona is sent to do: a goal in the persona's own terms, how to tell it was achieved, and optionally where to start, what the judge should watch for and the question the persona answers at the end.",
                  "properties": {
                    "id": {
                      "pattern": "^[a-z0-9_]+$",
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "entry_url": {
                      "description": "Overrides target.entry_url",
                      "format": "uri",
                      "type": "string"
                    },
                    "goal": {
                      "description": "Stated in terms a persona would understand, e.g. 'buy one of the blue shirts'",
                      "type": "string"
                    },
                    "success_criteria": {
                      "description": "What reaching the goal looks like. The persona is never shown these: it is given `goal` and `brief` and decides for itself when it is finished. Only a `url_matches` criterion is checked by machine; every other kind is context for the judge. See `kind`.",
                      "items": {
                        "additionalProperties": false,
                        "description": "One way to tell the goal was reached: what sort of evidence it is (`kind`) and what to look for (`value`). Only a `url_matches` criterion is checked by machine; see `kind`.",
                        "properties": {
                          "kind": {
                            "description": "What the criterion states and who checks it. `url_matches` is the only kind checked by machine: `value` is part of a URL, e.g. `/order/confirmed`, and when the persona says it is finished the session ends with `end_reason` `success_criteria_met` if `value` occurs in the last URL the browser was on, or `claimed_finished_unverified` if it does not (always, for a mission with no `url_matches` criterion). Nothing checks the other four: the judge that reads each session afterwards is shown every criterion as text, `<kind>=<value>`, beside the mission's goal, and nothing decides whether one was met. `text_visible`: text the page shows once the goal is reached. `element_visible`: an element on that page, in words or as a selector. `network_call`: a request the product makes when the goal is reached, e.g. `POST /api/orders`. `judge_assertion`: a plain-language statement about how the session ended, e.g. `the persona reached a signed-in page`. For a verified ending, give a mission a `url_matches` criterion beside any of these.",
                            "enum": [
                              "url_matches",
                              "text_visible",
                              "element_visible",
                              "network_call",
                              "judge_assertion"
                            ],
                            "type": "string"
                          },
                          "value": {
                            "description": "Read according to `kind`: part of a URL for `url_matches`, otherwise text for the judge.",
                            "type": "string"
                          }
                        },
                        "required": [
                          "kind",
                          "value"
                        ],
                        "type": "object"
                      },
                      "minItems": 1,
                      "type": "array"
                    },
                    "watch_list": {
                      "description": "Specific concerns the judge should look for, e.g. 'is the new discount widget discoverable'. The only place free opinions may be solicited: the persona itself is never asked to critique the product.",
                      "items": {
                        "type": "string"
                      },
                      "type": "array"
                    },
                    "authored_by": {
                      "default": "human",
                      "description": "Who wrote the mission: `human`, the default, for one a person wrote; `doppelganger` and `planner` for one an agent wrote on someone's behalf. A label only: it is kept with the run and changes nothing about how the mission is run.",
                      "enum": [
                        "human",
                        "doppelganger",
                        "planner"
                      ],
                      "type": "string"
                    },
                    "brief": {
                      "description": "Optional context from the mission author to the persona (what they were told before starting)",
                      "type": "string"
                    },
                    "verdict_prompt": {
                      "description": "The question about THIS product that the persona answers in their final diary entry, asked after the persona's own `persona.verdict_prompt`. Set it whenever the personas were written for a different product than the one under test: a roofing persona asked 'is this going to bring you roofing jobs' has nothing to answer with after a shirt-shop session, and the verdict comes back null. When present, this is the question the judge answers and `persona.verdict_prompt` is not applicable for the run.",
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "goal",
                    "success_criteria"
                  ],
                  "type": "object"
                },
                "minItems": 1,
                "type": "array"
              },
              "budget": {
                "additionalProperties": false,
                "description": "How far each session may go and how much the run may spend. A session ends at the first of its caps it reaches: `steps_per_session` steps (`end_reason` `budget_steps`), `tokens_per_session` model tokens billed, which is the prompt not read from cache plus the reply (`budget_tokens`), or `wall_clock_s_per_session` seconds (`budget_wall_clock`). `max_concurrent_sessions` is the most of the run's sessions that run at once, and your plan's own limit bounds it. `total_cost_usd` pauses the run once the sessions it has recorded have cost that much: no further session starts until it is resumed, and the ones already running finish.",
                "properties": {
                  "steps_per_session": {
                    "minimum": 1,
                    "type": "integer"
                  },
                  "tokens_per_session": {
                    "minimum": 1,
                    "type": "integer"
                  },
                  "wall_clock_s_per_session": {
                    "minimum": 1,
                    "type": "integer"
                  },
                  "max_concurrent_sessions": {
                    "minimum": 1,
                    "type": "integer"
                  },
                  "total_cost_usd": {
                    "description": "Soft cap; run pauses when reached",
                    "minimum": 0,
                    "type": "number"
                  }
                },
                "required": [
                  "steps_per_session"
                ],
                "type": "object"
              },
              "baseline_run_id": {
                "description": "Run to diff against, typically the last run on main",
                "type": "string"
              },
              "providers": {
                "additionalProperties": false,
                "description": "Which model, on which provider or router, with which credential, every model call in this run uses. `entries` are named; `default` names the entry every role uses unless `roles` says otherwise; `roles` binds the three callers (the actor loop, the observer's judge for session judgment and run merge, the reproduction model check) to entry names. A role that resolves to no entry is a producer error. When this block is present it wins over `actor.actor_model` and `actor.judge_model`; when absent, those two and the worker's own process credential apply, which is every run written before 0.4.1. Resolved by the worker that makes the call, at the start of its activity, never by the API or a workflow.",
                "properties": {
                  "entries": {
                    "additionalProperties": {
                      "additionalProperties": false,
                      "description": "One model on one provider or router, in pydantic-ai's own terms: `model` is `<provider>:<model>` and the prefix decides how the rest is read. Closed by design: a credential arrives only as `credential_ref`, so a field this object does not list is an inline secret and every reader rejects it.",
                      "properties": {
                        "model": {
                          "description": "pydantic-ai model string: anthropic:claude-opus-5, bedrock:us.anthropic.claude-opus-5, openai:gpt-5.6, openrouter:anthropic/claude-opus-5, azure:<deployment>, google:gemini-3-pro, google-cloud:gemini-3-pro, litellm:<model>.",
                          "pattern": "^[a-z][a-z0-9-]*:.+$",
                          "type": "string"
                        },
                        "credential_ref": {
                          "description": "Reference to the credential, never the credential: `env://NAME`, `awssm://<secret name>`. Absent means the provider's ambient credential (IRSA or instance role on bedrock, Application Default Credentials on google-cloud); for prefixes that have no ambient path, absent is a producer error at resolution. One secret holds one string; what the string is (API key, bearer token, service-account JSON) is fixed by the prefix.",
                          "pattern": "^[a-z][a-z0-9+.-]*://.+$",
                          "type": "string"
                        },
                        "base_url": {
                          "description": "Endpoint override: a gateway (LiteLLM, a private OpenAI-compatible URL), an Azure resource endpoint, a Bedrock VPC endpoint. Absent means the provider's public endpoint.",
                          "format": "uri",
                          "type": "string"
                        },
                        "region": {
                          "description": "bedrock: the AWS region the model is invoked in. Absent means AWS_REGION / AWS_DEFAULT_REGION on the worker.",
                          "type": "string"
                        },
                        "project": {
                          "description": "google-cloud: the GCP project. Absent means the one Application Default Credentials carry.",
                          "type": "string"
                        },
                        "location": {
                          "description": "google-cloud: the Vertex location, e.g. us-central1 or global.",
                          "type": "string"
                        },
                        "api_version": {
                          "description": "azure: the Azure OpenAI api-version. Absent means OPENAI_API_VERSION on the worker.",
                          "type": "string"
                        }
                      },
                      "required": [
                        "model"
                      ],
                      "type": "object",
                      "x-closed": true
                    },
                    "minProperties": 1,
                    "propertyNames": {
                      "pattern": "^[a-z0-9][a-z0-9_-]{0,63}$"
                    },
                    "type": "object"
                  },
                  "default": {
                    "type": "string",
                    "description": "Entry name. Absent with exactly one entry means that entry."
                  },
                  "roles": {
                    "additionalProperties": false,
                    "description": "Which entry, by name, each of the run's three model callers uses: `actor`, the model that plays the persona; `judge`, the judge that reads each session and merges the run's findings; `reproduction`, the model check made when an issue is replayed. A role left out uses `default`.",
                    "properties": {
                      "actor": {
                        "type": "string"
                      },
                      "judge": {
                        "type": "string"
                      },
                      "reproduction": {
                        "type": "string"
                      }
                    },
                    "type": "object"
                  }
                },
                "required": [
                  "entries"
                ],
                "type": "object"
              },
              "actor": {
                "additionalProperties": false,
                "description": "How the persona sees the page and which models drive it: the observation mode, the persona and judge models, when a screenshot is taken, and whether the session is recorded on video.",
                "properties": {
                  "observation_mode": {
                    "default": "vision",
                    "description": "How the persona perceives the page. `vision`, the default and the only mode supported today: the persona sees a screenshot of the page when it chooses to look. `tree`, `dom_diff` and `hybrid` are reserved for modes that read the page's structure instead of, or beside, a screenshot, and nothing runs them yet: a spec naming one is refused with a 422 by `POST /api/v1/runs`, `POST /api/v1/runs/validate` and the `validate_spec` and `start_run` tools, before any run exists.",
                    "enum": [
                      "vision",
                      "tree",
                      "dom_diff",
                      "hybrid"
                    ],
                    "type": "string"
                  },
                  "actor_model": {
                    "description": "Model id for navigation steps",
                    "type": "string"
                  },
                  "judge_model": {
                    "description": "Model id for the observer",
                    "type": "string"
                  },
                  "screenshot_policy": {
                    "default": "on_look",
                    "description": "`on_look`: the persona sees a screenshot only when it chooses to look at the page. `every_step`: a screenshot after every action is also kept in the session's trace for the judge.",
                    "enum": [
                      "on_look",
                      "every_step",
                      "decision_points",
                      "on_escalation"
                    ],
                    "type": "string"
                  },
                  "record_video": {
                    "default": true,
                    "type": "boolean"
                  }
                },
                "type": "object"
              },
              "connectors": {
                "additionalProperties": false,
                "description": "Where the run's results are delivered once its artifact is assembled: `github` files issues and comments on a pull request in your repository, and `webhook` sends one signed POST to a URL of yours. Both are optional; a run with neither is read through the API, the dashboard and the MCP tools.",
                "properties": {
                  "github": {
                    "additionalProperties": false,
                    "description": "Deliver the run to the GitHub repository `repo` (`owner/name`). `create_issues` files a GitHub issue for each issue that reproduced, is not specific to one persona and is rated at least `min_severity_for_issue`; `pr_comment` posts one comment on the run on the pull request named in `trigger.pr`, created once and updated after that. Both are off by default, so a block that turns neither on writes nothing. `check_run` is accepted and nothing posts a check run yet. Delivery writes as Saram's GitHub App where it is installed on `repo`, and otherwise with `token_ref`.",
                    "properties": {
                      "repo": {
                        "type": "string"
                      },
                      "create_issues": {
                        "default": false,
                        "type": "boolean"
                      },
                      "pr_comment": {
                        "default": false,
                        "type": "boolean"
                      },
                      "check_run": {
                        "default": false,
                        "type": "boolean"
                      },
                      "min_severity_for_issue": {
                        "default": "medium",
                        "description": "The least severe issue `create_issues` files, from least to most: `low`, `medium` (the default), `high`, `critical`. An issue rated below it stays in the run's artifact and is not filed.",
                        "enum": [
                          "low",
                          "medium",
                          "high",
                          "critical"
                        ],
                        "type": "string"
                      },
                      "token_ref": {
                        "description": "Reference to a GitHub token that may write issues and comments on `repo`, never the token: `awssm://<secret name>` under the requesting tenant's own prefix (a credential of kind `github`, POST /api/v1/credentials), or `env://NAME` on an ungated local stack. The fallback, not the connector's identity: delivery authenticates as the deployment's GitHub App installation on `repo` and reads this only when the App is not installed there. With neither, every record the delivery would have written fails with that reason; no other credential is tried, so a run never writes to its repo as somebody else. Resolved by the worker that delivers, at delivery.",
                        "pattern": "^[a-z][a-z0-9+.-]*://.+$",
                        "type": "string"
                      }
                    },
                    "type": "object"
                  },
                  "webhook": {
                    "additionalProperties": false,
                    "description": "One signed POST when the run has finished and its artifact is assembled: a small JSON summary (run id, status, session and issue counts, links to the run and the artifact), so a CI job or a deploy pipeline can act on a run without polling. Sent once, after the run's row is closed, by an activity that never fails the run: a receiver that answers 5xx, 429 or nothing is retried a bounded number of times and then given up on, and the outcome is recorded on the run. A run that is cancelled, or that fails before an artifact exists, sends nothing. Closed by design: the signing key arrives only as `secret_ref`, so a field this object does not list is an inline secret and every reader rejects it.",
                    "properties": {
                      "url": {
                        "description": "Where the POST goes. `https` only; the control plane refuses a URL naming a loopback, private, link-local or otherwise non-public address, whether written as one or resolved to one at delivery, unless the deployment allows private hosts (a local stack), and then `http` is accepted for a loopback host too. Redirects are not followed.",
                        "format": "uri",
                        "type": "string"
                      },
                      "secret_ref": {
                        "description": "Reference to the HMAC-SHA256 key the body is signed with, never the key: `awssm://<secret name>` under the requesting tenant's own prefix (a credential of kind `webhook`, POST /api/v1/credentials), or `env://NAME` on an ungated local stack. Resolved by the worker that delivers, at delivery. The signature is `Saram-Signature: t=<unix seconds>,v1=<hex>`, the digest being HMAC-SHA256 over `<t>.<raw body>` - the scheme Stripe uses - so a receiver verifies the raw bytes it received and refuses a timestamp outside its own tolerance.",
                        "pattern": "^[a-z][a-z0-9+.-]*://.+$",
                        "type": "string"
                      }
                    },
                    "required": [
                      "url",
                      "secret_ref"
                    ],
                    "type": "object",
                    "x-closed": true
                  },
                  "webhook_url": {
                    "description": "Never read. Reserved since 0.3 and superseded by `webhook` (0.6.0), which carries the signing key a bare URL has no room for; kept so a spec that sets it still validates.",
                    "format": "uri",
                    "type": "string"
                  }
                },
                "type": "object"
              },
              "files": {
                "description": "Files the personas bring with them. Bytes never travel in the spec: upload each file with POST /api/v1/files first and name it here by the `ref` that answers. A file belongs to the tenant that uploaded it, so any key or member of that tenant may name it in a run and nobody else may.",
                "items": {
                  "additionalProperties": false,
                  "description": "One file a persona brings with them: the uploaded file (`ref`), what the persona and the target call it (`name`), what the persona knows about it (`description`), and which sessions carry it (`assign`).",
                  "properties": {
                    "ref": {
                      "description": "A file uploaded through POST /api/v1/files by the tenant starting the run: saram-file://<tenant_id>/<file_id>, where the owner segment is that tenant's id. A file uploaded before files were a tenant's carries the name of the caller that uploaded it there instead, and only that caller may name it.",
                      "pattern": "^saram-file://[a-z0-9][a-z0-9_-]{0,63}/f_[a-z2-7]{26}$",
                      "type": "string"
                    },
                    "name": {
                      "description": "What the persona calls it and what the target receives as the filename. Unique within the run.",
                      "pattern": "^[A-Za-z0-9][A-Za-z0-9 ._-]{0,127}$",
                      "type": "string"
                    },
                    "description": {
                      "description": "What the persona knows about it, in one sentence, briefed verbatim.",
                      "minLength": 1,
                      "type": "string"
                    },
                    "assign": {
                      "additionalProperties": false,
                      "description": "Which sessions carry it: those matching every list given. Absent means every session.",
                      "properties": {
                        "personas": {
                          "items": {
                            "type": "string"
                          },
                          "minItems": 1,
                          "type": "array"
                        },
                        "missions": {
                          "items": {
                            "pattern": "^[a-z0-9_]+$",
                            "type": "string"
                          },
                          "minItems": 1,
                          "type": "array"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "required": [
                    "ref",
                    "name",
                    "description"
                  ],
                  "type": "object"
                },
                "maxItems": 10,
                "type": "array"
              }
            },
            "required": [
              "target",
              "personas",
              "missions",
              "budget"
            ],
            "title": "RunSpec",
            "type": "object"
          },
          "allow_concurrent": {
            "default": false,
            "description": "Start this run even though one you started is still in flight. Say true only because a person asked for both runs.",
            "title": "Allow Concurrent",
            "type": "boolean"
          }
        },
        "required": [
          "spec"
        ],
        "title": "start_runArguments",
        "type": "object"
      },
      "outputSchema": {
        "additionalProperties": false,
        "description": "A run accepted and started. Follow it by its `run_id`: `GET /api/v1/runs/{run_id}`, or the MCP tools\n`get_run_status` and `wait_for_run`.",
        "properties": {
          "run_id": {
            "description": "The run's id: the handle every other run route and tool takes.",
            "title": "Run Id",
            "type": "string"
          },
          "workflow_id": {
            "description": "The id of the workflow executing the run, for support requests.",
            "title": "Workflow Id",
            "type": "string"
          }
        },
        "required": [
          "run_id",
          "workflow_id"
        ],
        "title": "StartedRun",
        "type": "object"
      },
      "annotations": {
        "readOnlyHint": false,
        "openWorldHint": true
      }
    },
    {
      "name": "get_run_status",
      "description": "Where a run has got to: its status, whether it is done, how many sessions are in each state, what it has cost, and for each session that has not finished the heartbeat_step it last reported - the only field that says where a session in flight is. A finished session says whose failure ended it (fault), and one that errored says why (end_reason). To wait for a run rather than watch it, use wait_for_run; get_run is the same run with its spec, budgets and trace refs.",
      "inputSchema": {
        "properties": {
          "run_id": {
            "description": "Run id, as start_run returned it.",
            "title": "Run Id",
            "type": "string"
          }
        },
        "required": [
          "run_id"
        ],
        "title": "get_run_statusArguments",
        "type": "object"
      },
      "outputSchema": {
        "$defs": {
          "SessionProgress": {
            "additionalProperties": false,
            "description": "One session of a run in flight, narrowed to what says where it has got to.",
            "properties": {
              "session_id": {
                "title": "Session Id",
                "type": "string"
              },
              "persona_id": {
                "title": "Persona Id",
                "type": "string"
              },
              "status": {
                "description": "`pending`, an artifact result, or `cancelled`. A session that is on a step right now still reads\n`pending` - a session is recorded when it ends - so `heartbeat_step` is what says it is alive, and\n`waiting_for_seat` what says it is queued for a browser seat instead.",
                "title": "Status",
                "type": "string"
              },
              "result": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Result"
              },
              "fault": {
                "anyOf": [
                  {
                    "enum": [
                      "none",
                      "saram",
                      "provider",
                      "target"
                    ],
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "Whose failure ended the session, as `SessionRow.fault` says it: `none` for an ending the persona or the\nloop reached, `saram`, `provider` or `target` on an `errored` one. Absent while the session is open, and on\na session recorded before this was classified - never read it as `none`.",
                "title": "Fault"
              },
              "end_reason": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "Why an `errored` session ended, on one line: `SessionRow.end_reason` with its line breaks and runs of\nwhitespace folded to single spaces, and nothing cut. Absent on every other result - those are the persona's\nown endings, and `get_run` has them.",
                "title": "End Reason"
              },
              "heartbeat_step": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "LIVE. The `step_id` the persona last reported: where a session in flight has got to, since `status` does\nnot say. Best effort - absent on a finished session, and on a running one whose live state cannot be read.",
                "title": "Heartbeat Step"
              },
              "last_heartbeat_at": {
                "anyOf": [
                  {
                    "format": "date-time",
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "When that heartbeat reached the server: how stale `heartbeat_step` is. Absent whenever it is.",
                "title": "Last Heartbeat At"
              },
              "waiting_for_seat": {
                "anyOf": [
                  {
                    "type": "boolean"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "LIVE. `true` while this session is queued for a browser seat: every seat in the pool is taken, so it takes\nno steps until one frees, and then starts without spending an attempt. `false` while it is setting up, on a\nseat, or paused. Best effort as `heartbeat_step`, and also absent on a session the run has not started yet\n(its `max_concurrent_sessions` holds it back). A session resuming after a pause or a pool failure queues the\nsame way, so it can be `true` beside a `heartbeat_step`.",
                "title": "Waiting For Seat"
              },
              "waiting_since": {
                "anyOf": [
                  {
                    "format": "date-time",
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "When the session was queued for the seat it is waiting for: how long it has waited. Absent whenever\n`waiting_for_seat` is not `true`.",
                "title": "Waiting Since"
              }
            },
            "required": [
              "session_id",
              "persona_id",
              "status"
            ],
            "title": "SessionProgress",
            "type": "object"
          }
        },
        "additionalProperties": false,
        "description": "`get_run_status` and `wait_for_run`: is this run finished, and if not, where has it got to.\n\n`RunDetail` narrowed to the fields an agent polls on. The spec, the budgets, the trace refs and the per\nsession tokens are all on `get_run` and are not repeated into a payload a client may fetch a dozen times.",
        "properties": {
          "run_id": {
            "title": "Run Id",
            "type": "string"
          },
          "status": {
            "description": "`pending`, `running`, `observing`, then one terminal value - the same vocabulary `RunSummary.status`\ncarries, with the same meaning. Read `done` rather than comparing against a list of them.",
            "title": "Status",
            "type": "string"
          },
          "done": {
            "description": "Whether `status` is terminal (`completed`, `cancelled`, `failed`), i.e. nothing more will happen to this\nrun. What a polling agent loops on; `status` then says how it ended and `completed` is the only one with\nresults to read.",
            "title": "Done",
            "type": "boolean"
          },
          "status_reason": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Why the run ended `failed` or `cancelled`, as `RunSummary.status_reason` says it. Absent otherwise.",
            "title": "Status Reason"
          },
          "session_counts": {
            "additionalProperties": {
              "type": "integer"
            },
            "title": "Session Counts",
            "type": "object"
          },
          "cost_usd": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Cost Usd"
          },
          "artifact_ref": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Set once the artifact is assembled, which is what makes the issue and feedback views non-empty.",
            "title": "Artifact Ref"
          },
          "sessions": {
            "items": {
              "$ref": "#/$defs/SessionProgress"
            },
            "title": "Sessions",
            "type": "array"
          },
          "created_at": {
            "format": "date-time",
            "title": "Created At",
            "type": "string"
          },
          "started_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Started At"
          },
          "finished_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Finished At"
          }
        },
        "required": [
          "run_id",
          "status",
          "done",
          "created_at"
        ],
        "title": "RunProgress",
        "type": "object"
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "wait_for_run",
      "description": "Wait for a run to finish, then answer exactly what get_run_status answers. Returns as soon as the run reaches a terminal status, or after timeout_s if it has not - so done=false means the timeout won, not that anything went wrong, and the way to keep waiting is to call this again. One call is one HTTP request, which is why timeout_s stops at 60; a run of any size takes several calls. Between them, say what the run is doing rather than waiting silently.",
      "inputSchema": {
        "properties": {
          "run_id": {
            "description": "Run id, as start_run returned it.",
            "title": "Run Id",
            "type": "string"
          },
          "timeout_s": {
            "default": 60,
            "description": "How long to wait, in seconds, at most 60.",
            "maximum": 60,
            "minimum": 1,
            "title": "Timeout S",
            "type": "integer"
          }
        },
        "required": [
          "run_id"
        ],
        "title": "wait_for_runArguments",
        "type": "object"
      },
      "outputSchema": {
        "$defs": {
          "SessionProgress": {
            "additionalProperties": false,
            "description": "One session of a run in flight, narrowed to what says where it has got to.",
            "properties": {
              "session_id": {
                "title": "Session Id",
                "type": "string"
              },
              "persona_id": {
                "title": "Persona Id",
                "type": "string"
              },
              "status": {
                "description": "`pending`, an artifact result, or `cancelled`. A session that is on a step right now still reads\n`pending` - a session is recorded when it ends - so `heartbeat_step` is what says it is alive, and\n`waiting_for_seat` what says it is queued for a browser seat instead.",
                "title": "Status",
                "type": "string"
              },
              "result": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Result"
              },
              "fault": {
                "anyOf": [
                  {
                    "enum": [
                      "none",
                      "saram",
                      "provider",
                      "target"
                    ],
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "Whose failure ended the session, as `SessionRow.fault` says it: `none` for an ending the persona or the\nloop reached, `saram`, `provider` or `target` on an `errored` one. Absent while the session is open, and on\na session recorded before this was classified - never read it as `none`.",
                "title": "Fault"
              },
              "end_reason": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "Why an `errored` session ended, on one line: `SessionRow.end_reason` with its line breaks and runs of\nwhitespace folded to single spaces, and nothing cut. Absent on every other result - those are the persona's\nown endings, and `get_run` has them.",
                "title": "End Reason"
              },
              "heartbeat_step": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "LIVE. The `step_id` the persona last reported: where a session in flight has got to, since `status` does\nnot say. Best effort - absent on a finished session, and on a running one whose live state cannot be read.",
                "title": "Heartbeat Step"
              },
              "last_heartbeat_at": {
                "anyOf": [
                  {
                    "format": "date-time",
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "When that heartbeat reached the server: how stale `heartbeat_step` is. Absent whenever it is.",
                "title": "Last Heartbeat At"
              },
              "waiting_for_seat": {
                "anyOf": [
                  {
                    "type": "boolean"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "LIVE. `true` while this session is queued for a browser seat: every seat in the pool is taken, so it takes\nno steps until one frees, and then starts without spending an attempt. `false` while it is setting up, on a\nseat, or paused. Best effort as `heartbeat_step`, and also absent on a session the run has not started yet\n(its `max_concurrent_sessions` holds it back). A session resuming after a pause or a pool failure queues the\nsame way, so it can be `true` beside a `heartbeat_step`.",
                "title": "Waiting For Seat"
              },
              "waiting_since": {
                "anyOf": [
                  {
                    "format": "date-time",
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "When the session was queued for the seat it is waiting for: how long it has waited. Absent whenever\n`waiting_for_seat` is not `true`.",
                "title": "Waiting Since"
              }
            },
            "required": [
              "session_id",
              "persona_id",
              "status"
            ],
            "title": "SessionProgress",
            "type": "object"
          }
        },
        "additionalProperties": false,
        "description": "`get_run_status` and `wait_for_run`: is this run finished, and if not, where has it got to.\n\n`RunDetail` narrowed to the fields an agent polls on. The spec, the budgets, the trace refs and the per\nsession tokens are all on `get_run` and are not repeated into a payload a client may fetch a dozen times.",
        "properties": {
          "run_id": {
            "title": "Run Id",
            "type": "string"
          },
          "status": {
            "description": "`pending`, `running`, `observing`, then one terminal value - the same vocabulary `RunSummary.status`\ncarries, with the same meaning. Read `done` rather than comparing against a list of them.",
            "title": "Status",
            "type": "string"
          },
          "done": {
            "description": "Whether `status` is terminal (`completed`, `cancelled`, `failed`), i.e. nothing more will happen to this\nrun. What a polling agent loops on; `status` then says how it ended and `completed` is the only one with\nresults to read.",
            "title": "Done",
            "type": "boolean"
          },
          "status_reason": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Why the run ended `failed` or `cancelled`, as `RunSummary.status_reason` says it. Absent otherwise.",
            "title": "Status Reason"
          },
          "session_counts": {
            "additionalProperties": {
              "type": "integer"
            },
            "title": "Session Counts",
            "type": "object"
          },
          "cost_usd": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Cost Usd"
          },
          "artifact_ref": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Set once the artifact is assembled, which is what makes the issue and feedback views non-empty.",
            "title": "Artifact Ref"
          },
          "sessions": {
            "items": {
              "$ref": "#/$defs/SessionProgress"
            },
            "title": "Sessions",
            "type": "array"
          },
          "created_at": {
            "format": "date-time",
            "title": "Created At",
            "type": "string"
          },
          "started_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Started At"
          },
          "finished_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Finished At"
          }
        },
        "required": [
          "run_id",
          "status",
          "done",
          "created_at"
        ],
        "title": "RunProgress",
        "type": "object"
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "cancel_run",
      "description": "Cancel a run that is still going: its sessions stop where they are and the run ends as cancelled. There is no resume and no refund - what has been spent is spent, and the only way on from here is a new run. A run that is not running is refused rather than cancelled twice.",
      "inputSchema": {
        "properties": {
          "run_id": {
            "description": "Run id.",
            "title": "Run Id",
            "type": "string"
          }
        },
        "required": [
          "run_id"
        ],
        "title": "cancel_runArguments",
        "type": "object"
      },
      "outputSchema": {
        "additionalProperties": false,
        "description": "A pause, resume or cancel delivered to a run.",
        "properties": {
          "run_id": {
            "description": "The run the signal was delivered to.",
            "title": "Run Id",
            "type": "string"
          },
          "signal": {
            "description": "The signal delivered: pause, resume or cancel.",
            "title": "Signal",
            "type": "string"
          }
        },
        "required": [
          "run_id",
          "signal"
        ],
        "title": "RunSignalled",
        "type": "object"
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": true
      }
    },
    {
      "name": "list_personas",
      "description": "The synthetic personas this deployment can run, by the address a run spec names them with (id@version), with each one's pack and tags. Narrow to one pack with the pack argument, or call list_packs to see the cohorts. About 200 characters a persona.",
      "inputSchema": {
        "properties": {
          "pack": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Only personas in this pack, matched exactly.",
            "title": "Pack"
          }
        },
        "title": "list_personasArguments",
        "type": "object"
      },
      "outputSchema": {
        "$defs": {
          "PersonaSummary": {
            "additionalProperties": false,
            "description": "One persona a run can name, by its address, with its name, pack and tags.",
            "properties": {
              "address": {
                "description": "`id@version`, what a run spec and a `sessions.persona_id` carry.",
                "title": "Address",
                "type": "string"
              },
              "id": {
                "title": "Id",
                "type": "string"
              },
              "version": {
                "title": "Version",
                "type": "integer"
              },
              "name": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Name"
              },
              "pack": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Pack"
              },
              "tags": {
                "items": {
                  "type": "string"
                },
                "title": "Tags",
                "type": "array"
              }
            },
            "required": [
              "address",
              "id",
              "version"
            ],
            "title": "PersonaSummary",
            "type": "object"
          }
        },
        "additionalProperties": false,
        "description": "The personas a run can name. `GET /api/v1/personas/{address}` answers one in full.",
        "properties": {
          "personas": {
            "items": {
              "$ref": "#/$defs/PersonaSummary"
            },
            "title": "Personas",
            "type": "array"
          }
        },
        "required": [
          "personas"
        ],
        "title": "PersonaList",
        "type": "object"
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "get_persona",
      "description": "One persona in full, as the registry holds it and as a run spec may inline it: who they are, what they are trying to do, how they behave under friction, and what they find hard. Read this to say whose experience a verdict or an issue is - never to speak as them.",
      "inputSchema": {
        "properties": {
          "address": {
            "description": "id@version, or a bare id for the latest version.",
            "title": "Address",
            "type": "string"
          }
        },
        "required": [
          "address"
        ],
        "title": "get_personaArguments",
        "type": "object"
      },
      "outputSchema": {
        "$defs": {
          "AbandonBehavior": {
            "description": "What the persona's diary says when they give up, on one task or on the whole session. `narrates_reason` (the default): they say why before they stop. `silent`: they simply stop.",
            "enum": [
              "silent",
              "narrates_reason"
            ],
            "title": "AbandonBehavior",
            "type": "string"
          },
          "AccessibilityEnum": {
            "description": "One accessibility need the persona has.",
            "enum": [
              "screen_reader",
              "keyboard_only",
              "low_vision",
              "reduced_motion",
              "color_blind"
            ],
            "title": "AccessibilityEnum",
            "type": "string"
          },
          "ActionPolicy": {
            "additionalProperties": false,
            "description": "How the persona works a page: how much of it they read, what they work it with, whether they wander or head straight for the goal, and how they treat defaults, help and errors. Each value becomes a line of the persona's briefing.",
            "properties": {
              "reading": {
                "$ref": "#/$defs/Reading"
              },
              "input": {
                "$ref": "#/$defs/Input"
              },
              "exploration": {
                "$ref": "#/$defs/Exploration"
              },
              "trust_defaults": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/TrustDefaults"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "help_seeking": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/HelpSeeking"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "error_response": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/ErrorResponse"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              }
            },
            "required": [
              "reading",
              "input",
              "exploration"
            ],
            "title": "ActionPolicy",
            "type": "object"
          },
          "AuthState": {
            "description": "The kind of visitor the persona arrives as: `anonymous` (the default), a `new_user`, a `returning_user` or an `admin`. Descriptive, and listed in each issue's `preconditions`; it signs nobody in. A session is signed in by the account it is handed from the run spec's `target.seed.accounts`.",
            "enum": [
              "anonymous",
              "new_user",
              "returning_user",
              "admin"
            ],
            "title": "AuthState",
            "type": "string"
          },
          "Constraints": {
            "additionalProperties": false,
            "description": "The browser the persona uses: the kind of device, the screen size, the connection, the language, accessibility needs and the signed-in state they start in. Most of these set up the browser session rather than the persona's briefing. A replay of an issue runs with the same ones, and the issue lists them in `preconditions`.",
            "properties": {
              "device": {
                "$ref": "#/$defs/Device"
              },
              "viewport": {
                "pattern": "^[0-9]+x[0-9]+$",
                "title": "Viewport",
                "type": "string"
              },
              "network": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/Network"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": "fast"
              },
              "accessibility": {
                "anyOf": [
                  {
                    "items": {
                      "$ref": "#/$defs/AccessibilityEnum"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Accessibility"
              },
              "locale": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": "en-US",
                "title": "Locale"
              },
              "auth_state": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/AuthState"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": "anonymous"
              }
            },
            "required": [
              "device",
              "viewport"
            ],
            "title": "Constraints",
            "type": "object"
          },
          "Conversation": {
            "additionalProperties": false,
            "description": "How the persona talks to an in-product assistant or chat surface. Matters when the target is itself an AI product.",
            "properties": {
              "phrasing": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/Phrasing"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "asks_before_hunting": {
                "anyOf": [
                  {
                    "type": "boolean"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Asks Before Hunting"
              },
              "pushes_back_on_generic": {
                "anyOf": [
                  {
                    "type": "boolean"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Pushes Back On Generic"
              }
            },
            "title": "Conversation",
            "type": "object"
          },
          "Device": {
            "description": "The kind of device the browser presents as. `mobile` and `tablet` open a mobile, touch-enabled browser; `desktop` a desktop one. The screen size is `viewport`, set separately.",
            "enum": [
              "desktop",
              "mobile",
              "tablet"
            ],
            "title": "Device",
            "type": "string"
          },
          "DomainLiteracy": {
            "description": "How fluent the persona is in the language of the product's industry. `low`: does not know its terms and does not pretend to. `medium`: knows enough to follow along. `high`: fluent, and notices when a term is used loosely.",
            "enum": [
              "low",
              "medium",
              "high"
            ],
            "title": "DomainLiteracy",
            "type": "string"
          },
          "ErrorResponse": {
            "description": "First reaction to an error",
            "enum": [
              "retry_same",
              "retry_different",
              "back_out",
              "abandon"
            ],
            "title": "ErrorResponse",
            "type": "string"
          },
          "Exploration": {
            "description": "Wanders vs. heads straight for the goal",
            "enum": [
              "explores",
              "beeline"
            ],
            "title": "Exploration",
            "type": "string"
          },
          "Goals": {
            "additionalProperties": false,
            "description": "What the persona wants out of the visit, in their own terms: the `primary` goal, anything `secondary` they also want if they get that far, and how much they want it (`intensity`). All three go into the persona's briefing beside the mission's goal; what counts as success for the run is the mission's `success_criteria`.",
            "properties": {
              "primary": {
                "title": "Primary",
                "type": "string"
              },
              "secondary": {
                "anyOf": [
                  {
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Secondary"
              },
              "intensity": {
                "$ref": "#/$defs/Intensity"
              }
            },
            "required": [
              "primary",
              "intensity"
            ],
            "title": "Goals",
            "type": "object"
          },
          "HelpSeeking": {
            "description": "Uses tooltips, help links, search",
            "enum": [
              "never",
              "after_error",
              "eager"
            ],
            "title": "HelpSeeking",
            "type": "string"
          },
          "Input": {
            "description": "What the persona works the page with. `mouse`: a computer with a mouse and a keyboard. `keyboard`: tabs between fields and presses Enter rather than reach for the mouse. `touch`: a phone, tapping and swiping. This shapes the persona's habits; whether the browser itself is a touch device is `constraints.device`.",
            "enum": [
              "mouse",
              "keyboard",
              "touch"
            ],
            "title": "Input",
            "type": "string"
          },
          "Intensity": {
            "description": "How much they want it; interacts with tolerance",
            "enum": [
              "low",
              "medium",
              "high"
            ],
            "title": "Intensity",
            "type": "string"
          },
          "Network": {
            "description": "The connection the browser runs on. `fast` (the default): not throttled. `regular_4g` and `slow_3g`: bandwidth limited and latency added, like those mobile connections. `offline_flaky` is not supported yet, and a session that asks for it does not start.",
            "enum": [
              "fast",
              "regular_4g",
              "slow_3g",
              "offline_flaky"
            ],
            "title": "Network",
            "type": "string"
          },
          "Phrasing": {
            "description": "Asks like they would ask a person, vs. writes instructions",
            "enum": [
              "plain_speech",
              "commands",
              "mixed"
            ],
            "title": "Phrasing",
            "type": "string"
          },
          "Priors": {
            "additionalProperties": false,
            "description": "What the persona knows before they arrive: how well they know this product, the language of its industry and technology in general, the expectations they bring from other products, and what they were told beforehand. Each goes into the persona's briefing and shapes what they understand, expect and notice.",
            "properties": {
              "product_familiarity": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/ProductFamiliarity"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "domain_literacy": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/DomainLiteracy"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "technical_literacy": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/TechnicalLiteracy"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "mental_models": {
                "anyOf": [
                  {
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Mental Models"
              },
              "prior_instructions": {
                "anyOf": [
                  {
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Prior Instructions"
              }
            },
            "title": "Priors",
            "type": "object"
          },
          "ProductFamiliarity": {
            "description": "How well the persona knows this product before the session. `none`: never used it and not sure what it is. `low`: seen it once and remembers almost nothing. `medium`: knows this kind of product, not this one in particular. `high`: knows it well and expects things where they left them.",
            "enum": [
              "none",
              "low",
              "medium",
              "high"
            ],
            "title": "ProductFamiliarity",
            "type": "string"
          },
          "Provenance": {
            "additionalProperties": false,
            "description": "How this persona was grounded. Generators fill this.",
            "properties": {
              "generator": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Generator"
              },
              "sources": {
                "anyOf": [
                  {
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Sources"
              },
              "generated_at": {
                "anyOf": [
                  {
                    "format": "date-time",
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Generated At"
              },
              "notes": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Notes"
              }
            },
            "title": "Provenance",
            "type": "object"
          },
          "Reading": {
            "description": "How much on-screen text they consume",
            "enum": [
              "reads",
              "skims",
              "ignores"
            ],
            "title": "Reading",
            "type": "string"
          },
          "TechnicalLiteracy": {
            "description": "How comfortable the persona is with technology. `low`: not technical; keys, tokens and APIs mean nothing to them. `medium`: finds their way around an app's settings without being technical. `high`: technical, and notices when a key or a token is handled badly.",
            "enum": [
              "low",
              "medium",
              "high"
            ],
            "title": "TechnicalLiteracy",
            "type": "string"
          },
          "Tolerance": {
            "additionalProperties": false,
            "description": "How much the persona puts up with before giving up. Saram counts, step by step, the steps without visible progress, the errors and slow responses, and the moments with no clear next action, and ends the session `abandoned` the moment one crosses its threshold here; the model playing the persona cannot argue with the count. The persona is also told these limits in their own terms.",
            "properties": {
              "patience_steps": {
                "minimum": 1,
                "title": "Patience Steps",
                "type": "integer"
              },
              "error_threshold": {
                "minimum": 0,
                "title": "Error Threshold",
                "type": "integer"
              },
              "latency_sensitivity_ms": {
                "anyOf": [
                  {
                    "minimum": 0,
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Latency Sensitivity Ms"
              },
              "confusion_threshold": {
                "anyOf": [
                  {
                    "minimum": 1,
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Confusion Threshold"
              },
              "attempts_per_subtask": {
                "anyOf": [
                  {
                    "minimum": 1,
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Attempts Per Subtask"
              },
              "abandon_behavior": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/AbandonBehavior"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": "narrates_reason"
              }
            },
            "required": [
              "patience_steps",
              "error_threshold"
            ],
            "title": "Tolerance",
            "type": "object"
          },
          "TrustDefaults": {
            "description": "Accepts prefilled/default choices vs. checks everything",
            "enum": [
              "low",
              "medium",
              "high"
            ],
            "title": "TrustDefaults",
            "type": "string"
          },
          "Verbosity": {
            "description": "How long the persona's diary entries run. `terse`: short. `normal`: as much as the moment deserves. `verbose`: in full, with the detail they noticed.",
            "enum": [
              "terse",
              "normal",
              "verbose"
            ],
            "title": "Verbosity",
            "type": "string"
          },
          "Vocabulary": {
            "additionalProperties": false,
            "description": "Terms the persona does and does not understand. More specific than the literacy enums in `priors`; the renderer tells the actor to say so when an unknown term appears rather than guess.",
            "properties": {
              "knows": {
                "anyOf": [
                  {
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Knows"
              },
              "unknown": {
                "anyOf": [
                  {
                    "items": {
                      "type": "string"
                    },
                    "type": "array"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Unknown"
              }
            },
            "title": "Vocabulary",
            "type": "object"
          },
          "Voice": {
            "additionalProperties": false,
            "description": "Narration style only. It never influences what the persona does.",
            "properties": {
              "style": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Style"
              },
              "verbosity": {
                "anyOf": [
                  {
                    "$ref": "#/$defs/Verbosity"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              }
            },
            "title": "Voice",
            "type": "object"
          }
        },
        "additionalProperties": false,
        "description": "A structured persona: a sampling strategy over the UI state space plus a tolerance function. Two kinds of fields: a structured core the run enforces and the dashboard diffs (`tolerance`, `constraints`, `action_policy`), and prose behavior fields the model reads (`backstory`, `vocabulary`, `facts`, `conversation`, `verdict_prompt`). Both drive what the persona does. `voice` affects narration only.",
        "properties": {
          "id": {
            "pattern": "^[a-z0-9_]+$",
            "title": "Id",
            "type": "string"
          },
          "version": {
            "minimum": 1,
            "title": "Version",
            "type": "integer"
          },
          "name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Name"
          },
          "pack": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Pack"
          },
          "tags": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Tags"
          },
          "backstory": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Backstory"
          },
          "vocabulary": {
            "anyOf": [
              {
                "$ref": "#/$defs/Vocabulary"
              },
              {
                "type": "null"
              }
            ],
            "default": null
          },
          "facts": {
            "anyOf": [
              {
                "additionalProperties": {
                  "type": "string"
                },
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Facts"
          },
          "conversation": {
            "anyOf": [
              {
                "$ref": "#/$defs/Conversation"
              },
              {
                "type": "null"
              }
            ],
            "default": null
          },
          "verdict_prompt": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Verdict Prompt"
          },
          "goals": {
            "$ref": "#/$defs/Goals"
          },
          "priors": {
            "$ref": "#/$defs/Priors"
          },
          "action_policy": {
            "$ref": "#/$defs/ActionPolicy"
          },
          "tolerance": {
            "$ref": "#/$defs/Tolerance"
          },
          "constraints": {
            "$ref": "#/$defs/Constraints"
          },
          "voice": {
            "anyOf": [
              {
                "$ref": "#/$defs/Voice"
              },
              {
                "type": "null"
              }
            ],
            "default": null
          },
          "provenance": {
            "anyOf": [
              {
                "$ref": "#/$defs/Provenance"
              },
              {
                "type": "null"
              }
            ],
            "default": null
          }
        },
        "required": [
          "id",
          "version",
          "goals",
          "priors",
          "action_policy",
          "tolerance",
          "constraints"
        ],
        "title": "Persona",
        "type": "object"
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "list_packs",
      "description": "The persona packs - the cohorts a run draws from - with how many personas each holds and their addresses. A run spec has no pack field: it names personas one by one, so this is how you put a whole cohort in one. Personas that declare no pack are in none of these; list_personas has them.",
      "inputSchema": {
        "properties": {},
        "title": "list_packsArguments",
        "type": "object"
      },
      "outputSchema": {
        "$defs": {
          "PackSummary": {
            "additionalProperties": false,
            "description": "One cohort of the registry, as `list_packs` folds `GET /personas` into it.",
            "properties": {
              "pack": {
                "title": "Pack",
                "type": "string"
              },
              "personas": {
                "title": "Personas",
                "type": "integer"
              },
              "addresses": {
                "description": "Every `id@version` in the pack, sorted: what a run spec's `personas` names.",
                "items": {
                  "type": "string"
                },
                "title": "Addresses",
                "type": "array"
              }
            },
            "required": [
              "pack",
              "personas",
              "addresses"
            ],
            "title": "PackSummary",
            "type": "object"
          }
        },
        "additionalProperties": false,
        "properties": {
          "packs": {
            "description": "Sorted by name. A persona that declares no pack is in no cohort and is in none of these - `list_personas`\nis what has all of them, the same way `RunSummary.packs` lists a run's cohorts and not its packless\npersonas.",
            "items": {
              "$ref": "#/$defs/PackSummary"
            },
            "title": "Packs",
            "type": "array"
          }
        },
        "required": [
          "packs"
        ],
        "title": "PackList",
        "type": "object"
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "get_provider_status",
      "description": "Which model provider your runs are billed to: the state of your caller's own ChatGPT login (none, pending, connected, expired or error), what the gateway last saw of its consumer rate limit, and - once connected - the exact providers block to paste into a run spec, credential name included. Metadata about a login and never any part of one: no token, no routing key. When state is not 'connected', tell the person, and offer to sign them in to ChatGPT: with their go-ahead, start_chatgpt_login answers a link and a code for them to open in their browser. While state is 'pending', pending carries that link and code again - show them, rather than starting another.",
      "inputSchema": {
        "properties": {},
        "title": "get_provider_statusArguments",
        "type": "object"
      },
      "outputSchema": {
        "$defs": {
          "CodexGateway": {
            "additionalProperties": false,
            "description": "What the ChatGPT gateway reports about this caller's slot: whether it can serve, and the consumer rate\nlimit's state from the last `x-codex-*` headers it saw. Never a token and never a routing key.",
            "properties": {
              "login": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Login"
              },
              "primary_used_percent": {
                "anyOf": [
                  {
                    "type": "number"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Primary Used Percent"
              },
              "primary_reset_at": {
                "anyOf": [
                  {
                    "format": "date-time",
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Primary Reset At"
              },
              "last_error": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Last Error"
              }
            },
            "title": "CodexGateway",
            "type": "object"
          },
          "CodexPending": {
            "additionalProperties": false,
            "description": "A device login in flight. The `device_auth_id` that redeems it is not here and never leaves the pod.",
            "properties": {
              "user_code": {
                "title": "User Code",
                "type": "string"
              },
              "verification_url": {
                "title": "Verification Url",
                "type": "string"
              },
              "expires_at": {
                "format": "date-time",
                "title": "Expires At",
                "type": "string"
              }
            },
            "required": [
              "user_code",
              "verification_url",
              "expires_at"
            ],
            "title": "CodexPending",
            "type": "object"
          },
          "CodexProviders": {
            "additionalProperties": false,
            "description": "The `providers` block to paste into a run spec, secret name included, so the caller never has to type\none. The ref is the routing key's name, never its value.",
            "properties": {
              "entries": {
                "additionalProperties": {
                  "$ref": "#/$defs/CodexProvidersEntry"
                },
                "title": "Entries",
                "type": "object"
              },
              "roles": {
                "additionalProperties": {
                  "type": "string"
                },
                "title": "Roles",
                "type": "object"
              }
            },
            "required": [
              "entries",
              "roles"
            ],
            "title": "CodexProviders",
            "type": "object"
          },
          "CodexProvidersEntry": {
            "additionalProperties": false,
            "description": "One `providers` entry of a run spec, routed through this caller's ChatGPT login.",
            "properties": {
              "model": {
                "title": "Model",
                "type": "string"
              },
              "base_url": {
                "title": "Base Url",
                "type": "string"
              },
              "credential_ref": {
                "title": "Credential Ref",
                "type": "string"
              }
            },
            "required": [
              "model",
              "base_url",
              "credential_ref"
            ],
            "title": "CodexProvidersEntry",
            "type": "object"
          }
        },
        "additionalProperties": false,
        "description": "`GET /api/v1/providers/codex`. Metadata about a login; never any part of one.",
        "properties": {
          "caller": {
            "title": "Caller",
            "type": "string"
          },
          "state": {
            "enum": [
              "none",
              "pending",
              "connected",
              "expired",
              "error"
            ],
            "title": "State",
            "type": "string"
          },
          "pending": {
            "anyOf": [
              {
                "$ref": "#/$defs/CodexPending"
              },
              {
                "type": "null"
              }
            ],
            "default": null
          },
          "account_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Account Id"
          },
          "plan_type": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Plan Type"
          },
          "connected_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Connected At"
          },
          "expires_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Expires At"
          },
          "last_refresh": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Last Refresh"
          },
          "error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Why the last login attempt failed, when `state` is `error` and no gateway call was involved. The OAuth\n`error_description`, which cannot carry a token.",
            "title": "Error"
          },
          "gateway": {
            "anyOf": [
              {
                "$ref": "#/$defs/CodexGateway"
              },
              {
                "type": "null"
              }
            ],
            "default": null
          },
          "providers": {
            "anyOf": [
              {
                "$ref": "#/$defs/CodexProviders"
              },
              {
                "type": "null"
              }
            ],
            "default": null
          }
        },
        "required": [
          "caller",
          "state"
        ],
        "title": "CodexStatus",
        "type": "object"
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "start_chatgpt_login",
      "description": "Start connecting the person's own ChatGPT subscription, or reconnecting one that get_provider_status says is not 'connected'. Call it only when the person has asked for it, in this session: a login is theirs alone and serves only the runs they start. Answers a verification_url and a user_code - show both to the person, exactly as given, and ask them to open the link in their own browser, sign in to ChatGPT, and enter the code. The sign-in is theirs to do: never open the link, sign in or enter the code for them. The code lasts expires_in seconds (15 minutes); once they say they are done, call get_provider_status, which says 'connected' and prints the providers block to paste into a run spec. Refused while an earlier code is still open (the refusal carries that link and code) and for a credential that is not a person's own.",
      "inputSchema": {
        "properties": {},
        "title": "start_chatgpt_loginArguments",
        "type": "object"
      },
      "outputSchema": {
        "additionalProperties": false,
        "description": "A ChatGPT sign-in started for a person to finish in their own browser: the link, the code, and how long\nthey have. The `start_chatgpt_login` answer; `get_provider_status` says when the login has connected.",
        "properties": {
          "caller": {
            "description": "Whose ChatGPT login this connects: the caller the credential identifies.",
            "title": "Caller",
            "type": "string"
          },
          "verification_url": {
            "description": "The page to open in a browser, where the person signs in to ChatGPT. Show it to them.",
            "title": "Verification Url",
            "type": "string"
          },
          "user_code": {
            "description": "The code the person enters on that page. Show it to them.",
            "title": "User Code",
            "type": "string"
          },
          "expires_in": {
            "description": "Seconds until the code expires and the sign-in has to start again.",
            "title": "Expires In",
            "type": "integer"
          }
        },
        "required": [
          "caller",
          "verification_url",
          "user_code",
          "expires_in"
        ],
        "title": "CodexSignIn",
        "type": "object"
      },
      "annotations": {
        "readOnlyHint": false,
        "idempotentHint": false,
        "openWorldHint": true
      }
    },
    {
      "name": "create_share_link",
      "description": "Publish one finished session's recording as a public link: anyone holding the URL can watch it, with the persona's steps and narration, without signing in; the recording hides only what the browser pool masked when it recorded it (masked and warning in the answer say what that is), so the test account's email and whatever else the product showed are visible as the browser showed them; and it plays until it expires or you revoke it with revoke_share_link and the id this answers. Call it only when a person has asked to share this session, and show them the answer's warning before the link goes anywhere. The url is in this answer and never again - only a hash of it is kept - so a lost link is replaced by minting another and revoking this one. It expires after expires_in_days (30 unless you ask for 1 to 90) and never after the run's evidence does; capped_by_evidence says when that won. Refused while your tenant's public sharing is off, which only Saram turns on; for a session that has not ended, whose recording is not stored yet; for a run whose evidence has expired; and for a session the browser pool did not record under its mask, which paints over what the persona typed.",
      "inputSchema": {
        "properties": {
          "run_id": {
            "description": "Run id.",
            "title": "Run Id",
            "type": "string"
          },
          "session_id": {
            "description": "Session id, one of the sessions get_run lists.",
            "title": "Session Id",
            "type": "string"
          },
          "expires_in_days": {
            "default": 30,
            "description": "How many days the link plays, 1 to 90; 30 when omitted. Never past the run's evidence lifetime, whatever you ask for. No link lives forever and none is extended: mint a new one instead.",
            "maximum": 90,
            "minimum": 1,
            "title": "Expires In Days",
            "type": "integer"
          }
        },
        "required": [
          "run_id",
          "session_id"
        ],
        "title": "create_share_linkArguments",
        "type": "object"
      },
      "outputSchema": {
        "additionalProperties": false,
        "description": "A new public link to one session's replay, and the one and only sight of its URL.\n\nAnyone holding `url` can watch the session's recording, which hides only what the browser pool masked when it\nrecorded it (`masked`): whatever else the browser showed is in it. Only a hash of the link's token is stored, so\nno route shows the URL again; a lost link is replaced by minting another and revoking this one with\n`POST /api/v1/shares/{id}/revoke`.",
        "properties": {
          "id": {
            "description": "shr_...: the link's id, for listing and revoking it. Not the link itself.",
            "title": "Id",
            "type": "string"
          },
          "url": {
            "description": "The public link. Shown once, here, and never again by any route. Anyone who has it can watch this session until it expires or is revoked, with no sign-in.",
            "title": "Url",
            "type": "string"
          },
          "run_id": {
            "title": "Run Id",
            "type": "string"
          },
          "session_id": {
            "title": "Session Id",
            "type": "string"
          },
          "created_at": {
            "format": "date-time",
            "title": "Created At",
            "type": "string"
          },
          "created_by": {
            "description": "key_... for an API key, usr_... for a person, operator for the operator.",
            "title": "Created By",
            "type": "string"
          },
          "expires_at": {
            "format": "date-time",
            "title": "Expires At",
            "type": "string"
          },
          "capped_by_evidence": {
            "description": "True when the run's evidence lifetime, and not the days asked for, set expires_at: the link ends with the recording.",
            "title": "Capped By Evidence",
            "type": "boolean"
          },
          "masked": {
            "description": "True when the browser pool recorded the session under its mask: the value of every field the persona typed into, every one-time-code field and every data-saram-mask element are painted over in every frame. False only for a session of Saram's own, which is shared unmasked.",
            "title": "Masked",
            "type": "boolean"
          },
          "warning": {
            "description": "What anyone holding the link will see, what in the recording is masked and what is not, and how to revoke it. Show it to whoever posts the link.",
            "title": "Warning",
            "type": "string"
          }
        },
        "required": [
          "id",
          "url",
          "run_id",
          "session_id",
          "created_at",
          "created_by",
          "expires_at",
          "capped_by_evidence",
          "masked",
          "warning"
        ],
        "title": "ShareCreated",
        "type": "object"
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": true
      }
    },
    {
      "name": "list_share_links",
      "description": "Every public link minted on one run's sessions, newest first - live, expired and revoked - each with its id (what revoke_share_link takes), the session it plays, its last four characters, its state, who minted and who revoked it, when it expires and how often it was opened. Never a link itself: a URL is shown once, by create_share_link. public_shares says whether your tenant may share at all; while it is off no link plays, whatever its state. mask_required says whether a session of yours is shared only when the browser pool recorded it under its mask (recorded_under_mask on its session row): while it is true, create_share_link refuses any other.",
      "inputSchema": {
        "properties": {
          "run_id": {
            "description": "Run id.",
            "title": "Run Id",
            "type": "string"
          }
        },
        "required": [
          "run_id"
        ],
        "title": "list_share_linksArguments",
        "type": "object"
      },
      "outputSchema": {
        "$defs": {
          "ShareSummary": {
            "additionalProperties": false,
            "description": "One public link to a session's replay, as the list and the revocation answer it. Never the link itself.",
            "properties": {
              "id": {
                "title": "Id",
                "type": "string"
              },
              "run_id": {
                "title": "Run Id",
                "type": "string"
              },
              "session_id": {
                "title": "Session Id",
                "type": "string"
              },
              "token_hint": {
                "description": "The link's last four characters, to tell your links apart.",
                "title": "Token Hint",
                "type": "string"
              },
              "state": {
                "description": "live plays, expired and revoked do not. Read it beside ShareList.public_shares: while your tenant's sharing is off no link plays, whatever its state.",
                "enum": [
                  "live",
                  "expired",
                  "revoked"
                ],
                "title": "State",
                "type": "string"
              },
              "created_at": {
                "format": "date-time",
                "title": "Created At",
                "type": "string"
              },
              "created_by": {
                "title": "Created By",
                "type": "string"
              },
              "caller": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "Who minted it, in words, when the credential had a name.",
                "title": "Caller"
              },
              "expires_at": {
                "format": "date-time",
                "title": "Expires At",
                "type": "string"
              },
              "revoked_at": {
                "anyOf": [
                  {
                    "format": "date-time",
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Revoked At"
              },
              "revoked_by": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "description": "Who revoked it: any principal of your tenant that may write runs can.",
                "title": "Revoked By"
              },
              "view_count": {
                "description": "How many times the replay was opened. Nothing about who opened it is kept.",
                "title": "View Count",
                "type": "integer"
              },
              "last_viewed_at": {
                "anyOf": [
                  {
                    "format": "date-time",
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null,
                "title": "Last Viewed At"
              }
            },
            "required": [
              "id",
              "run_id",
              "session_id",
              "token_hint",
              "state",
              "created_at",
              "created_by",
              "expires_at",
              "view_count"
            ],
            "title": "ShareSummary",
            "type": "object"
          }
        },
        "additionalProperties": false,
        "description": "A run's public links, revoked and expired ones included, newest first.",
        "properties": {
          "run_id": {
            "title": "Run Id",
            "type": "string"
          },
          "public_shares": {
            "description": "Whether your tenant may publish sessions at all. Off, a new link is refused with a 403 and no existing link plays; their rows are kept and they play again, until their own expiry, once it is back on.",
            "title": "Public Shares",
            "type": "boolean"
          },
          "mask_required": {
            "description": "Whether a session of yours is shared only when the browser pool recorded it under its mask (SessionRow.recorded_under_mask): a share of any other is refused with a 409. True for every customer; false only for Saram's own sessions, which are shared unmasked.",
            "title": "Mask Required",
            "type": "boolean"
          },
          "shares": {
            "items": {
              "$ref": "#/$defs/ShareSummary"
            },
            "title": "Shares",
            "type": "array"
          }
        },
        "required": [
          "run_id",
          "public_shares",
          "mask_required",
          "shares"
        ],
        "title": "ShareList",
        "type": "object"
      },
      "annotations": {
        "readOnlyHint": true
      }
    },
    {
      "name": "revoke_share_link",
      "description": "Stop a public share link playing: the next request for it is refused, a video already playing included once it asks for its next part. There is no undoing it; to share the session again, create_share_link mints a new link. Any credential of your tenant that may write runs revokes any of its links, not only the one that minted it. The link's record stays, with who revoked it and when, and asking again answers the same record.",
      "inputSchema": {
        "properties": {
          "share_id": {
            "description": "shr_...: the link's id, as create_share_link and list_share_links answer it.",
            "title": "Share Id",
            "type": "string"
          }
        },
        "required": [
          "share_id"
        ],
        "title": "revoke_share_linkArguments",
        "type": "object"
      },
      "outputSchema": {
        "additionalProperties": false,
        "description": "One public link to a session's replay, as the list and the revocation answer it. Never the link itself.",
        "properties": {
          "id": {
            "title": "Id",
            "type": "string"
          },
          "run_id": {
            "title": "Run Id",
            "type": "string"
          },
          "session_id": {
            "title": "Session Id",
            "type": "string"
          },
          "token_hint": {
            "description": "The link's last four characters, to tell your links apart.",
            "title": "Token Hint",
            "type": "string"
          },
          "state": {
            "description": "live plays, expired and revoked do not. Read it beside ShareList.public_shares: while your tenant's sharing is off no link plays, whatever its state.",
            "enum": [
              "live",
              "expired",
              "revoked"
            ],
            "title": "State",
            "type": "string"
          },
          "created_at": {
            "format": "date-time",
            "title": "Created At",
            "type": "string"
          },
          "created_by": {
            "title": "Created By",
            "type": "string"
          },
          "caller": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Who minted it, in words, when the credential had a name.",
            "title": "Caller"
          },
          "expires_at": {
            "format": "date-time",
            "title": "Expires At",
            "type": "string"
          },
          "revoked_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Revoked At"
          },
          "revoked_by": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Who revoked it: any principal of your tenant that may write runs can.",
            "title": "Revoked By"
          },
          "view_count": {
            "description": "How many times the replay was opened. Nothing about who opened it is kept.",
            "title": "View Count",
            "type": "integer"
          },
          "last_viewed_at": {
            "anyOf": [
              {
                "format": "date-time",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Last Viewed At"
          }
        },
        "required": [
          "id",
          "run_id",
          "session_id",
          "token_hint",
          "state",
          "created_at",
          "created_by",
          "expires_at",
          "view_count"
        ],
        "title": "ShareSummary",
        "type": "object"
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": true
      }
    }
  ]
}
