# Get Issue

- **Method:** `GET`
- **Path:** `/api/v1/runs/{run_id}/issues/{issue_id}`
- **Operation ID:** `get_issue`
- **Tags:** artifact

One issue in full - its claim and how it was checked, repro steps, preconditions and evidence - with
its reading and the evidence refs to fetch. A 404 until the run's artifact is assembled; an id that is
one of the run's tester errors is a 404 that says where to read it instead.

## Effective servers

- `https://api.saram.sh`

  Saram

## Authentication

- **HTTPBearer**: HTTP bearer

  a Saram API key (`sk_saram_<env>_...`) or a person's AuthKit access token, as `Authorization: Bearer <credential>`. Both are told apart by shape and produce the same principal.

## Path parameters

- **`run_id` (required)**: `string`
- **`issue_id` (required)**: `string`

## Responses

### 200 Successful Response

**Content type:** `application/json`

schema: `IssueDetail`, no additional properties

One issue with its reading and its evidence refs beside it: `GET /runs/{id}/issues/{issue_id}` and the MCP
`get_issue` answer.

`issue` is the contract object verbatim and `reading` is the entry `IssueList.reading[]` carries for the same
issue, computed by the same function, so a surface that shows one issue - an issue page, or an agent that
calls `get_issue` first - reads whether it may be filed alone, and how to read its persona-voiced fields,
without a second call and cannot disagree with the list about them.

- **`evidence_refs` (required)**: `object`, schema: `EvidenceRefs`, no additional properties

  The store URIs on one issue, gathered where a consumer that wants to *fetch* evidence looks for them.

  `screenshots` are what the MCP `get_screenshot` tool accepts. The other two are not - it serves images
  only - and are fetched over `GET /api/v1/blobs?ref=` with the same key, which is why they are named here
  rather than left for a caller to dig out of `issue.evidence`.
  - **`dom_snapshot`**: `string | null`
  - **`screenshots`**: `array of string`
  - **`video`**: `string | null`
- **`issue` (required)**: `object`, schema: `Issue`, no additional properties

  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.
  - **`affected_sessions` (required)**: `array of string`
  - **`confidence` (required)**: `number`, minimum: `0`, maximum: `1`
  - **`description` (required)**: `string`
  - **`evidence` (required)**: `object`, schema: `Evidence1`, no additional properties

    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.
    - **`step_refs` (required)**: `array`, minItems: `1`

      **Items:**

      schema: `StepRef`

      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.
      - **`session_id` (required)**: `string`
      - **`step_id` (required)**: `integer`
    - **`console`**: `(array of object) | null`
    - **`dom_snapshot`**: `string | null`, format: `uri`, minLength: `1`
    - **`network`**: `(array of object) | null`
    - **`screenshots`**: `array | null`

      **Items:**

      `string`, format: `uri`, minLength: `1`
    - **`video`**: `object | null`, schema: `Video`

      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.
      - **`ref`**: `string | null`
      - **`ts_end_s`**: `number | null`
      - **`ts_start_s`**: `number | null`
  - **`id` (required)**: `string`
  - **`location` (required)**: `object`, schema: `Location1`, no additional properties

    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).
    - **`route` (required)**: `string`
    - **`element_description`**: `string | null`
    - **`selector`**: `string | null`
    - **`viewport`**: `string | null`
  - **`reproducible` (required)**: `boolean`
  - **`severity` (required)**: `string`, possible values: `"low", "medium", "high", "critical"`

    Schema: `Severity`.

    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`.
  - **`type` (required)**: `string`

    Schema: `Type`. Known values, open (more may be added): `blocker`, `friction`, `confusion`, `bug`, `contradiction`.

    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.
  - **`affected_count`**: `integer | null`
  - **`affected_fraction`**: `number | null`, minimum: `0`, maximum: `1`
  - **`affected_personas`**: `(array of string) | null`
  - **`baseline_status`**: `string | null`

    Schema: `BaselineStatus`. Known values, open (more may be added): `new`, `persisting`, `resolved`, `unknown`.

    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`.
  - **`claim`**: `object | null`, schema: `Claim`, no additional properties

    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.
    - **`action_step`**: `object | null`, schema: `ActionStep`, no additional properties

      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.
      - **`session_id` (required)**: `string`
      - **`step_id` (required)**: `integer`
    - **`attribution`**: `string | null`

      Schema: `Attribution`. Known values, open (more may be added): `product`, `tester`, `undetermined`.

      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`.
    - **`check`**: `object | null`, schema: `Check`, no additional properties

      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.
      - **`verdict` (required)**: `string`

        Schema: `Verdict1`. Known values, open (more may be added): `supported`, `unsupported`, `ungrounded`, `skipped`.

        `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.
      - **`checked_by`**: `string | null`

        Schema: `CheckedBy`. Known values, open (more may be added): `judge`, `code`, `manual`.

        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.
      - **`quote`**: `string | null`
      - **`quote_step`**: `object | null`, schema: `QuoteStep`, no additional properties

        The step whose ledger block the quote came from.
        - **`session_id` (required)**: `string`
        - **`step_id` (required)**: `integer`
      - **`reason`**: `string | null`
    - **`expected`**: `string | null`
    - **`observed`**: `string | null`
    - **`verification`**: `object | null`, schema: `Verification`, no additional properties

      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`.
      - **`status` (required)**: `string`, possible values: `"verified", "marker_matched", "inconclusive", "unverified"`

        Schema: `Status`.

        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.
      - **`markers`**: `(array of string) | null`
      - **`method`**: `string | null`

        Schema: `Method`. Known values, open (more may be added): `marker`, `assertion`, `manual`.

        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.
      - **`note`**: `string | null`
  - **`connector_refs`**: `object | null`

    **Additional properties:**

    `string`
  - **`contradiction`**: `object | null`, schema: `Contradiction`, no additional properties

    Populated for type=contradiction: the two statements/surfaces that conflict, each pinned to a step.
    - **`a`**: `object | null`, schema: `A`

      The first of the two statements that conflict: what the product said (`text`), and the session and step where it said it.
      - **`session_id`**: `string | null`
      - **`step_id`**: `integer | null`
      - **`text`**: `string | null`
    - **`b`**: `object | null`, schema: `B`

      The second of the two statements that conflict: what the product said (`text`), and the session and step where it said it.
      - **`session_id`**: `string | null`
      - **`step_id`**: `integer | null`
      - **`text`**: `string | null`
  - **`first_seen_step`**: `integer | null`
  - **`persona_rationale`**: `string | null`
  - **`persona_specific`**: `boolean | null`
  - **`preconditions`**: `(array of string) | null`
  - **`reached_sessions`**: `(array of string) | null`
  - **`repro_steps`**: `array | null`

    **Items:**

    schema: `ReproStep`

    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.
    - **`action` (required)**: `string`
    - **`expect`**: `string | null`
    - **`replace`**: `boolean | null`
    - **`target`**: `string | null`
    - **`value`**: `string | null`
  - **`reproduction`**: `object | null`, schema: `Reproduction1`, no additional properties

    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.
    - **`attempted`**: `boolean | null`
    - **`attempts`**: `integer | null`
    - **`notes`**: `string | null`
    - **`result`**: `string | null`

      Schema: `Result1`. Known values, open (more may be added): `reproduced`, `not_reproduced`, `flaky`, `skipped`.

      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.
  - **`suggested_owner`**: `string | null`
  - **`tester_error`**: `object | null`, schema: `TesterError`, no additional properties

    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.
    - **`kind` (required)**: `string`

      Schema: `Kind1`. Known values, open (more may be added): `mistyped_credential`, `mistyped_input`, `wrong_element`, `placeholder_as_value`, `ignored_instruction`, `other`.

      `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.
    - **`quote` (required)**: `string`
    - **`decided_by`**: `string | null`

      Schema: `DecidedBy`. Known values, open (more may be added): `code`, `judge`.

      `code`: a mechanical comparison decided it (the credential check). `judge`: the observer's attribution model call under its own instructions decided it. A classification.
    - **`quote_step`**: `object | null`, schema: `QuoteStep`, no additional properties

      The step whose ledger block the quote came from.

      *Schema `QuoteStep` is shown above.*
    - **`reason`**: `string | null`
  - **`title`**: `string | null`, maxLength: `120`
- **`reading` (required)**: `object`, schema: `IssueReading`, no additional properties

  How one issue of `IssueList.issues` may be acted on: whether it may be filed as a ticket on its own, and
  which of its fields are the persona's voice rather than observed behaviour.
  - **`issue_id` (required)**: `string`
  - **`may_file_ticket_alone` (required)**: `boolean`

    True only when reproduction cleared this issue to be filed as a confirmed bug on its own. False means
    report it with its evidence and mark it unconfirmed.
  - **`subjective_fields` (required)**: `array of string`

    Fields on the issue that are the persona's voice rather than observed behaviour. Quote, never state.
  - **`advisory`**: `string | null`

    How to read those fields, present only when there are any.
- **`run_id` (required)**: `string`

**Example:**

```json
{
  "run_id": "run_2026-09-26_4f1c2a",
  "issue": {
    "id": "iss_signup_submit_500",
    "type": "bug",
    "severity": "high",
    "reproducible": true,
    "reproduction": {
      "attempted": true,
      "result": "reproduced",
      "attempts": 2,
      "notes": "Both replays filled the sign-up form with a password containing a space and pressed Create account: POST /api/signup answered 500 and the page did not change."
    },
    "persona_specific": false,
    "claim": {
      "action_step": {
        "session_id": "run_2026-09-26_4f1c2a.sess_01_dana_first_timer",
        "step_id": 7
      },
      "expected": "Create account submits the form and signs the new user in.",
      "observed": "POST /api/signup answered 500; the form stayed on screen with every field filled and no message.",
      "verification": {
        "status": "verified",
        "method": "marker",
        "markers": [
          "failed_requests"
        ],
        "note": "The same failed request at the judged step on both attempts; the claim check found the title supported by the network line, and the failure is the product's."
      },
      "attribution": "product",
      "check": {
        "verdict": "supported",
        "quote": "[step 7] click -> button[type=submit]: POST /api/signup 500",
        "quote_step": {
          "session_id": "run_2026-09-26_4f1c2a.sess_01_dana_first_timer",
          "step_id": 7
        },
        "reason": "The request the button sends fails on the server, which is what the title claims.",
        "checked_by": "judge"
      }
    },
    "affected_sessions": [
      "run_2026-09-26_4f1c2a.sess_01_dana_first_timer"
    ],
    "affected_personas": [
      "dana_first_timer@1"
    ],
    "affected_count": 1,
    "affected_fraction": 0.5,
    "reached_sessions": [
      "run_2026-09-26_4f1c2a.sess_01_dana_first_timer",
      "run_2026-09-26_4f1c2a.sess_02_marcus_fast_skimmer"
    ],
    "first_seen_step": 7,
    "location": {
      "route": "/signup",
      "selector": "button[type=submit]",
      "element_description": "Create account button under the sign-up form",
      "viewport": "1440x900"
    },
    "title": "Create account fails with a server error when the password contains a space",
    "description": "On /signup, submitting the form with a password that contains a space sends POST /api/signup, which answers 500. The page shows no error and keeps the form filled, so the persona pressed Create account three more times before giving up.",
    "persona_rationale": "I pressed the button four times and nothing happened, so I assumed the site was broken.",
    "evidence": {
      "step_refs": [
        {
          "session_id": "run_2026-09-26_4f1c2a.sess_01_dana_first_timer",
          "step_id": 7
        },
        {
          "session_id": "run_2026-09-26_4f1c2a.sess_01_dana_first_timer",
          "step_id": 10
        }
      ],
      "screenshots": [
        "s3://saram-evidence/run_2026-09-26_4f1c2a/run_2026-09-26_4f1c2a.sess_01_dana_first_timer/shots/0007.png"
      ]
    },
    "repro_steps": [
      {
        "action": "navigate",
        "target": "https://example.com/signup",
        "expect": "sign-up form visible"
      },
      {
        "action": "fill",
        "target": "form#signup",
        "expect": "a password containing a space"
      },
      {
        "action": "click",
        "target": "button[type=submit]",
        "expect": "a signed-in page"
      }
    ],
    "preconditions": [
      "viewport 1440x900",
      "auth_state anonymous"
    ],
    "baseline_status": "new",
    "confidence": 0.92
  },
  "reading": {
    "issue_id": "iss_signup_submit_500",
    "may_file_ticket_alone": true,
    "subjective_fields": [
      "persona_rationale"
    ],
    "advisory": "SUBJECTIVE. `persona_rationale` is persona-voiced narration from an affected session, not a statement of observed behaviour. Quote it as the persona's words; the objective claim is `description`."
  },
  "evidence_refs": {
    "screenshots": [
      "s3://saram-evidence/run_2026-09-26_4f1c2a/run_2026-09-26_4f1c2a.sess_01_dana_first_timer/shots/0007.png"
    ]
  }
}
```

### 403 the API key lacks the required scope

### 422 Validation Error

**Content type:** `application/json`

schema: `HTTPValidationError`

- **`detail`**: `array`

  **Items:**

  schema: `ValidationError`
  - **`loc` (required)**: `array of (string | integer)`
  - **`msg` (required)**: `string`
  - **`type` (required)**: `string`
  - **`ctx`**: `object`
  - **`input`**: `any`

**Example:**

```json
{
  "detail": [
    {
      "loc": [
        ""
      ],
      "msg": "",
      "type": "",
      "input": null,
      "ctx": {}
    }
  ]
}
```

## Schemas

- `A` — shown above.
- `ActionStep` — shown above.

### Attribution

**Type:** `string`

Known values, open (more may be added): `product`, `tester`, `undetermined`.

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`.

**Example:**

```json
"product"
```

**Example:**

```json
"tester"
```

**Example:**

```json
"undetermined"
```

- `B` — shown above.

### BaselineStatus

**Type:** `string`

Known values, open (more may be added): `new`, `persisting`, `resolved`, `unknown`.

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`.

**Example:**

```json
"new"
```

**Example:**

```json
"persisting"
```

**Example:**

```json
"resolved"
```

**Example:**

```json
"unknown"
```

- `Check` — shown above.

### CheckedBy

**Type:** `string`

Known values, open (more may be added): `judge`, `code`, `manual`.

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.

**Example:**

```json
"judge"
```

**Example:**

```json
"code"
```

**Example:**

```json
"manual"
```

- `Claim` — shown above.
- `Contradiction` — shown above.

### DecidedBy

**Type:** `string`

Known values, open (more may be added): `code`, `judge`.

`code`: a mechanical comparison decided it (the credential check). `judge`: the observer's attribution model call under its own instructions decided it. A classification.

**Example:**

```json
"code"
```

**Example:**

```json
"judge"
```

- `Evidence1` — shown above.
- `EvidenceRefs` — shown above.
- `HTTPValidationError` — shown above.
- `Issue` — shown above.
- `IssueDetail` — shown above.
- `IssueReading` — shown above.

### Kind1

**Type:** `string`

Known values, open (more may be added): `mistyped_credential`, `mistyped_input`, `wrong_element`, `placeholder_as_value`, `ignored_instruction`, `other`.

`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.

**Example:**

```json
"mistyped_credential"
```

**Example:**

```json
"mistyped_input"
```

**Example:**

```json
"wrong_element"
```

**Example:**

```json
"placeholder_as_value"
```

**Example:**

```json
"ignored_instruction"
```

**Example:**

```json
"other"
```

- `Location1` — shown above.

### Method

**Type:** `string`

Known values, open (more may be added): `marker`, `assertion`, `manual`.

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.

**Example:**

```json
"marker"
```

**Example:**

```json
"assertion"
```

**Example:**

```json
"manual"
```

- `QuoteStep` — shown above.
- `ReproStep` — shown above.
- `Reproduction1` — shown above.

### Result1

**Type:** `string`

Known values, open (more may be added): `reproduced`, `not_reproduced`, `flaky`, `skipped`.

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.

**Example:**

```json
"reproduced"
```

**Example:**

```json
"not_reproduced"
```

**Example:**

```json
"flaky"
```

**Example:**

```json
"skipped"
```

### Severity

**Type:** `string`, possible values: `"low", "medium", "high", "critical"`

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`.

### Status

**Type:** `string`, possible values: `"verified", "marker_matched", "inconclusive", "unverified"`

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.

- `StepRef` — shown above.
- `TesterError` — shown above.

### Type

**Type:** `string`

Known values, open (more may be added): `blocker`, `friction`, `confusion`, `bug`, `contradiction`.

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.

**Example:**

```json
"blocker"
```

**Example:**

```json
"friction"
```

**Example:**

```json
"confusion"
```

**Example:**

```json
"bug"
```

**Example:**

```json
"contradiction"
```

- `ValidationError` — shown above.

### Verdict1

**Type:** `string`

Known values, open (more may be added): `supported`, `unsupported`, `ungrounded`, `skipped`.

`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.

**Example:**

```json
"supported"
```

**Example:**

```json
"unsupported"
```

**Example:**

```json
"ungrounded"
```

**Example:**

```json
"skipped"
```

- `Verification` — shown above.
- `Video` — shown above.
