{
  "components": {
    "schemas": {
      "AcknowledgeGapRequest": {
        "properties": {
          "gap": {
            "$ref": "#/components/schemas/GapRef"
          }
        },
        "required": [
          "gap"
        ],
        "title": "AcknowledgeGapRequest",
        "type": "object"
      },
      "AcknowledgeGapResponse": {
        "properties": {
          "acknowledgement": {
            "$ref": "#/components/schemas/GapAck"
          },
          "artifact": {
            "$ref": "#/components/schemas/AuthoredArtifact"
          }
        },
        "required": [
          "artifact",
          "acknowledgement"
        ],
        "title": "AcknowledgeGapResponse",
        "type": "object"
      },
      "AnalysisView": {
        "description": "What the Gap page reads after a reload, a restart, or a teammate opening\nthe project (round 17, F-02).",
        "properties": {
          "analysis_id": {
            "title": "Analysis Id",
            "type": "string"
          },
          "analyzer_age": {
            "default": "unknown",
            "enum": [
              "current",
              "older",
              "newer",
              "unknown"
            ],
            "title": "Analyzer Age",
            "type": "string"
          },
          "analyzer_version": {
            "title": "Analyzer Version",
            "type": "string"
          },
          "gaps": {
            "items": {
              "$ref": "#/components/schemas/Gap"
            },
            "title": "Gaps",
            "type": "array"
          },
          "related_refs": {
            "items": {
              "$ref": "#/components/schemas/RelatedRef"
            },
            "title": "Related Refs",
            "type": "array"
          },
          "suggestions": {
            "items": {
              "$ref": "#/components/schemas/Suggestion"
            },
            "title": "Suggestions",
            "type": "array"
          }
        },
        "required": [
          "analysis_id",
          "analyzer_age",
          "analyzer_version",
          "gaps",
          "related_refs",
          "suggestions"
        ],
        "title": "AnalysisView",
        "type": "object"
      },
      "ArtifactCreateRequest": {
        "properties": {
          "alias": {
            "title": "Alias",
            "type": "string"
          },
          "base": {
            "$ref": "#/components/schemas/DraftRef"
          },
          "overlays": {
            "items": {
              "$ref": "#/components/schemas/DraftRef"
            },
            "title": "Overlays",
            "type": "array"
          },
          "overrides": {
            "additionalProperties": true,
            "title": "Overrides",
            "type": "object"
          }
        },
        "required": [
          "base",
          "alias"
        ],
        "title": "ArtifactCreateRequest",
        "type": "object"
      },
      "ArtifactFreshnessView": {
        "description": "**One advisory per artifact, expandable to per-analysis detail.**\n\n`manual`-class in the UI's sense and **never a refusal**: it is not inserted\ninto the manual-gap collection the export gate refuses on. `readiness` is the\nartifact's own `source_analysis_state`, passed through \u2014 freshness and\nreadiness are different questions and this model keeps them apart.",
        "properties": {
          "artifact_id": {
            "title": "Artifact Id",
            "type": "string"
          },
          "components": {
            "items": {
              "$ref": "#/components/schemas/ComponentFreshnessView"
            },
            "title": "Components",
            "type": "array"
          },
          "legacy_coverage_rule": {
            "title": "Legacy Coverage Rule",
            "type": "boolean"
          },
          "outcome": {
            "enum": [
              "current",
              "older",
              "newer",
              "unknown",
              "unordered",
              "frozen_history",
              "invalid",
              "edited_revision",
              "no_inherited_analysis"
            ],
            "title": "Outcome",
            "type": "string"
          },
          "readiness": {
            "title": "Readiness",
            "type": "string"
          },
          "running_analyzer_version": {
            "title": "Running Analyzer Version",
            "type": "string"
          }
        },
        "required": [
          "artifact_id",
          "components",
          "legacy_coverage_rule",
          "outcome",
          "readiness",
          "running_analyzer_version"
        ],
        "title": "ArtifactFreshnessView",
        "type": "object"
      },
      "AssertNotApplicableRequest": {
        "description": "No free-text reason, by design (round 21, F-11): an unbounded, durable,\nuser-controlled field with no storage or redaction contract is a liability\nrather than an explanation. The audit row records who and when.",
        "properties": {
          "ref": {
            "$ref": "#/components/schemas/DraftRefBody"
          }
        },
        "required": [
          "ref"
        ],
        "title": "AssertNotApplicableRequest",
        "type": "object"
      },
      "AuthoredArtifact": {
        "description": "The unit that is governed, attested and handed off.",
        "properties": {
          "alias": {
            "title": "Alias",
            "type": "string"
          },
          "alias_generation": {
            "title": "Alias Generation",
            "type": "integer"
          },
          "analysis_ids": {
            "items": {
              "type": "string"
            },
            "title": "Analysis Ids",
            "type": "array"
          },
          "base": {
            "$ref": "#/components/schemas/DraftRef"
          },
          "created_at": {
            "title": "Created At",
            "type": "string"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "overlays": {
            "items": {
              "$ref": "#/components/schemas/DraftRef"
            },
            "title": "Overlays",
            "type": "array"
          },
          "overrides_sha256": {
            "title": "Overrides Sha256",
            "type": "string"
          },
          "project_id": {
            "title": "Project Id",
            "type": "string"
          },
          "revision": {
            "title": "Revision",
            "type": "string"
          },
          "source_analysis_state": {
            "enum": [
              "unknown",
              "not_applicable",
              "clear",
              "blocked",
              "stale",
              "attested"
            ],
            "title": "Source Analysis State",
            "type": "string"
          },
          "unacknowledged_manual_gaps": {
            "items": {
              "$ref": "#/components/schemas/GapRef"
            },
            "title": "Unacknowledged Manual Gaps",
            "type": "array"
          },
          "unresolved_blocking_gaps": {
            "items": {
              "$ref": "#/components/schemas/GapRef"
            },
            "title": "Unresolved Blocking Gaps",
            "type": "array"
          }
        },
        "required": [
          "alias",
          "alias_generation",
          "analysis_ids",
          "base",
          "created_at",
          "id",
          "overlays",
          "overrides_sha256",
          "project_id",
          "revision",
          "source_analysis_state",
          "unacknowledged_manual_gaps",
          "unresolved_blocking_gaps"
        ],
        "title": "AuthoredArtifact",
        "type": "object"
      },
      "CarriedAnalysisView": {
        "description": "One inherited analysis, as the expandable detail shows it.",
        "properties": {
          "age": {
            "enum": [
              "current",
              "older",
              "newer",
              "unknown"
            ],
            "title": "Age",
            "type": "string"
          },
          "analysis_id": {
            "title": "Analysis Id",
            "type": "string"
          },
          "analyzer_version": {
            "title": "Analyzer Version",
            "type": "string"
          },
          "importer_version": {
            "title": "Importer Version",
            "type": "string"
          },
          "state": {
            "enum": [
              "active",
              "quarantined"
            ],
            "title": "State",
            "type": "string"
          },
          "superseded_by": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Superseded By"
          }
        },
        "required": [
          "age",
          "analysis_id",
          "analyzer_version",
          "importer_version",
          "state",
          "superseded_by"
        ],
        "title": "CarriedAnalysisView",
        "type": "object"
      },
      "CheckpointDiff": {
        "description": "What changed between two checkpoints of one run.\n\n``truncated`` is the platform's own admission that the diff is partial, and\nit is carried rather than dropped: a truncated diff presented as a whole one\nis the same defect as an incomplete trace presented as complete.",
        "properties": {
          "added": {
            "items": {
              "additionalProperties": true,
              "type": "object"
            },
            "title": "Added",
            "type": "array"
          },
          "change_count": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Change Count"
          },
          "changed": {
            "items": {
              "additionalProperties": true,
              "type": "object"
            },
            "title": "Changed",
            "type": "array"
          },
          "from_checkpoint": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "From Checkpoint"
          },
          "removed": {
            "items": {
              "additionalProperties": true,
              "type": "object"
            },
            "title": "Removed",
            "type": "array"
          },
          "run_id": {
            "title": "Run Id",
            "type": "string"
          },
          "to_checkpoint": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "To Checkpoint"
          },
          "truncated": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Truncated"
          }
        },
        "required": [
          "added",
          "change_count",
          "changed",
          "from_checkpoint",
          "removed",
          "run_id",
          "to_checkpoint",
          "truncated"
        ],
        "title": "CheckpointDiff",
        "type": "object"
      },
      "CheckpointPage": {
        "description": "One page of a run's checkpoints, in sequence order.\n\n**``complete`` and ``dropped`` are not here, and the reason is upstream**\n`[0.8.0, Phase-5 own audit]`. This class carried them, with a docstring\nsaying they were *\"carried for the same reason the trace carries them: a\nstrip that cannot say whether it is showing all of them invites the reader\nto conclude things from an absence.\"* That sentence is **true of the\ntrace** \u2014 `TraceResponse` declares both \u2014 and was copied onto a response\nthat has neither: `CheckpointListResponse` declares nine fields and neither\nof these is among them. `kneo_client`'s own mapper says so in as many words,\nand reads them with `getattr(..., None)` precisely because they *\"exist only\non the trace response\"*.\n\nSo both were structurally **always ``null``**, the SPA's\n``complete === false`` branch could never fire, and this docstring promised\na reader something the platform does not send. Publishing a branch nothing\ncan fill is `A3`'s defect in the other direction \u2014 there the contract\ndeclared responses no handler could produce; here it declared fields no\nupstream could populate.\n\n**When `kneo_serv` adds them to the checkpoint list**, restore both fields\nhere, restore the strip's warning, and say so \u2014 the projection already reads\nthem through the same `Page`, so it is a schema change and a UI branch, not\na new call.",
        "properties": {
          "checkpoints": {
            "items": {
              "additionalProperties": true,
              "type": "object"
            },
            "title": "Checkpoints",
            "type": "array"
          },
          "limit": {
            "title": "Limit",
            "type": "integer"
          },
          "next_offset": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Next Offset"
          },
          "offset": {
            "title": "Offset",
            "type": "integer"
          },
          "paging_limit_reached": {
            "title": "Paging Limit Reached",
            "type": "boolean"
          },
          "returned": {
            "title": "Returned",
            "type": "integer"
          },
          "total": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Total"
          }
        },
        "required": [
          "checkpoints",
          "limit",
          "next_offset",
          "offset",
          "paging_limit_reached",
          "returned",
          "total"
        ],
        "title": "CheckpointPage",
        "type": "object"
      },
      "ComponentFreshnessView": {
        "description": "One contributing component, at the bytes S4 froze it at.",
        "properties": {
          "edited_revision": {
            "title": "Edited Revision",
            "type": "boolean"
          },
          "lineages": {
            "items": {
              "$ref": "#/components/schemas/LineageFreshnessView"
            },
            "title": "Lineages",
            "type": "array"
          },
          "outcome": {
            "enum": [
              "current",
              "older",
              "newer",
              "unknown",
              "unordered",
              "frozen_history",
              "invalid",
              "edited_revision",
              "no_inherited_analysis"
            ],
            "title": "Outcome",
            "type": "string"
          },
          "position": {
            "title": "Position",
            "type": "integer"
          },
          "ref": {
            "title": "Ref",
            "type": "string"
          },
          "revision": {
            "title": "Revision",
            "type": "string"
          }
        },
        "required": [
          "edited_revision",
          "lineages",
          "outcome",
          "position",
          "ref",
          "revision"
        ],
        "title": "ComponentFreshnessView",
        "type": "object"
      },
      "ConfirmRequest": {
        "properties": {
          "exposure_digest": {
            "title": "Exposure Digest",
            "type": "string"
          }
        },
        "required": [
          "exposure_digest"
        ],
        "title": "ConfirmRequest",
        "type": "object"
      },
      "CreateProjectRequest": {
        "description": "What a client may say when creating a project: a name, and nothing else.\n\n**No path field, deliberately.** The server allocates\n`<workspace_base>/<project-id>`, so an ordinary principal has no input that\nreaches the filesystem \u2014 which is what makes managed creation safe for them\nwhile *attaching* an existing tree stays an operator-only host command\n(`bff_routers`, ADR-009). A `root` field here would be the whole difference.",
        "properties": {
          "name": {
            "maxLength": 255,
            "minLength": 1,
            "title": "Name",
            "type": "string"
          }
        },
        "required": [
          "name"
        ],
        "title": "CreateProjectRequest",
        "type": "object"
      },
      "DraftBody": {
        "properties": {
          "ref": {
            "title": "Ref",
            "type": "string"
          },
          "revision": {
            "title": "Revision",
            "type": "string"
          },
          "text": {
            "title": "Text",
            "type": "string"
          }
        },
        "required": [
          "ref",
          "text",
          "revision"
        ],
        "title": "DraftBody",
        "type": "object"
      },
      "DraftReassociationView": {
        "description": "What the new ref now carries.",
        "properties": {
          "analysis_ids": {
            "items": {
              "type": "string"
            },
            "title": "Analysis Ids",
            "type": "array"
          },
          "new_ref": {
            "title": "New Ref",
            "type": "string"
          },
          "old_ref": {
            "title": "Old Ref",
            "type": "string"
          }
        },
        "required": [
          "analysis_ids",
          "new_ref",
          "old_ref"
        ],
        "title": "DraftReassociationView",
        "type": "object"
      },
      "DraftRef": {
        "description": "Identity **is** the canonical project-relative path (round 3, F-10).\n\nThe file-first model makes git and external renames normal events, and a\nstore id would need reconciliation semantics for both. ``revision`` is the\nSHA-256 of current bytes, so a write has a precondition to check.",
        "properties": {
          "path": {
            "title": "Path",
            "type": "string"
          },
          "revision": {
            "title": "Revision",
            "type": "string"
          }
        },
        "required": [
          "path",
          "revision"
        ],
        "title": "DraftRef",
        "type": "object"
      },
      "DraftRefBody": {
        "description": "The draft being asserted about, by path and expected revision.",
        "properties": {
          "path": {
            "title": "Path",
            "type": "string"
          },
          "revision": {
            "title": "Revision",
            "type": "string"
          }
        },
        "required": [
          "path",
          "revision"
        ],
        "title": "DraftRefBody",
        "type": "object"
      },
      "DraftSummary": {
        "properties": {
          "analysis_ids": {
            "items": {
              "type": "string"
            },
            "title": "Analysis Ids",
            "type": "array"
          },
          "can_assert_not_applicable": {
            "title": "Can Assert Not Applicable",
            "type": "boolean"
          },
          "capabilities": {
            "title": "Capabilities",
            "type": "string"
          },
          "kind": {
            "title": "Kind",
            "type": "string"
          },
          "not_applicable_asserted": {
            "title": "Not Applicable Asserted",
            "type": "boolean"
          },
          "ref": {
            "title": "Ref",
            "type": "string"
          },
          "registered_at": {
            "title": "Registered At",
            "type": "string"
          },
          "revision": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Revision"
          }
        },
        "required": [
          "ref",
          "kind",
          "capabilities",
          "registered_at",
          "revision",
          "analysis_ids",
          "can_assert_not_applicable",
          "not_applicable_asserted"
        ],
        "title": "DraftSummary",
        "type": "object"
      },
      "Environment": {
        "description": "The project-scoped **alias** and URL only.\n\nNever ``profile_name`` \u2014 that is server-side configuration \u2014 and never the\nkey. An earlier shape exposed ``name``, which would have leaked the profile\nidentifier and generated a SPA type unable to send the documented alias\n(round 2, F-14).",
        "properties": {
          "alias": {
            "title": "Alias",
            "type": "string"
          },
          "url": {
            "title": "Url",
            "type": "string"
          }
        },
        "required": [
          "alias",
          "url"
        ],
        "title": "Environment",
        "type": "object"
      },
      "ErrorEnvelope": {
        "description": "The body **every** refusal carries, as a published schema `[0.8.0 A3]`.\n\n`api_contract.md` has specified this shape since 0.1.0 and the BFF has\nreturned it since the Phase-5 audit universalised it. What was never true is\nthe **published** contract: FastAPI inferred `HTTPValidationError` \u2014\n`{\"detail\": [...]}` \u2014 onto 55 of 60 operations, a body no handler here can\nproduce, and published none of this one.\n\nThat is not a new discovery. `create_app`'s own comment records it as the\nreason `/docs` was switched off: *\"what it published was not true \u2026 an\ninteractive console that answers 'what does this API do' with a wrong answer\nis worse than no console, because it is believed.\"* Turning off the **viewer**\nleft the **artifact** \u2014 which is what the SPA's types are generated from under\nADR-011, and the only thing a client author reads \u2014 saying the same wrong\nthing, and `gen_openapi.py --check` stayed green because the committed file\nfaithfully reproduced the inference.\n\nIt is the body of **every refusal status an operation declares** \u2014 each\n`422`, each scaffold `501`, and each status a route lists in `responses=` \u2014\nattached by one publisher rather than by each route, so a route declaring a\nrefusal gets this body without naming it. The one declared non-2xx that is not\nan envelope is `GET /api/readyz`'s `503`, a readiness probe's answer, which\npublishes its own `Readiness` body.\n\n**A declared status is a floor, not the enumeration.** An operation can refuse\nwith a status it does not declare (most do not list `401`, `403`, `404` or\n`409`); the codes a client can switch on are enumerated in `api_contract.md`'s\nrefusal vocabulary, and every one answered in a response body arrives in this\nshape (the OIDC callback's codes travel in a redirect's query string instead).",
        "properties": {
          "code": {
            "description": "A stable, switchable refusal code. Never prose.",
            "examples": [
              "request_invalid"
            ],
            "title": "Code",
            "type": "string"
          },
          "details": {
            "additionalProperties": true,
            "description": "Per-refusal structure, present when the refusal has one. `request_invalid` carries `errors`, pydantic's per-field list with the offending `input` removed; a route's own refusal may carry nothing here.",
            "title": "Details",
            "type": "object"
          },
          "message": {
            "description": "A human sentence. Never switched on by a client.",
            "examples": [
              "The request body did not match the endpoint's schema."
            ],
            "title": "Message",
            "type": "string"
          },
          "request_id": {
            "description": "Always present, so every failure is traceable end to end. Carries the upstream platform id when the platform produced the failure.",
            "examples": [
              "bff-9f2c1a4e"
            ],
            "title": "Request Id",
            "type": "string"
          }
        },
        "required": [
          "code",
          "message",
          "request_id"
        ],
        "title": "ErrorEnvelope",
        "type": "object"
      },
      "ExportView": {
        "description": "What `GET \u2026/artifacts/{id}/export` returns.\n\nThe route returned a bare `dict`, so the OpenAPI described it as\n`additionalProperties: true` with no shape and the SPA's generated types\ncould say nothing about it \u2014 the hand-off page fell back to\n`Record<string, unknown>` and rendered the whole envelope as JSON rather\nthan the spec (Phase-5 audit, B6). A typed response is what makes the\nADR-011 generation chain reach this route at all.",
        "properties": {
          "artifact_id": {
            "title": "Artifact Id",
            "type": "string"
          },
          "base_ref": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Base Ref"
          },
          "filename": {
            "title": "Filename",
            "type": "string"
          },
          "spec_yaml": {
            "title": "Spec Yaml",
            "type": "string"
          }
        },
        "required": [
          "artifact_id",
          "base_ref",
          "spec_yaml",
          "filename"
        ],
        "title": "ExportView",
        "type": "object"
      },
      "ExposurePreflight": {
        "properties": {
          "exposure_digest": {
            "title": "Exposure Digest",
            "type": "string"
          },
          "manifest": {
            "items": {
              "$ref": "#/components/schemas/ManifestEntry"
            },
            "title": "Manifest",
            "type": "array"
          },
          "privileged_entries": {
            "items": {
              "type": "string"
            },
            "title": "Privileged Entries",
            "type": "array"
          },
          "root_identity": {
            "title": "Root Identity",
            "type": "string"
          },
          "status": {
            "enum": [
              "unconfirmed",
              "confirmed",
              "root_changed",
              "manifest_changed"
            ],
            "title": "Status",
            "type": "string"
          }
        },
        "required": [
          "root_identity",
          "exposure_digest",
          "privileged_entries",
          "manifest",
          "status"
        ],
        "title": "ExposurePreflight",
        "type": "object"
      },
      "ExposureView": {
        "description": "What an owner must read before registering, and confirm afterwards.",
        "properties": {
          "exposure_digest": {
            "title": "Exposure Digest",
            "type": "string"
          },
          "privileged_entries": {
            "items": {
              "type": "string"
            },
            "title": "Privileged Entries",
            "type": "array"
          },
          "root_identity": {
            "title": "Root Identity",
            "type": "string"
          }
        },
        "required": [
          "root_identity",
          "exposure_digest",
          "privileged_entries"
        ],
        "title": "ExposureView",
        "type": "object"
      },
      "FreshnessOffer": {
        "description": "An operation the user may run \u2014 **never a claim about what it will do**.\n\nThe store decides the effect and the projection is re-read afterwards. There\nis no \"this import will supersede\" and no \"re-importing cannot help\": four\nreview rounds each produced a version of that sentence and execution refuted\neach one.",
        "properties": {
          "analysis_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Analysis Id"
          },
          "kind": {
            "enum": [
              "re_import",
              "open_successor",
              "new_artifact",
              "quarantine_surface"
            ],
            "title": "Kind",
            "type": "string"
          },
          "ref": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Ref"
          }
        },
        "required": [
          "analysis_id",
          "kind",
          "ref"
        ],
        "title": "FreshnessOffer",
        "type": "object"
      },
      "Gap": {
        "properties": {
          "detail": {
            "title": "Detail",
            "type": "string"
          },
          "kind": {
            "title": "Kind",
            "type": "string"
          },
          "ref": {
            "$ref": "#/components/schemas/GapRef"
          },
          "severity": {
            "enum": [
              "blocking",
              "manual",
              "info"
            ],
            "title": "Severity",
            "type": "string"
          },
          "source_ref": {
            "$ref": "#/components/schemas/SourceRefView"
          },
          "spec_ref": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Spec Ref"
          },
          "title": {
            "title": "Title",
            "type": "string"
          }
        },
        "required": [
          "detail",
          "kind",
          "ref",
          "severity",
          "source_ref",
          "spec_ref",
          "title"
        ],
        "title": "Gap",
        "type": "object"
      },
      "GapAck": {
        "properties": {
          "acknowledged_at": {
            "title": "Acknowledged At",
            "type": "string"
          },
          "acknowledged_by_display_name": {
            "title": "Acknowledged By Display Name",
            "type": "string"
          },
          "gap": {
            "$ref": "#/components/schemas/GapRef"
          }
        },
        "required": [
          "gap",
          "acknowledged_by_display_name",
          "acknowledged_at"
        ],
        "title": "GapAck",
        "type": "object"
      },
      "GapRef": {
        "description": "A gap's identity. Never a bare ``gap_id`` (round 20, F-01).",
        "properties": {
          "analysis_id": {
            "title": "Analysis Id",
            "type": "string"
          },
          "gap_id": {
            "maxLength": 128,
            "title": "Gap Id",
            "type": "string"
          }
        },
        "required": [
          "analysis_id",
          "gap_id"
        ],
        "title": "GapRef",
        "type": "object"
      },
      "GapResolutionRequest": {
        "description": "One adjudication, bound to one card and one exact revision.\n\n**No free-text note, and its absence is a decision** (r3 F-03). The first draft\nrequired one, unbounded and durable \u2014 which is exactly the field\n`gap_acknowledgement.note` was removed for: user-controlled content with no size,\nredaction, encryption or retention contract. Who attested, when, which card and\nagainst which bytes *is* the record.",
        "properties": {
          "event": {
            "enum": [
              "attest",
              "retract"
            ],
            "title": "Event",
            "type": "string"
          },
          "expected_revision": {
            "title": "Expected Revision",
            "type": "string"
          },
          "gap_id": {
            "maxLength": 128,
            "title": "Gap Id",
            "type": "string"
          },
          "ref": {
            "title": "Ref",
            "type": "string"
          }
        },
        "required": [
          "gap_id",
          "ref",
          "expected_revision",
          "event"
        ],
        "title": "GapResolutionRequest",
        "type": "object"
      },
      "GapResolutionView": {
        "description": "The effective state after the call, recomputed from the log.\n\n`outstanding_blocking_gaps` is **recomputed server-side at that revision** rather\nthan being derived by the client from a stale local list, so one round trip tells\nan author exactly what remains. `GapRef` pairs, never bare ids \u2014 a bare `gap_id`\nis unique only within one analysis (r4 F-03).",
        "properties": {
          "analysis_id": {
            "title": "Analysis Id",
            "type": "string"
          },
          "effective_event": {
            "enum": [
              "attest",
              "retract"
            ],
            "title": "Effective Event",
            "type": "string"
          },
          "gap_id": {
            "title": "Gap Id",
            "type": "string"
          },
          "outstanding_blocking_gaps": {
            "items": {
              "$ref": "#/components/schemas/GapRef"
            },
            "title": "Outstanding Blocking Gaps",
            "type": "array"
          },
          "revision": {
            "title": "Revision",
            "type": "string"
          },
          "seq": {
            "title": "Seq",
            "type": "integer"
          }
        },
        "required": [
          "analysis_id",
          "effective_event",
          "gap_id",
          "outstanding_blocking_gaps",
          "revision",
          "seq"
        ],
        "title": "GapResolutionView",
        "type": "object"
      },
      "GovernRequest": {
        "description": "No alias, no draft, no overlays \u2014 the artifact carries all of it.",
        "properties": {
          "artifact_id": {
            "title": "Artifact Id",
            "type": "string"
          }
        },
        "required": [
          "artifact_id"
        ],
        "title": "GovernRequest",
        "type": "object"
      },
      "GraphEdge": {
        "description": "One transition.\n\n``label`` and ``conditional`` are **independent**: upstream compiles a\ncondition to a callable it cannot serialize, so it sends only the boolean\n``conditional`` alongside a free ``label``. An earlier version of this model\ninvented a single ``condition`` string from them, which would have drawn a\ngated edge as a plain one whenever the label was absent.\n\n``priority`` is not projected: it exists upstream but is always ``0`` for\nspec-compiled graphs, so rendering it would imply an ordering that is not\nthere.",
        "properties": {
          "conditional": {
            "default": false,
            "title": "Conditional",
            "type": "boolean"
          },
          "label": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Label"
          },
          "source": {
            "title": "Source",
            "type": "string"
          },
          "target": {
            "title": "Target",
            "type": "string"
          }
        },
        "required": [
          "conditional",
          "label",
          "source",
          "target"
        ],
        "title": "GraphEdge",
        "type": "object"
      },
      "GraphNode": {
        "description": "One node of the compiled topology.\n\n``label`` projects the platform's ``name``; a ``sequential`` step's ``kind``\nis ``null`` upstream and becomes ``\"step\"`` here so the canvas always has\nsomething to render. The node error-policy fields the platform also sends\n(``on_error``, ``fallback_node``, ``max_retries``, ``timeout_seconds``) are\ndeliberately not projected yet \u2014 they are `graph`-family only and belong\nwith the editor read path, not the topology spike.",
        "properties": {
          "id": {
            "title": "Id",
            "type": "string"
          },
          "kind": {
            "title": "Kind",
            "type": "string"
          },
          "label": {
            "title": "Label",
            "type": "string"
          }
        },
        "required": [
          "id",
          "kind",
          "label"
        ],
        "title": "GraphNode",
        "type": "object"
      },
      "GraphView": {
        "description": "The **platform-rendered** topology (ADR-004).\n\nStudio never derives a graph from the YAML, so the picture and the spec\ncannot disagree. ``available`` is false when the workflow family has no\ntopology to draw \u2014 the SPA must render that state rather than an empty\ncanvas that looks like a broken one.\n\n``sequential`` **does** render: upstream serializes it as a linear chain.\nOnly orchestration, handoff, group-chat and agent-only specs return\n``workflow_kind: null``, and upstream does not say *which*, so the reason\nstring here is necessarily general until the effective spec is built (0.2.0)\nand its own ``workflow.type`` can name the family.",
        "properties": {
          "available": {
            "title": "Available",
            "type": "boolean"
          },
          "edges": {
            "items": {
              "$ref": "#/components/schemas/GraphEdge"
            },
            "title": "Edges",
            "type": "array"
          },
          "nodes": {
            "items": {
              "$ref": "#/components/schemas/GraphNode"
            },
            "title": "Nodes",
            "type": "array"
          },
          "start": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Start"
          },
          "unavailable_reason": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Unavailable Reason"
          },
          "workflow_kind": {
            "anyOf": [
              {
                "enum": [
                  "graph",
                  "sequential"
                ],
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Workflow Kind"
          }
        },
        "required": [
          "available",
          "edges",
          "nodes",
          "start",
          "unavailable_reason",
          "workflow_kind"
        ],
        "title": "GraphView",
        "type": "object"
      },
      "ImportResult": {
        "description": "**Import is draft-only in 0.1.0, in both binding states.**\n\n``artifact`` and ``portability`` are ``null`` unconditionally. The bound\nbranch requires and validates an alias and then writes a draft like any\nother; creating the artifact is the separate, explicit\n``POST \u2026/artifacts`` step.\n\nThe fields stay on the model rather than being removed: the shape is the\n0.2.0 target, and a client that already reads them keeps working when they\nstart being populated. What was wrong was the *documentation* \u2014 three\npublished pages and the SPA all described a bound import returning an\nartifact, so the SPA carried a branch that could never run\n(external review, F-10).",
        "properties": {
          "analysis_ids": {
            "items": {
              "type": "string"
            },
            "title": "Analysis Ids",
            "type": "array"
          },
          "artifact": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/AuthoredArtifact"
              },
              {
                "type": "null"
              }
            ]
          },
          "draft": {
            "$ref": "#/components/schemas/DraftRef"
          },
          "gaps": {
            "items": {
              "$ref": "#/components/schemas/Gap"
            },
            "title": "Gaps",
            "type": "array"
          },
          "portability": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Portability"
              },
              {
                "type": "null"
              }
            ]
          },
          "spec_yaml": {
            "title": "Spec Yaml",
            "type": "string"
          },
          "suggestions": {
            "items": {
              "$ref": "#/components/schemas/Suggestion"
            },
            "title": "Suggestions",
            "type": "array"
          },
          "summary": {
            "$ref": "#/components/schemas/ImportSummary"
          }
        },
        "required": [
          "analysis_ids",
          "artifact",
          "draft",
          "gaps",
          "portability",
          "spec_yaml",
          "suggestions",
          "summary"
        ],
        "title": "ImportResult",
        "type": "object"
      },
      "ImportSource": {
        "properties": {
          "accepts": {
            "items": {
              "type": "string"
            },
            "title": "Accepts",
            "type": "array"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "label": {
            "title": "Label",
            "type": "string"
          },
          "status": {
            "enum": [
              "supported",
              "experimental",
              "planned"
            ],
            "title": "Status",
            "type": "string"
          }
        },
        "required": [
          "id",
          "label",
          "status",
          "accepts"
        ],
        "title": "ImportSource",
        "type": "object"
      },
      "ImportSources": {
        "properties": {
          "sources": {
            "items": {
              "$ref": "#/components/schemas/ImportSource"
            },
            "title": "Sources",
            "type": "array"
          }
        },
        "required": [
          "sources"
        ],
        "title": "ImportSources",
        "type": "object"
      },
      "ImportSummary": {
        "properties": {
          "credential_count": {
            "title": "Credential Count",
            "type": "integer"
          },
          "edge_count": {
            "title": "Edge Count",
            "type": "integer"
          },
          "node_count": {
            "title": "Node Count",
            "type": "integer"
          },
          "source": {
            "title": "Source",
            "type": "string"
          },
          "tool_count": {
            "title": "Tool Count",
            "type": "integer"
          },
          "unsupported_count": {
            "title": "Unsupported Count",
            "type": "integer"
          },
          "workflow_family": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/WorkflowFamily"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "source",
          "workflow_family",
          "node_count",
          "edge_count",
          "tool_count",
          "credential_count",
          "unsupported_count"
        ],
        "title": "ImportSummary",
        "type": "object"
      },
      "InvitationRequest": {
        "properties": {
          "exposure_digest": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Exposure Digest"
          },
          "role": {
            "$ref": "#/components/schemas/Role"
          },
          "selector_kind": {
            "title": "Selector Kind",
            "type": "string"
          },
          "selector_value": {
            "title": "Selector Value",
            "type": "string"
          }
        },
        "required": [
          "role",
          "selector_kind",
          "selector_value"
        ],
        "title": "InvitationRequest",
        "type": "object"
      },
      "InvitationView": {
        "properties": {
          "expires_at": {
            "format": "date-time",
            "title": "Expires At",
            "type": "string"
          },
          "held_reason": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Held Reason"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "role": {
            "$ref": "#/components/schemas/Role"
          },
          "selector_kind": {
            "title": "Selector Kind",
            "type": "string"
          },
          "selector_value": {
            "title": "Selector Value",
            "type": "string"
          }
        },
        "required": [
          "id",
          "role",
          "selector_kind",
          "selector_value",
          "expires_at",
          "held_reason"
        ],
        "title": "InvitationView",
        "type": "object"
      },
      "LineageFreshnessView": {
        "description": "One lineage \u2014 the four-member key \u2014 beneath a component.\n\nTwo sources with identical output are two lineages, each with its own outcome\nand its own offers; a version that counted live matches without grouping this\nway read them as one unorderable lineage.",
        "properties": {
          "age": {
            "anyOf": [
              {
                "enum": [
                  "current",
                  "older",
                  "newer",
                  "unknown"
                ],
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Age"
          },
          "carried": {
            "items": {
              "$ref": "#/components/schemas/CarriedAnalysisView"
            },
            "title": "Carried",
            "type": "array"
          },
          "elected_analysis_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Elected Analysis Id"
          },
          "elected_in_frozen_set": {
            "title": "Elected In Frozen Set",
            "type": "boolean"
          },
          "importer": {
            "title": "Importer",
            "type": "string"
          },
          "invalid": {
            "title": "Invalid",
            "type": "boolean"
          },
          "offers": {
            "items": {
              "$ref": "#/components/schemas/FreshnessOffer"
            },
            "title": "Offers",
            "type": "array"
          },
          "ordered": {
            "title": "Ordered",
            "type": "boolean"
          },
          "outcome": {
            "enum": [
              "current",
              "older",
              "newer",
              "unknown",
              "unordered",
              "frozen_history",
              "invalid"
            ],
            "title": "Outcome",
            "type": "string"
          },
          "output_sha256": {
            "title": "Output Sha256",
            "type": "string"
          },
          "source_sha256": {
            "title": "Source Sha256",
            "type": "string"
          }
        },
        "required": [
          "age",
          "carried",
          "elected_analysis_id",
          "elected_in_frozen_set",
          "importer",
          "invalid",
          "offers",
          "ordered",
          "outcome",
          "output_sha256",
          "source_sha256"
        ],
        "title": "LineageFreshnessView",
        "type": "object"
      },
      "LoginRequest": {
        "properties": {
          "bootstrap_claim": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Bootstrap Claim"
          },
          "password": {
            "minLength": 1,
            "title": "Password",
            "type": "string"
          },
          "username": {
            "maxLength": 256,
            "minLength": 1,
            "title": "Username",
            "type": "string"
          }
        },
        "required": [
          "username",
          "password"
        ],
        "title": "LoginRequest",
        "type": "object"
      },
      "ManifestEntry": {
        "description": "One registered ref, annotated with why it counts or does not.\n\n`privileged` is the annotation that matters: those are the entries that\nfeed `exposure_digest`, so they are what an owner is being asked about.\nStudio-created drafts are listed too \u2014 an owner deciding what sharing this\ntree reveals should see the whole manifest, not the subset that happens to\nmove the digest.",
        "properties": {
          "kind": {
            "title": "Kind",
            "type": "string"
          },
          "privileged": {
            "title": "Privileged",
            "type": "boolean"
          },
          "ref": {
            "title": "Ref",
            "type": "string"
          }
        },
        "required": [
          "ref",
          "kind",
          "privileged"
        ],
        "title": "ManifestEntry",
        "type": "object"
      },
      "Me": {
        "properties": {
          "auth_mode": {
            "enum": [
              "none",
              "static",
              "oidc"
            ],
            "title": "Auth Mode",
            "type": "string"
          },
          "display_name": {
            "title": "Display Name",
            "type": "string"
          },
          "principal_id": {
            "title": "Principal Id",
            "type": "string"
          },
          "projects": {
            "items": {
              "$ref": "#/components/schemas/Project"
            },
            "title": "Projects",
            "type": "array"
          }
        },
        "required": [
          "principal_id",
          "display_name",
          "auth_mode",
          "projects"
        ],
        "title": "Me",
        "type": "object"
      },
      "Member": {
        "properties": {
          "display_name": {
            "title": "Display Name",
            "type": "string"
          },
          "is_quota_principal": {
            "title": "Is Quota Principal",
            "type": "boolean"
          },
          "principal_id": {
            "title": "Principal Id",
            "type": "string"
          },
          "role": {
            "$ref": "#/components/schemas/Role"
          }
        },
        "required": [
          "principal_id",
          "display_name",
          "role",
          "is_quota_principal"
        ],
        "title": "Member",
        "type": "object"
      },
      "MemberRequest": {
        "properties": {
          "exposure_digest": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Exposure Digest"
          },
          "role": {
            "$ref": "#/components/schemas/Role"
          }
        },
        "required": [
          "role"
        ],
        "title": "MemberRequest",
        "type": "object"
      },
      "PlatformTestRun": {
        "description": "The **platform's** view of a started run \u2014 its lifecycle, not Studio's.\n\nPresent only when `StudioTestRun.status` is ``started``, and read live from\n``platform.runs.get`` against the run's *recorded* binding. ``status`` here may\nindependently be queued, running, blocked, completed or failed while Studio's own\nstatus stays ``started``: Studio records that it knows one platform id, and does\nnot mirror the platform lifecycle into its row.",
        "properties": {
          "error": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Error"
          },
          "output_text": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Output Text"
          },
          "status": {
            "title": "Status",
            "type": "string"
          },
          "usage": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/RunUsage"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "error",
          "output_text",
          "status",
          "usage"
        ],
        "title": "PlatformTestRun",
        "type": "object"
      },
      "Portability": {
        "description": "``handoffable \u21d4 blockers == []``. Recomputed per read, never persisted.",
        "properties": {
          "blockers": {
            "items": {
              "$ref": "#/components/schemas/PortabilityBlocker"
            },
            "title": "Blockers",
            "type": "array"
          }
        },
        "required": [
          "blockers"
        ],
        "title": "Portability",
        "type": "object"
      },
      "PortabilityBlocker": {
        "properties": {
          "code": {
            "enum": [
              "registry_reference",
              "filesystem_dependency",
              "spec_path_reference",
              "unsupported_transport"
            ],
            "title": "Code",
            "type": "string"
          },
          "detail": {
            "title": "Detail",
            "type": "string"
          },
          "spec_ref": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Spec Ref"
          }
        },
        "required": [
          "code",
          "detail",
          "spec_ref"
        ],
        "title": "PortabilityBlocker",
        "type": "object"
      },
      "PreflightCredentialsView": {
        "description": "The credential references the platform exposes for this artifact's frozen\nbinding \u2014 **redacted entries, never values**.\n\n``credentials`` is keyed by section (``providers``, ``extra``,\n``service_tokens``), each mapping a name to the platform's redacted entry\n(``present``/``status``/``env_var``\u2026). ``None`` when ``state`` is ``unavailable``.",
        "properties": {
          "alias": {
            "title": "Alias",
            "type": "string"
          },
          "alias_generation": {
            "title": "Alias Generation",
            "type": "integer"
          },
          "credentials": {
            "anyOf": [
              {
                "additionalProperties": {
                  "additionalProperties": true,
                  "type": "object"
                },
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Credentials"
          },
          "platform_environment": {
            "title": "Platform Environment",
            "type": "string"
          },
          "state": {
            "enum": [
              "available",
              "unavailable"
            ],
            "title": "State",
            "type": "string"
          },
          "unavailable_reason": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Unavailable Reason"
          }
        },
        "required": [
          "alias",
          "alias_generation",
          "credentials",
          "platform_environment",
          "state",
          "unavailable_reason"
        ],
        "title": "PreflightCredentialsView",
        "type": "object"
      },
      "PreflightPolicyView": {
        "description": "The target environment's policy, as the platform holds it for this artifact's\nfrozen binding.\n\n``policy`` is the platform's own body, untyped on the wire (`kneo_serv` returns a\nfree-form policy object), passed through rather than re-modelled so Studio cannot\nquietly drop a key the Dashboard shows. ``None`` when ``state`` is ``unavailable``,\nor when the platform holds no policy for the environment.",
        "properties": {
          "alias": {
            "title": "Alias",
            "type": "string"
          },
          "alias_generation": {
            "title": "Alias Generation",
            "type": "integer"
          },
          "platform_environment": {
            "title": "Platform Environment",
            "type": "string"
          },
          "policy": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Policy"
          },
          "state": {
            "enum": [
              "available",
              "unavailable"
            ],
            "title": "State",
            "type": "string"
          },
          "unavailable_reason": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Unavailable Reason"
          }
        },
        "required": [
          "alias",
          "alias_generation",
          "platform_environment",
          "policy",
          "state",
          "unavailable_reason"
        ],
        "title": "PreflightPolicyView",
        "type": "object"
      },
      "PreviewRequest": {
        "description": "The editor's buffer, and which draft it is a buffer *for*.\n\n**`alias` is required, and round 2's F-02 is why.** Preview calls the platform, and\n`client_for(runtime, project_id, alias, \u2026)` cannot be built without one \u2014 the\nartifact-addressed govern routes take theirs from the artifact row, and preview has\nno artifact. Omitting it made the first version of this contract unimplementable\nrather than merely thin.",
        "properties": {
          "alias": {
            "title": "Alias",
            "type": "string"
          },
          "draft": {
            "$ref": "#/components/schemas/DraftRefBody"
          },
          "text": {
            "title": "Text",
            "type": "string"
          }
        },
        "required": [
          "draft",
          "text",
          "alias"
        ],
        "title": "PreviewRequest",
        "type": "object"
      },
      "PreviewView": {
        "description": "`{revision, ok, stages}` \u2014 one entry per stage that RAN, in order.\n\n**A stage that did not run is absent**, which is how *validate-not-run* and\n*compile-not-run* are distinguished without inventing a third truth value. Compile\nis absent exactly when validate failed, because that is what\n`validate_then_compile` actually does.\n\n`ok` is true only if every stage ran **and** every stage passed.\n\n`revision` is the request's own, copied verbatim: advisory as an input \u2014 preview\njudges the text, not the file \u2014 and echoed so a client can discard a response that\nraced its own buffer. Round 3's F-02 found four paragraphs promising that echo\nwhile the model had no field to carry it.",
        "properties": {
          "ok": {
            "title": "Ok",
            "type": "boolean"
          },
          "revision": {
            "title": "Revision",
            "type": "string"
          },
          "stages": {
            "items": {
              "$ref": "#/components/schemas/StageView"
            },
            "title": "Stages",
            "type": "array"
          }
        },
        "required": [
          "ok",
          "revision",
          "stages"
        ],
        "title": "PreviewView",
        "type": "object"
      },
      "Project": {
        "properties": {
          "binding_state": {
            "enum": [
              "bound",
              "unbound"
            ],
            "title": "Binding State",
            "type": "string"
          },
          "created_at": {
            "title": "Created At",
            "type": "string"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "name": {
            "title": "Name",
            "type": "string"
          },
          "role": {
            "enum": [
              "owner",
              "editor",
              "viewer"
            ],
            "title": "Role",
            "type": "string"
          },
          "storage_kind": {
            "enum": [
              "managed",
              "attached"
            ],
            "title": "Storage Kind",
            "type": "string"
          }
        },
        "required": [
          "id",
          "name",
          "role",
          "created_at",
          "binding_state",
          "storage_kind"
        ],
        "title": "Project",
        "type": "object"
      },
      "ProjectLimits": {
        "description": "Read-only, and no principal changes a cap (round 22, F-09). In this cut\nthe caps are fixed module constants \u2014 no command raises one yet (r12 F-03).",
        "properties": {
          "nearest_cap": {
            "enum": [
              "project",
              "principal"
            ],
            "title": "Nearest Cap",
            "type": "string"
          },
          "principal_taint_cap": {
            "title": "Principal Taint Cap",
            "type": "integer"
          },
          "principal_taint_used": {
            "title": "Principal Taint Used",
            "type": "integer"
          },
          "retained_max": {
            "title": "Retained Max",
            "type": "integer"
          },
          "retained_used": {
            "title": "Retained Used",
            "type": "integer"
          },
          "taint_cap": {
            "title": "Taint Cap",
            "type": "integer"
          },
          "taint_used": {
            "title": "Taint Used",
            "type": "integer"
          }
        },
        "required": [
          "taint_used",
          "taint_cap",
          "principal_taint_used",
          "principal_taint_cap",
          "retained_used",
          "retained_max",
          "nearest_cap"
        ],
        "title": "ProjectLimits",
        "type": "object"
      },
      "Readiness": {
        "description": "What a probe is told. Short on purpose.\n\nNo version, no configuration, no counts: this answers unauthenticated, so\neverything in it is public, and an availability probe is not a place to\npublish facts about a deployment.",
        "properties": {
          "status": {
            "enum": [
              "ready",
              "degraded"
            ],
            "title": "Status",
            "type": "string"
          },
          "store": {
            "title": "Store",
            "type": "boolean"
          }
        },
        "required": [
          "status",
          "store"
        ],
        "title": "Readiness",
        "type": "object"
      },
      "ReassociationRequest": {
        "description": "Both refs, the revision they must **both** hold, and the exposure aggregate.",
        "properties": {
          "expected_revision": {
            "title": "Expected Revision",
            "type": "string"
          },
          "exposure_digest": {
            "title": "Exposure Digest",
            "type": "string"
          },
          "new_ref": {
            "title": "New Ref",
            "type": "string"
          },
          "old_ref": {
            "title": "Old Ref",
            "type": "string"
          }
        },
        "required": [
          "old_ref",
          "new_ref",
          "expected_revision",
          "exposure_digest"
        ],
        "title": "ReassociationRequest",
        "type": "object"
      },
      "RegisterRequest": {
        "description": "Registering a file that already existed under the project root.\n\n``exposure_digest`` is a **compare-and-set**, not an audit field: it proves\nthe caller read the project's current exposure before widening it. Without\nit an owner adds entries one at a time, each individually reasonable, and\nnever faces the aggregate \u2014 which is how the manifest gets rebuilt into the\nexposure it exists to prevent (round 22, F-06).",
        "properties": {
          "capabilities": {
            "default": "read",
            "enum": [
              "read",
              "read_write"
            ],
            "title": "Capabilities",
            "type": "string"
          },
          "exposure_digest": {
            "title": "Exposure Digest",
            "type": "string"
          },
          "kind": {
            "default": "spec",
            "enum": [
              "spec",
              "overlay",
              "skill_source"
            ],
            "title": "Kind",
            "type": "string"
          },
          "path": {
            "title": "Path",
            "type": "string"
          }
        },
        "required": [
          "path",
          "exposure_digest"
        ],
        "title": "RegisterRequest",
        "type": "object"
      },
      "RelatedRef": {
        "description": "A draft this analysis relates to, and what that file holds **right now**.\n\n**The Gap page's identity problem, solved server-side** (0.3.0, r3 F-01). The page\nreceives only an `analysisId` and its routes carry no draft ref, so it cannot\nconstruct a resolution request \u2014 which needs both a `ref` and an\n`expected_revision`. This is what it reads instead.\n\n`can_attest` is computed here rather than in the client precisely because the\nclient would have to re-derive the route's own precondition \u2014 an attestation\nrequires an edit, so the revision must differ from the analysis output. Deriving\nit twice is how a button and the rule it represents drift apart.",
        "properties": {
          "availability": {
            "default": "present",
            "enum": [
              "present",
              "missing"
            ],
            "title": "Availability",
            "type": "string"
          },
          "can_attest": {
            "default": false,
            "title": "Can Attest",
            "type": "boolean"
          },
          "path": {
            "title": "Path",
            "type": "string"
          },
          "revision": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Revision"
          },
          "standing_attestations": {
            "items": {
              "$ref": "#/components/schemas/StandingAttestation"
            },
            "title": "Standing Attestations",
            "type": "array"
          }
        },
        "required": [
          "availability",
          "can_attest",
          "path",
          "revision",
          "standing_attestations"
        ],
        "title": "RelatedRef",
        "type": "object"
      },
      "Role": {
        "enum": [
          "viewer",
          "editor",
          "owner"
        ],
        "title": "Role",
        "type": "string"
      },
      "RunGraph": {
        "description": "The run's graph, and where execution has reached in it.\n\n``current`` and ``visited`` are what the canvas animates. They are the\nplatform's, not Studio's: a picture Studio derived from the trace would be a\nsecond source that can disagree with the run it claims to describe.",
        "properties": {
          "current": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Current"
          },
          "edges": {
            "items": {
              "$ref": "#/components/schemas/GraphEdge"
            },
            "title": "Edges",
            "type": "array"
          },
          "max_steps": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Max Steps"
          },
          "nodes": {
            "items": {
              "$ref": "#/components/schemas/GraphNode"
            },
            "title": "Nodes",
            "type": "array"
          },
          "run_id": {
            "title": "Run Id",
            "type": "string"
          },
          "start": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Start"
          },
          "status": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Status"
          },
          "visited": {
            "items": {
              "type": "string"
            },
            "title": "Visited",
            "type": "array"
          },
          "workflow_kind": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Workflow Kind"
          }
        },
        "required": [
          "current",
          "edges",
          "max_steps",
          "nodes",
          "run_id",
          "start",
          "status",
          "visited",
          "workflow_kind"
        ],
        "title": "RunGraph",
        "type": "object"
      },
      "RunUsage": {
        "description": "Token usage, as the platform reports it for one run.",
        "properties": {
          "input_tokens": {
            "title": "Input Tokens",
            "type": "integer"
          },
          "output_tokens": {
            "title": "Output Tokens",
            "type": "integer"
          },
          "total_tokens": {
            "title": "Total Tokens",
            "type": "integer"
          }
        },
        "required": [
          "input_tokens",
          "output_tokens",
          "total_tokens"
        ],
        "title": "RunUsage",
        "type": "object"
      },
      "SaveDraftRequest": {
        "description": "The buffer, and the revision the author believed they were editing.",
        "properties": {
          "expected_revision": {
            "title": "Expected Revision",
            "type": "string"
          },
          "text": {
            "title": "Text",
            "type": "string"
          }
        },
        "required": [
          "text",
          "expected_revision"
        ],
        "title": "SaveDraftRequest",
        "type": "object"
      },
      "SaveDraftResponse": {
        "description": "The revision recomputed from the bytes that were actually written.",
        "properties": {
          "revision": {
            "title": "Revision",
            "type": "string"
          }
        },
        "required": [
          "revision"
        ],
        "title": "SaveDraftResponse",
        "type": "object"
      },
      "SourceRefView": {
        "properties": {
          "locator": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Locator"
          },
          "source_id": {
            "title": "Source Id",
            "type": "string"
          },
          "source_label": {
            "title": "Source Label",
            "type": "string"
          }
        },
        "required": [
          "locator",
          "source_id",
          "source_label"
        ],
        "title": "SourceRefView",
        "type": "object"
      },
      "StageView": {
        "description": "One governance stage's verdict.\n\n``ResponseModel`` because ``detail`` is always sent \u2014 ``{}`` when a stage\nhas nothing to add \u2014 and the contract said it might be absent. Found by the\nspec-driven half of ``test_response_contract``, not by the survey that\nproduced this change: that survey read ``kneo_studio/schemas/``, and a\nresponse model declared next to its router was outside the net it cast.",
        "properties": {
          "detail": {
            "additionalProperties": true,
            "title": "Detail",
            "type": "object"
          },
          "ok": {
            "title": "Ok",
            "type": "boolean"
          },
          "stage": {
            "title": "Stage",
            "type": "string"
          }
        },
        "required": [
          "detail",
          "ok",
          "stage"
        ],
        "title": "StageView",
        "type": "object"
      },
      "StandingAttestation": {
        "description": "Testimony that still stands, and the bytes it was given about.\n\nBoth halves, because `(analysis, gap, revision)` is the event's identity and a\nwithdrawal has to name it. Reporting the gap without the revision is what left\nthe client deriving the revision from the file \u2014 the mistake that produced three\nfail-open variants across two external rounds.",
        "properties": {
          "gap_id": {
            "title": "Gap Id",
            "type": "string"
          },
          "revision": {
            "title": "Revision",
            "type": "string"
          }
        },
        "required": [
          "gap_id",
          "revision"
        ],
        "title": "StandingAttestation",
        "type": "object"
      },
      "StopResult": {
        "description": "What asking the platform to stop a run actually did.\n\n**Three outcomes, never collapsed into \"it worked\"** (0.7.0 S10, review\nround 3). The platform answers `200` whether it recorded a cancellation or\nhad nothing to cancel, so a caller reading the status code alone would\nannounce a cancellation that never happened.\n\n* ``cancelled`` \u2014 the platform returned ``cancelled``. **True of two\n  histories** \u2014 this request recorded it, or an earlier one did \u2014 and the\n  status cannot say which, so the sentence claims only what holds for both\n  `[touchpoint 2 round 1, F-04]`. This arm was ``already_cancelled`` and read\n  every first Stop as a repeat, on the wire and in the audit row.\n* ``already_finished`` \u2014 it had reached a terminal status. A successful\n  **no-op**: not an error, and not silence.\n* ``requested`` \u2014 a live status came back. The installed producer never\n  returns one from a cancel; this is the fallback for one that might.\n\n`message` is the sentence to show. It is server-side because the three\nreadings turn on the platform's semantics \u2014 cooperative cancellation, and no\npromise that completed effects are reversed \u2014 and a client re-deriving them\nwould be re-deriving a contract it does not own.",
        "properties": {
          "message": {
            "title": "Message",
            "type": "string"
          },
          "outcome": {
            "enum": [
              "requested",
              "already_finished",
              "cancelled"
            ],
            "title": "Outcome",
            "type": "string"
          },
          "status": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Status"
          }
        },
        "required": [
          "message",
          "outcome",
          "status"
        ],
        "title": "StopResult",
        "type": "object"
      },
      "StudioTestRun": {
        "description": "One test run, as Studio knows it.\n\n``id`` is the Studio ``test_run.id`` and is what ``{run_id}`` names in the route \u2014\n**never the platform id**, which is ``platform_run_id`` and is never accepted as a\nlocator. ``status`` is Studio's knowledge of the *dispatch*:\n\n* ``pending`` \u2014 dispatch or reconciliation has not reached a fenced conclusion;\n* ``started`` \u2014 Studio knows exactly one platform id; ``platform`` is live;\n* ``failed_to_start`` \u2014 Studio has **proof** no dispatch was accepted;\n* ``unknown`` \u2014 correlation completed but could not prove exactly one run;\n* ``abandoned`` \u2014 recovery exhausted its attempt budget.\n\nA non-null ``platform_run_id`` is an **invariant of** ``started``, not a partial\nfact attached to another status. The four non-started states carry\n``platform: null`` and cost no platform call.",
        "properties": {
          "artifact_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Artifact Id"
          },
          "created_at": {
            "title": "Created At",
            "type": "string"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "platform": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PlatformTestRun"
              },
              {
                "type": "null"
              }
            ]
          },
          "platform_run_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Platform Run Id"
          },
          "status": {
            "enum": [
              "pending",
              "started",
              "failed_to_start",
              "abandoned",
              "unknown"
            ],
            "title": "Status",
            "type": "string"
          },
          "updated_at": {
            "title": "Updated At",
            "type": "string"
          }
        },
        "required": [
          "artifact_id",
          "created_at",
          "id",
          "platform",
          "platform_run_id",
          "status",
          "updated_at"
        ],
        "title": "StudioTestRun",
        "type": "object"
      },
      "Suggestion": {
        "description": "Never a verdict. The policy-report stage's own ``StageView`` is the\nauthoritative answer, and the SPA must render the two distinctly.",
        "properties": {
          "category": {
            "enum": [
              "tool_permission",
              "guardrail",
              "human_approval",
              "secret_ref"
            ],
            "title": "Category",
            "type": "string"
          },
          "detail": {
            "title": "Detail",
            "type": "string"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "rationale": {
            "title": "Rationale",
            "type": "string"
          },
          "spec_ref": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Spec Ref"
          },
          "title": {
            "title": "Title",
            "type": "string"
          }
        },
        "required": [
          "category",
          "detail",
          "id",
          "rationale",
          "spec_ref",
          "title"
        ],
        "title": "Suggestion",
        "type": "object"
      },
      "TestRunCreateRequest": {
        "additionalProperties": false,
        "description": "`{artifact_id, input}` and nothing else.\n\nClosed to extra fields on purpose: alias, generation, profile, environment, the\nspec, overlays, overrides, `async_mode`, `session_id` and the idempotency key are\nall derived server-side from the artifact and never accepted from the caller. A\nbody that names one of them is a `422`, not a silently ignored field \u2014 the latter\nis how a client comes to believe it chose the environment.",
        "properties": {
          "artifact_id": {
            "title": "Artifact Id",
            "type": "string"
          },
          "input": {
            "title": "Input",
            "type": "string"
          }
        },
        "required": [
          "artifact_id",
          "input"
        ],
        "title": "TestRunCreateRequest",
        "type": "object"
      },
      "TraceWindow": {
        "description": "One page of a trace, with everything needed to describe it honestly.\n\nA `ResponseModel`, so every field is **present and nullable** rather than\noptional: \"the key was missing\" and \"the platform said null\" are not\ndifferent answers, and a panel branching on the difference is writing a\nbranch against nothing.",
        "properties": {
          "complete": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Complete"
          },
          "dropped": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Dropped"
          },
          "events": {
            "items": {
              "additionalProperties": true,
              "type": "object"
            },
            "title": "Events",
            "type": "array"
          },
          "limit": {
            "title": "Limit",
            "type": "integer"
          },
          "next_offset": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Next Offset"
          },
          "offset": {
            "title": "Offset",
            "type": "integer"
          },
          "paging_limit_reached": {
            "title": "Paging Limit Reached",
            "type": "boolean"
          },
          "returned": {
            "title": "Returned",
            "type": "integer"
          },
          "run_status": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Run Status"
          },
          "run_terminal": {
            "title": "Run Terminal",
            "type": "boolean"
          },
          "total": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Total"
          },
          "window": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Window"
          }
        },
        "required": [
          "complete",
          "dropped",
          "events",
          "limit",
          "next_offset",
          "offset",
          "paging_limit_reached",
          "returned",
          "run_status",
          "run_terminal",
          "total",
          "window"
        ],
        "title": "TraceWindow",
        "type": "object"
      },
      "TransferRequest": {
        "properties": {
          "to_principal": {
            "title": "To Principal",
            "type": "string"
          }
        },
        "required": [
          "to_principal"
        ],
        "title": "TransferRequest",
        "type": "object"
      },
      "TransferView": {
        "properties": {
          "expires_at": {
            "title": "Expires At",
            "type": "string"
          },
          "from_principal": {
            "title": "From Principal",
            "type": "string"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "project_id": {
            "title": "Project Id",
            "type": "string"
          },
          "proposed_at": {
            "title": "Proposed At",
            "type": "string"
          },
          "state": {
            "title": "State",
            "type": "string"
          },
          "to_principal": {
            "title": "To Principal",
            "type": "string"
          }
        },
        "required": [
          "expires_at",
          "from_principal",
          "id",
          "project_id",
          "proposed_at",
          "state",
          "to_principal"
        ],
        "title": "TransferView",
        "type": "object"
      },
      "WorkflowFamily": {
        "enum": [
          "sequential",
          "graph"
        ],
        "title": "WorkflowFamily",
        "type": "string"
      }
    }
  },
  "info": {
    "title": "Kneo Agent Studio BFF",
    "version": "0.9.0"
  },
  "openapi": "3.1.0",
  "paths": {
    "/api/import/sources": {
      "get": {
        "description": "The registry backing the source picker.",
        "operationId": "list_sources_api_import_sources_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ImportSources"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "List Sources",
        "tags": [
          "import"
        ]
      }
    },
    "/api/me": {
      "get": {
        "description": "The caller, and the projects they may open.\n\nDeliberately **not** behind :func:`authorize`: that helper is\nproject-scoped, and this route is what the SPA calls to discover which\nprojects exist for it. Under ``auth_mode = none`` the caller is the\nbootstrapped local owner \u2014 a real principal owning real rows, not a bypass\n(ADR-008/012), which is what keeps 0.2.0's login modes a migration rather\nthan a rewrite.",
        "operationId": "me_api_me_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Me"
                }
              }
            },
            "description": "Successful Response"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`unauthenticated` \u2014 no session, or one that no longer resolves, under a login mode (`static` or `oidc`). Never under `auth_mode = none`."
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "The caller's principal is not active in this store \u2014 the store is not the one this process bootstrapped. No code of its own (`http_503`)."
          }
        },
        "summary": "Me",
        "tags": [
          "identity"
        ]
      }
    },
    "/api/projects": {
      "get": {
        "description": "The caller's projects. Membership *is* the filter \u2014 never a directory.",
        "operationId": "list_projects_api_projects_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/Project"
                  },
                  "title": "Response List Projects Api Projects Get",
                  "type": "array"
                }
              }
            },
            "description": "Successful Response"
          },
          "401": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "`unauthenticated` \u2014 no session, or one that no longer resolves, under a login mode (`static` or `oidc`). Never under `auth_mode = none`."
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Under `auth_mode = none`, the local owner is no longer active (an adopted instance started under `none` again). No code of its own (`http_503`)."
          }
        },
        "summary": "List Projects",
        "tags": [
          "identity"
        ]
      },
      "post": {
        "description": "Create a **managed** project; the creator becomes its owner.\n\nRequires an authenticated caller and answers **401** without one. Not\nbehind `authorize`, for the same reason as `GET /api/projects`: that helper\nis project-scoped and there is no project yet. Unscoped is not\nunauthenticated \u2014 the bar is *any authenticated principal*\n(`bff_routers`), which under `auth_mode = none` is the bootstrapped local\nowner.\n\nThe request carries only a name: the server allocates\n`<workspace_base>/<project-id>`, and a root that equals, contains or sits\ninside another project's is refused (`store.projects.create_managed`).\nAttaching an existing tree stays an operator-only host command.",
        "operationId": "create_project_api_projects_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateProjectRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Create Project",
        "tags": [
          "identity"
        ]
      }
    },
    "/api/projects/{project_id}": {
      "get": {
        "description": "One project, if the caller is a member of it.\n\n``authorize`` answers **404** when they are not, which is the point: a 403\nwould confirm the id exists to someone with no business knowing.",
        "operationId": "get_project_api_projects__project_id__get",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Get Project",
        "tags": [
          "identity"
        ]
      }
    },
    "/api/projects/{project_id}/analyses": {
      "get": {
        "description": "Candidate analyses for these bytes.\n\nDeliberately a **list**: the same output can carry several analyses, so\n\"the analysis for these bytes\" is not a function (round 21, F-01).\nSuperseded associations are excluded \u2014 inheritance follows only the active\nsuccessor (round 22, F-02).",
        "operationId": "list_candidates_api_projects__project_id__analyses_get",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          },
          {
            "description": "the normalized draft's digest",
            "in": "query",
            "name": "output_sha256",
            "required": true,
            "schema": {
              "description": "the normalized draft's digest",
              "title": "Output Sha256",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "type": "string"
                  },
                  "title": "Response List Candidates Api Projects  Project Id  Analyses Get",
                  "type": "array"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "List Candidates",
        "tags": [
          "analyses"
        ]
      }
    },
    "/api/projects/{project_id}/analyses/{analysis_id}": {
      "get": {
        "description": "The exact analysis \u2014 titles, details, severities and suggestions.",
        "operationId": "read_analysis_api_projects__project_id__analyses__analysis_id__get",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "analysis_id",
            "required": true,
            "schema": {
              "title": "Analysis Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalysisView"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Read Analysis",
        "tags": [
          "analyses"
        ]
      }
    },
    "/api/projects/{project_id}/analyses/{analysis_id}/resolutions": {
      "post": {
        "description": "Attest that an edit addressed a blocking card, or withdraw that. **Owner.**\n\nThe transition that makes the authoring loop terminate. An imported draft's\nanalysis is keyed to its content, so editing it leaves the new bytes with no\nmatching analysis \u2014 `unknown`, which does not export \u2014 and the one escape,\n`assert_not_applicable`, is deliberately refused for a draft that ever carried an\nanalysis. This is the audited, per-card way through.\n\n**Every precondition here is load-bearing, and one of them is the whole design:**\n\n* the analysis must be **related to the ref** in `manifest_analysis`. One analysis\n  can relate to several refs and one ref to several analyses, so neither side may\n  be inferred from the other;\n* `expected_revision` is compare-and-set against the bytes on disk, so an\n  attestation cannot be written about a file that moved under the author;\n* an `attest` whose revision equals the analysis's own `output_sha256` is\n  **refused**. Without that, an owner could attest against the *unchanged\n  imported bytes* for every blocking id and export them \u2014 the whole-draft result\n  one card at a time.\n\nAnswers the effective state and the **recomputed** outstanding set, so an author\nlearns what remains in one round trip rather than deriving it from a stale list.",
        "operationId": "record_gap_resolution_api_projects__project_id__analyses__analysis_id__resolutions_post",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "analysis_id",
            "required": true,
            "schema": {
              "title": "Analysis Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GapResolutionRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GapResolutionView"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Record Gap Resolution",
        "tags": [
          "analyses"
        ]
      }
    },
    "/api/projects/{project_id}/artifacts": {
      "get": {
        "description": "Every live artifact in the project, newest first.\n\n**Nothing could enumerate them.** An artifact was reachable only by the\nnavigation that created it, and that navigation lived in transient React\nstate \u2014 so a reload, a new tab or a restart lost the only route to it, on a\nproduct whose pitch is a durable governed unit. Drafts have had a list route\nsince the first cut; artifacts never got one (external review, F-05).\n\nSoft-deleted artifacts are excluded: `deleted_at` is the tombstone the\nexport route answers `410` for, and a picker offering them would lead the\nreader somewhere that refuses by design.\n\nSame projection as the single read, so a list entry and the page it opens\ncannot disagree about the artifact they describe.",
        "operationId": "list_artifacts_api_projects__project_id__artifacts_get",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/AuthoredArtifact"
                  },
                  "title": "Response List Artifacts Api Projects  Project Id  Artifacts Get",
                  "type": "array"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "List Artifacts",
        "tags": [
          "artifacts"
        ]
      },
      "post": {
        "description": "Create an artifact from authored inputs and freeze what it inherits.",
        "operationId": "create_artifact_api_projects__project_id__artifacts_post",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ArtifactCreateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthoredArtifact"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Create Artifact",
        "tags": [
          "artifacts"
        ]
      }
    },
    "/api/projects/{project_id}/artifacts/{artifact_id}": {
      "delete": {
        "description": "Soft-delete an artifact and release the snapshot it held. **Editor.**\n\n**The recovery path the save-any-text decision needs.** Once any buffer can be\nsaved and explicitly frozen, an unwanted creation charges quota with no\nuser-visible way back \u2014 and snapshot GC has nothing to collect for an ordinary\nartifact, because nothing ever drops a reference. `store.artifacts.soft_delete`\nshipped in 0.1.0 with no caller and was exempted in the declared-but-unwired gate\nas *\"no delete route yet\"*; this is that caller.\n\n**Soft**, not a row deletion: an artifact is a provenance record, and export\nrefusals, acknowledgements and attestations all point at it. `deleted_at` is what\nexport reads to answer `410`.\n\nAuthorized as `CREATE_ARTIFACT` \u2014 the same capability that made the thing.\nDeleting your own mistake must not need more authority than making it, and a\nseparate action would be a role boundary nobody asked for.\n\nOne transaction: the tombstone and the reference drop cannot part company, or a\nsnapshot stays charged forever with nothing pointing at it. The **collection** runs\nafter the commit, deliberately \u2014 it deletes a directory, and filesystem effects do\nnot belong inside a database transaction.",
        "operationId": "delete_artifact_api_projects__project_id__artifacts__artifact_id__delete",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "artifact_id",
            "required": true,
            "schema": {
              "title": "Artifact Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Delete Artifact",
        "tags": [
          "artifacts"
        ]
      },
      "get": {
        "description": "One artifact, as the SPA needs it after a reload.\n\nCreation returned this projection and nothing could fetch it again, so a\npage addressed by `artifact_id` \u2014 the editor, hand-off \u2014 had no way to\nlearn its own base draft or its analysis state without the navigation that\ncreated it. A refresh emptied the page.\n\n`_reload` is the same projection the acknowledgement response returns, so\nthe two cannot drift into disagreeing about the artifact they describe.",
        "operationId": "read_artifact_api_projects__project_id__artifacts__artifact_id__get",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "artifact_id",
            "required": true,
            "schema": {
              "title": "Artifact Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthoredArtifact"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Read Artifact",
        "tags": [
          "artifacts"
        ]
      }
    },
    "/api/projects/{project_id}/artifacts/{artifact_id}/acknowledgements": {
      "post": {
        "description": "Acknowledge a ``manual`` gap frozen by one of this artifact's analyses.\n\nRound 18 promised export refuses an unacknowledged ``manual`` gap and gave\nthat gate **no record, route, authorization rule or SPA action** (round 19,\nF-03). Editor-only, audited, and idempotent on the full triple \u2014 the\n``analysis_id`` is part of the key because two inherited analyses may reuse\na local ``gap_id`` (round 20, F-01).",
        "operationId": "acknowledge_gap_api_projects__project_id__artifacts__artifact_id__acknowledgements_post",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "artifact_id",
            "required": true,
            "schema": {
              "title": "Artifact Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AcknowledgeGapRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AcknowledgeGapResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Acknowledge Gap",
        "tags": [
          "artifacts"
        ]
      }
    },
    "/api/projects/{project_id}/artifacts/{artifact_id}/export": {
      "get": {
        "description": "Readiness, then a **fresh** govern gate over the exact export body.\n\nRefuses with **every** reason, not just the first (round 6, F-03). The gate\nruns at request time rather than reading a persisted verdict, because a\nstored verdict goes stale the moment an input changes (round 16, F-04).",
        "operationId": "export_artifact_api_projects__project_id__artifacts__artifact_id__export_get",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "artifact_id",
            "required": true,
            "schema": {
              "title": "Artifact Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExportView"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Export Artifact",
        "tags": [
          "artifacts"
        ]
      }
    },
    "/api/projects/{project_id}/artifacts/{artifact_id}/freshness": {
      "get": {
        "description": "How old the analyses this artifact inherited are `[0.7.0 S13]`.\n\n**A projection, never a refusal.** It reads permanent metadata columns and\nruns the store's own ordering rules over them; it inserts nothing into the\nmanual-gap collection `assert_exportable` refuses on, and it rewrites no\nprovenance. `readiness` is the artifact's own state, passed through.\n\n**A route rather than an SPA derivation**, because the ordinary analysis\nendpoint answers `410` for a reclaimed payload and the age of a retained row\nhas to survive that \u2014 a client reading that `410` as *anything* is reading the\nabsence of a payload as a fact about a version.",
        "operationId": "analysis_freshness_api_projects__project_id__artifacts__artifact_id__freshness_get",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "artifact_id",
            "required": true,
            "schema": {
              "title": "Artifact Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArtifactFreshnessView"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Analysis Freshness",
        "tags": [
          "artifacts"
        ]
      }
    },
    "/api/projects/{project_id}/artifacts/{artifact_id}/preflight/credential-refs": {
      "get": {
        "description": "The credential references the **deployment** the artifact's frozen binding\nreaches exposes (`platform.credentials.list`) \u2014 redacted entries, never values.\n\n**Not environment-scoped, and the docs said it was** (0.7.0 S11, decision 4).\n`GET /v1/security/credentials` takes no environment: the frozen binding selects\nthe profile, and so which deployment is asked, but it does not narrow what that\ndeployment answers. The policy sibling above *is* scoped \u2014 its route names the\nenvironment in the path \u2014 and reading the two as a matched pair is the mistake.",
        "operationId": "preflight_credential_refs_api_projects__project_id__artifacts__artifact_id__preflight_credential_refs_get",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "artifact_id",
            "required": true,
            "schema": {
              "title": "Artifact Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PreflightCredentialsView"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Preflight Credential Refs",
        "tags": [
          "artifacts"
        ]
      }
    },
    "/api/projects/{project_id}/artifacts/{artifact_id}/preflight/policy": {
      "get": {
        "description": "The target environment's policy, read at the artifact's **frozen** generation\n(`platform.policies.environment_get`).",
        "operationId": "preflight_policy_api_projects__project_id__artifacts__artifact_id__preflight_policy_get",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "artifact_id",
            "required": true,
            "schema": {
              "title": "Artifact Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PreflightPolicyView"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Preflight Policy",
        "tags": [
          "artifacts"
        ]
      }
    },
    "/api/projects/{project_id}/drafts": {
      "get": {
        "description": "The project's **registered manifest** \u2014 never a directory listing.\n\nAn attached project starts empty, and that is the designed first-run state:\nStudio has been pointed at a working directory, not granted it.",
        "operationId": "list_drafts_api_projects__project_id__drafts_get",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/DraftSummary"
                  },
                  "title": "Response List Drafts Api Projects  Project Id  Drafts Get",
                  "type": "array"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "List Drafts",
        "tags": [
          "drafts"
        ]
      }
    },
    "/api/projects/{project_id}/drafts/assertions": {
      "post": {
        "description": "Assert that this draft needs no analysis. **Project owner only.**\n\nThe sole path out of `unknown`, and `unknown` does not export. Owner rather\nthan editor because it converts \"nobody assessed this\" into \"somebody\ndecided it needed no assessment\" \u2014 a judgement, not an edit.",
        "operationId": "assert_draft_not_applicable_api_projects__project_id__drafts_assertions_post",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AssertNotApplicableRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Assert Draft Not Applicable",
        "tags": [
          "drafts"
        ]
      }
    },
    "/api/projects/{project_id}/drafts/exposure": {
      "get": {
        "description": "The aggregate a registration is compare-and-set against.\n\nDerived, never stored: a digest kept in a column drifts from the entries it\nsummarises, and the drift is invisible precisely when it matters.\n\n**This route computed its three fields from four observations** \u2014 `root_for`\ntwice, `root_identity` once, and `exposure_digest` recomputing both the\nidentity and the entries. So the digest it hands out could describe a tree or\na manifest other than the one it displayed, and that digest is what\n`POST \u2026/drafts/register` compare-and-sets against: an owner could widen an\nexposure they had not been shown. The same defect the external review found\ntwice in the exposure preflight, in the sibling route it did not reach \u2014 see\n`drafts.Exposure`. One observation now.",
        "operationId": "read_exposure_api_projects__project_id__drafts_exposure_get",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExposureView"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Read Exposure",
        "tags": [
          "drafts"
        ]
      }
    },
    "/api/projects/{project_id}/drafts/reassociations": {
      "post": {
        "description": "Carry a draft's analyses to a path holding the same bytes.\n\n**This is what makes a rename survivable.** An analysis relates to a *ref*, and both\nthe resolution route and artifact creation require the relation to be on the\n**current** ref \u2014 so after a bare `git mv` the transition simply stops, fail-closed,\nwith an artifact that cannot be created and gaps that cannot be resolved.\n\n**It moves no file and reads only the target**, so an external `git mv` stays an\nordinary event and no filesystem effect sits inside the transaction.\n\nCopy, not move: the old ref keeps its relations, so `assert_not_applicable` stays\nrefused there and a reappearing file cannot be laundered into a clean one.\n\nRefusals are the specification's, and each names which side failed \u2014\n`reassociation_source_not_found`, `reassociation_source_revision_unknown`,\n`draft_stale` with the offending ref, `reassociation_target_conflict`,\n`exposure_stale`, and `404 draft_not_found` for anything about the target that\ncannot be read. A retry after a lost response is idempotent while the preconditions\nstill hold; an intervening content or exposure change is not a retry of the same\nstate and gets the matching stale refusal.",
        "operationId": "reassociate_draft_api_projects__project_id__drafts_reassociations_post",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReassociationRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DraftReassociationView"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Reassociate Draft",
        "tags": [
          "drafts"
        ]
      }
    },
    "/api/projects/{project_id}/drafts/register": {
      "post": {
        "description": "Register a pre-existing file. **Project owner only.**\n\nOwner rather than editor because round 10 gave it to anyone with write,\nwhich hands a team editor the entire attached checkout one registration at\na time \u2014 the exposure the manifest exists to prevent, rebuilt through the\nmanifest itself.",
        "operationId": "register_draft_api_projects__project_id__drafts_register_post",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RegisterRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Register Draft",
        "tags": [
          "drafts"
        ]
      }
    },
    "/api/projects/{project_id}/drafts/{ref}": {
      "get": {
        "description": "One registered draft.\n\n``{ref:path}`` accepts slashes deliberately \u2014 a draft ref is a relative\npath, and refusing nesting at the router would only push callers toward\nencoding separators, which is harder to validate, not easier.\n:func:`kneo_studio.drafts.normalize_ref` is what makes that safe.",
        "operationId": "read_draft_api_projects__project_id__drafts__ref__get",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "ref",
            "required": true,
            "schema": {
              "title": "Ref",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DraftBody"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Read Draft",
        "tags": [
          "drafts"
        ]
      },
      "put": {
        "description": "Save a draft. **Any text saves** \u2014 validity is not this route's business.\n\nA draft is a *file* (ADR-009). Refusing to persist invalid YAML would lose an\nauthor's work mid-thought and push them to edit outside Studio, which is the\nopposite of what the loop is for. Validity is enforced by **preview, the govern\nloop and the export gate** \u2014 not here, and not at artifact creation, which\nresolves an alias and hashes a snapshot without parsing it.\n\n**`expected_revision` is a compare-and-set**, and a mismatch is\n`409 draft_stale` carrying `{path, expected_revision, revision}` \u2014 the body the\nerror table already defines for that code. The comparison and the write happen\nunder one project lock, so a concurrent save cannot land between them.\n\n**Atomic per draft, and not across drafts.** The store writes a temporary file\nand renames it, so a concurrent reader sees old or new bytes and never a partial\nfile. A multi-file save is N independent calls and the client owns the\nsequencing: one transaction spanning arbitrary files is a lock-ordering hazard\nfor no gain.\n\nWriting bytes does **not** create, supersede or invalidate an analysis \u2014\nanalysis follows content by hash, and what an edited revision needs is the\nremediation protocol's attestation.",
        "operationId": "save_draft_api_projects__project_id__drafts__ref__put",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "ref",
            "required": true,
            "schema": {
              "title": "Ref",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SaveDraftRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SaveDraftResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Save Draft",
        "tags": [
          "drafts"
        ]
      }
    },
    "/api/projects/{project_id}/drafts/{ref}/registration": {
      "delete": {
        "description": "Drop a manifest entry **without touching the file**.\n\nNo compare-and-set: this narrows exposure, and making the safe direction\nharder than the dangerous one is how people stop using it.",
        "operationId": "drop_draft_registration_api_projects__project_id__drafts__ref__registration_delete",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "ref",
            "required": true,
            "schema": {
              "title": "Ref",
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Drop Draft Registration",
        "tags": [
          "drafts"
        ]
      }
    },
    "/api/projects/{project_id}/environments": {
      "get": {
        "description": "The project's bound aliases \u2014 **alias and URL only** (ADR-014).\n\nNever the API key, and never ``profile_name``: the profile identifier is\nserver-side configuration, and an earlier shape that exposed it would have\ngenerated a SPA type unable to send the documented alias (round 2, F-14).\n\nThe URL is read from the resolved `kneo_client` profile rather than stored,\nbecause it is mutable external config \u2014 which is exactly why\n``endpoint_fingerprint`` exists. An alias whose profile is missing is still\nlisted, with an empty URL: \"configured but not resolvable\" is a state the\ndeveloper needs to see, not one to hide by omission.",
        "operationId": "list_environments_api_projects__project_id__environments_get",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/Environment"
                  },
                  "title": "Response List Environments Api Projects  Project Id  Environments Get",
                  "type": "array"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "List Environments",
        "tags": [
          "identity"
        ]
      }
    },
    "/api/projects/{project_id}/environments/{environment}/credential-refs": {
      "get": {
        "description": "List credential references a spec may bind to (`platform.credentials.list`).\n\n**Per environment**, not per project. The scaffold omitted `{environment}`,\nwhich `bff_routers.md` had right \u2014 a doc-vs-code audit caught it. Without\nthe segment the route cannot say *whose* credentials it lists, and a\nlater implementation would have had to change its own URL to find out.",
        "operationId": "credential_refs_api_projects__project_id__environments__environment__credential_refs_get",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "environment",
            "required": true,
            "schema": {
              "title": "Environment",
              "type": "string"
            }
          }
        ],
        "responses": {
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          },
          "501": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Not implemented in this cut"
          }
        },
        "summary": "Credential Refs",
        "tags": [
          "environments"
        ]
      }
    },
    "/api/projects/{project_id}/environments/{environment}/policy": {
      "get": {
        "description": "Read an environment policy for pre-deploy checks (`platform.policies.environment_get`).",
        "operationId": "get_policy_api_projects__project_id__environments__environment__policy_get",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "environment",
            "required": true,
            "schema": {
              "title": "Environment",
              "type": "string"
            }
          }
        ],
        "responses": {
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          },
          "501": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Not implemented in this cut"
          }
        },
        "summary": "Get Policy",
        "tags": [
          "environments"
        ]
      }
    },
    "/api/projects/{project_id}/exposure/confirm": {
      "post": {
        "description": "Compare-and-set: record that *this* exposure was reviewed and accepted.\n\nRewrites `root_identity` **and** `exposure_digest` together. Writing one\nwithout the other would leave a confirmation that reopens content while\nstill describing a boundary nobody agreed to.",
        "operationId": "confirm_exposure_api_projects__project_id__exposure_confirm_post",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConfirmRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExposurePreflight"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Confirm Exposure",
        "tags": [
          "exposure"
        ]
      }
    },
    "/api/projects/{project_id}/exposure/preflight": {
      "get": {
        "description": "What the owner must read before confirming. Read-only, and never gated.\n\nOwner-only rather than viewer-readable, unlike `GET \u2026/drafts/exposure`: this\none lists the whole manifest, and the person being asked to accept an\nexposure is the only one who can act on it.",
        "operationId": "read_preflight_api_projects__project_id__exposure_preflight_get",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExposurePreflight"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Read Preflight",
        "tags": [
          "exposure"
        ]
      }
    },
    "/api/projects/{project_id}/import/{source}": {
      "post": {
        "description": "Parse + normalize a foreign artifact into a draft spec and gap cards.",
        "operationId": "import_artifact_api_projects__project_id__import__source__post",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "source",
            "required": true,
            "schema": {
              "title": "Source",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "additionalProperties": true,
                "title": "Body",
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ImportResult"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Import Artifact",
        "tags": [
          "import"
        ]
      }
    },
    "/api/projects/{project_id}/invitations": {
      "get": {
        "description": "The owner's own pending grants.\n\nNot a disclosure: these are the invitations *this project* sent, so it says\nonly what an owner already did. It is also the only way to discover an id to\nrevoke, since creation deliberately returns none.",
        "operationId": "list_invitations_api_projects__project_id__invitations_get",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/InvitationView"
                  },
                  "title": "Response List Invitations Api Projects  Project Id  Invitations Get",
                  "type": "array"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "List Invitations",
        "tags": [
          "members"
        ]
      },
      "post": {
        "description": "Invite by configured identity. **Always the same answer.**\n\n`202` with a fixed body whether or not that identity exists here. Anything\nthat varied \u2014 a different status, an id that is only returned on a hit, even\na different latency \u2014 turns membership into an identity-existence oracle an\nowner can walk one candidate at a time. Being an owner of one project is not\na reason to be able to enumerate the instance.\n\nAudited, so bulk probing is visible in aggregate even though a single\nattempt is indistinguishable.",
        "operationId": "create_invitation_api_projects__project_id__invitations_post",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InvitationRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": {
                    "type": "string"
                  },
                  "title": "Response Create Invitation Api Projects  Project Id  Invitations Post",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Create Invitation",
        "tags": [
          "members"
        ]
      }
    },
    "/api/projects/{project_id}/invitations/{invitation_id}": {
      "delete": {
        "operationId": "revoke_invitation_api_projects__project_id__invitations__invitation_id__delete",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "invitation_id",
            "required": true,
            "schema": {
              "title": "Invitation Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Revoke Invitation",
        "tags": [
          "members"
        ]
      }
    },
    "/api/projects/{project_id}/limits": {
      "get": {
        "description": "Current taint usage and caps, read-only.\n\nNo principal changes a cap, and no API route does (round 22, F-04): the\nearlier wording named a remedy with no route, no request model and no\nauthorization entry. **In this cut the caps are fixed module constants** \u2014\nno command raises one either, so this reports how close a project is rather\nthan what to ask an operator for (r12 F-03).",
        "operationId": "read_limits_api_projects__project_id__limits_get",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProjectLimits"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Read Limits",
        "tags": [
          "analyses"
        ]
      }
    },
    "/api/projects/{project_id}/members": {
      "get": {
        "description": "Members of a project the caller belongs to \u2014 never a principal search.",
        "operationId": "list_members_api_projects__project_id__members_get",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/Member"
                  },
                  "title": "Response List Members Api Projects  Project Id  Members Get",
                  "type": "array"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "List Members",
        "tags": [
          "members"
        ]
      }
    },
    "/api/projects/{project_id}/members/{member_id}": {
      "delete": {
        "description": "Revoke membership. Owner-only, and never the last owner.",
        "operationId": "remove_member_api_projects__project_id__members__member_id__delete",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "member_id",
            "required": true,
            "schema": {
              "title": "Member Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/Member"
                  },
                  "title": "Response Remove Member Api Projects  Project Id  Members  Member Id  Delete",
                  "type": "array"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Remove Member",
        "tags": [
          "members"
        ]
      },
      "put": {
        "description": "Add a member, or change their role. Owner-only.",
        "operationId": "put_member_api_projects__project_id__members__member_id__put",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "member_id",
            "required": true,
            "schema": {
              "title": "Member Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MemberRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/Member"
                  },
                  "title": "Response Put Member Api Projects  Project Id  Members  Member Id  Put",
                  "type": "array"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Put Member",
        "tags": [
          "members"
        ]
      }
    },
    "/api/projects/{project_id}/quota-transfer": {
      "get": {
        "description": "The project's open proposal, or `null`.\n\nReadable by any member, not only the two principals involved: a proposal\nchanges who is liable for the project's bytes, and a member finding that\nout only after it completes is the surprise this surfaces to avoid.",
        "operationId": "read_open_transfer_api_projects__project_id__quota_transfer_get",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/TransferView"
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "title": "Response Read Open Transfer Api Projects  Project Id  Quota Transfer Get"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Read Open Transfer",
        "tags": [
          "quota"
        ]
      },
      "post": {
        "description": "Offer the charge to another member. Only the charged principal may.",
        "operationId": "propose_transfer_api_projects__project_id__quota_transfer_post",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TransferRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransferView"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Propose Transfer",
        "tags": [
          "quota"
        ]
      }
    },
    "/api/projects/{project_id}/quota-transfer/{transfer_id}/accept": {
      "post": {
        "description": "Take the charge. Only the named target may.\n\nThe move happens **in one transaction**: the project's\n`quota_principal_id`, both principals' accounted bytes, and the ledger rows\nthose decrements will later read. Splitting any of it leaves a project\ncharged to one principal while its ledger names another, which no later\noperation would notice.",
        "operationId": "accept_transfer_api_projects__project_id__quota_transfer__transfer_id__accept_post",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "transfer_id",
            "required": true,
            "schema": {
              "title": "Transfer Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransferView"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Accept Transfer",
        "tags": [
          "quota"
        ]
      }
    },
    "/api/projects/{project_id}/quota-transfer/{transfer_id}/decline": {
      "post": {
        "description": "Refuse the charge. Only the named target may.\n\n**Not in the original two-route design, and added deliberately.** Without\nit a target's only escape from an unwanted liability is to wait out the\nseven-day expiry, and `declined` \u2014 a value in the table's own CHECK\nconstraint \u2014 is a state nothing could ever write. A state no path reaches\nis the defect class this cut built a gate for.",
        "operationId": "decline_transfer_api_projects__project_id__quota_transfer__transfer_id__decline_post",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "transfer_id",
            "required": true,
            "schema": {
              "title": "Transfer Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransferView"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Decline Transfer",
        "tags": [
          "quota"
        ]
      }
    },
    "/api/projects/{project_id}/specs/compile": {
      "post": {
        "description": "`agent.specs.validate_then_compile` \u2014 the pair, in one call.\n\nIts docstring says it \"captures the Studio iterate-and-test pattern in one\ncall\", so Studio uses it rather than hand-sequencing.",
        "operationId": "compile_spec_api_projects__project_id__specs_compile_post",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GovernRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StageView"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Compile Spec",
        "tags": [
          "specs"
        ]
      }
    },
    "/api/projects/{project_id}/specs/explain": {
      "post": {
        "description": "`agent.specs.explain` \u2014 declared, not implemented.",
        "operationId": "explain_api_projects__project_id__specs_explain_post",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          },
          "501": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Not implemented in this cut"
          }
        },
        "summary": "Explain",
        "tags": [
          "specs"
        ]
      }
    },
    "/api/projects/{project_id}/specs/graph": {
      "post": {
        "description": "The compiled ``nodes``/``edges``/``start`` topology, for the canvas.\n\nStudio never derives a graph from the YAML itself, so the picture and the\nspec cannot disagree (ADR-004). A ``sequential`` workflow has no branching\ntopology to draw, and the response says so explicitly rather than returning\nan empty graph the SPA would render as a broken canvas.",
        "operationId": "graph_api_projects__project_id__specs_graph_post",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GovernRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GraphView"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Graph",
        "tags": [
          "specs"
        ]
      }
    },
    "/api/projects/{project_id}/specs/policy-report": {
      "post": {
        "description": "`agent.specs.policy_report` \u2014 the authoritative governance verdict.\n\nNon-mutating on the run estate, so a viewer may call it. Not side-effect\nfree: it writes a ``spec.*`` audit event upstream, and a 403 here is a\ngovernance **verdict** the UI must render rather than an error.",
        "operationId": "policy_report_api_projects__project_id__specs_policy_report_post",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GovernRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StageView"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Policy Report",
        "tags": [
          "specs"
        ]
      }
    },
    "/api/projects/{project_id}/specs/preview": {
      "post": {
        "description": "Validate and compile an **unsaved buffer**. Viewer; writes nothing.\n\nThe editor's inner loop. Stateless by contract \u2014 no draft, no analysis, no audit\nrow \u2014 which is what makes it safe to call repeatedly from the editor; a stateful\npreview would put an unbounded write path behind a text box.\n\n**Explicitly not an attestation**: no portability verdict, no `policy_report`, and\nits result cannot be handed off (round 11, F-03). A negative verdict is a\n**`200` with `ok: false`** \u2014 a completed gate with a bad answer is a complete\nanswer \u2014 and `409` stays reserved for a stage that never ran or a body that could\nnot be built.\n\n**The alias is resolved at the LIVE generation**, necessarily: only an artifact can\nfreeze one. So a rotated binding previews against the current endpoint, and the\ncapability gate and environment policy apply exactly as they do everywhere else.",
        "operationId": "preview_api_projects__project_id__specs_preview_post",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PreviewRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PreviewView"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Preview",
        "tags": [
          "specs"
        ]
      }
    },
    "/api/projects/{project_id}/specs/validate": {
      "post": {
        "description": "`agent.specs.validate` over the artifact's own body.",
        "operationId": "validate_api_projects__project_id__specs_validate_post",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GovernRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StageView"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Validate",
        "tags": [
          "specs"
        ]
      }
    },
    "/api/projects/{project_id}/test-runs": {
      "post": {
        "description": "Start an ad-hoc dev/local test run of an artifact's governed spec\n(`agent.specs.run`, `async_mode: true`).\n\nReturns `202` with a `Location` naming the new run and a `StudioTestRun` whose\nstatus is `started`. The run's platform lifecycle is then read through S3.",
        "operationId": "start_test_run_api_projects__project_id__test_runs_post",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TestRunCreateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StudioTestRun"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Start Test Run",
        "tags": [
          "test-runs"
        ]
      }
    },
    "/api/projects/{project_id}/test-runs/{run_id}": {
      "get": {
        "description": "One test run \u2014 Studio's dispatch status, plus the platform's view when it\nhas one.\n\n``{run_id}`` is the **Studio** id. Resolution is project-scoped and answers\n``404 test_run_not_found`` identically for a missing id and for one that belongs\nto another project (S2). The route performs **no local write and no audit**.",
        "operationId": "read_test_run_api_projects__project_id__test_runs__run_id__get",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "run_id",
            "required": true,
            "schema": {
              "title": "Run Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StudioTestRun"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Read Test Run",
        "tags": [
          "test-runs"
        ]
      }
    },
    "/api/projects/{project_id}/test-runs/{run_id}/cancel": {
      "post": {
        "description": "Ask the platform to stop this run.\n\n**Editor-only**, like starting one: it changes what is executing and what it\ncosts. The only write in the debug loop \u2014 `resume` and `replay` are deferred\nwith their reason in ADR-007's amendment.\n\n**The server's semantics are preserved exactly as they are.** Cancellation\nis *cooperative*: the platform records the request and the run stops when it\nnext reaches a point where it can. So the wording is \"requested\", and it\nnever promises that completed effects are reversed \u2014 a tool call that\nalready ran, already ran.\n\n**A terminal run is a successful no-op, not an error and not silence**\n(review round 3). The platform answers `200` with the run's *current*\nstatus, and this route reports that returned outcome rather than announcing\na cancellation because HTTP said 200. Three different things that a naive\nread would collapse into one:\n\n* the run was still going and cancellation is now requested;\n* the run had already finished, and nothing needed cancelling;\n* the run was already `cancelled`, which keeps the cooperative wording\n  because that is what it means.\n\n**The returned status wins over what the caller thought.** The server's\ncompare-and-set can hand back a terminal result a concurrent worker won, so\na UI that had rendered \"running\" a moment ago must consume the answer rather\nthan its own stale belief.",
        "operationId": "stop_test_run_api_projects__project_id__test_runs__run_id__cancel_post",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "run_id",
            "required": true,
            "schema": {
              "title": "Run Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StopResult"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Stop Test Run",
        "tags": [
          "test-runs"
        ]
      }
    },
    "/api/projects/{project_id}/test-runs/{run_id}/checkpoints": {
      "get": {
        "description": "The run's checkpoints, in sequence order.\n\nSorted by `sequence` ascending because the strip reads left to right and a\ncheckpoint's meaning is its position in the run. The platform's default is\nthe same; it is stated here so a change upstream does not silently reverse a\nstrip nobody re-reads.",
        "operationId": "read_run_checkpoints_api_projects__project_id__test_runs__run_id__checkpoints_get",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "run_id",
            "required": true,
            "schema": {
              "title": "Run Id",
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "title": "Offset",
              "type": "integer"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 100,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "in": "query",
            "name": "type",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Type"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckpointPage"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Read Run Checkpoints",
        "tags": [
          "test-runs"
        ]
      }
    },
    "/api/projects/{project_id}/test-runs/{run_id}/checkpoints/diff": {
      "get": {
        "description": "What changed between two checkpoints of this run.\n\nBoth bounds are the platform's to interpret, including their absence \u2014 this\nroute does not invent a default pair, because \"the last two\" is a guess and\nthe strip already knows which two the reader selected.",
        "operationId": "read_checkpoint_diff_api_projects__project_id__test_runs__run_id__checkpoints_diff_get",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "run_id",
            "required": true,
            "schema": {
              "title": "Run Id",
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "from_sequence",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "integer"
                },
                {
                  "type": "null"
                }
              ],
              "title": "From Sequence"
            }
          },
          {
            "in": "query",
            "name": "to_sequence",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "integer"
                },
                {
                  "type": "null"
                }
              ],
              "title": "To Sequence"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckpointDiff"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Read Checkpoint Diff",
        "tags": [
          "test-runs"
        ]
      }
    },
    "/api/projects/{project_id}/test-runs/{run_id}/graph": {
      "get": {
        "description": "The run's graph, as the platform executed it.\n\nThe canvas animates `current` and `visited` over **this** projection \u2014 the\nsame one the editor draws (ADR-004's projection rule is not revisited here).\nNodes and edges are passed through as the platform sent them rather than\nre-derived, because a picture Studio computed would be a second source that\ncan disagree with the run it claims to describe.",
        "operationId": "read_run_graph_api_projects__project_id__test_runs__run_id__graph_get",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "run_id",
            "required": true,
            "schema": {
              "title": "Run Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RunGraph"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Read Run Graph",
        "tags": [
          "test-runs"
        ]
      }
    },
    "/api/projects/{project_id}/test-runs/{run_id}/trace": {
      "get": {
        "description": "One page of the run's trace, with what it takes to read it honestly.\n\n**Pages, never `tail_trace`.** The follower yields events only, dropping the\n`complete`, `dropped` and `window` fields each page carries \u2014 so three events\nwith seven lost is indistinguishable from three complete. `TraceWindow`\ncarries those through, along with whether the offset ceiling has been\nreached and whether the run is terminal as against the stream having merely\nstopped.\n\nThe run's status is read alongside so those last two can be told apart. It\ncosts a second call and buys the difference between \"the run ended\" and \"we\nstopped following\", which look identical from a list that has stopped\ngrowing.",
        "operationId": "read_run_trace_api_projects__project_id__test_runs__run_id__trace_get",
        "parameters": [
          {
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "run_id",
            "required": true,
            "schema": {
              "title": "Run Id",
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "title": "Offset",
              "type": "integer"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 100,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "in": "query",
            "name": "event_type",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Event Type"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TraceWindow"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Read Run Trace",
        "tags": [
          "test-runs"
        ]
      }
    },
    "/api/readyz": {
      "get": {
        "description": "Answer whether this instance can serve.\n\n`503` when the store cannot be reached, so an orchestrator reads the status\nline rather than parsing the body \u2014 the body is for a human looking at why.",
        "operationId": "readyz_api_readyz_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Readiness"
                }
              }
            },
            "description": "Successful Response"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Readiness"
                }
              }
            },
            "description": "Degraded: the store did not answer. The body is `Readiness` (`status: degraded`, `store: false`), **not** an error envelope."
          }
        },
        "summary": "Readyz",
        "tags": [
          "ops"
        ]
      }
    },
    "/api/session": {
      "delete": {
        "description": "The row goes, not just the cookie.\n\nClearing the cookie alone would leave a live session id in every log and\nproxy cache that saw it, redeemable by anyone who kept a copy \u2014 which is the\nwhole reason sessions are rows rather than self-contained tokens.",
        "operationId": "logout_api_session_delete",
        "responses": {
          "204": {
            "description": "Successful Response"
          }
        },
        "summary": "Logout",
        "tags": [
          "identity"
        ]
      },
      "post": {
        "operationId": "login_api_session_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LoginRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Me"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Login",
        "tags": [
          "identity"
        ]
      }
    },
    "/api/session/oidc/callback": {
      "get": {
        "description": "Consume the attempt, exchange the code, verify the token, issue a session.\n\n**Every failure is a redirect carrying an opaque code** \u2014 `invalid_state`,\n`expired_attempt`, `provider_error`, `not_admitted`,\n`login_capacity_exhausted` \u2014 never the provider's raw error text and never\nthe `state` value. This is a browser navigation, so anything put here lands\nin history, proxy logs and the `Referer` header of whatever the login page\nloads next.\n\n**The token verification runs off the event loop**, in a worker thread under\nthe same concurrency bound as the password verify; `login_capacity_exhausted`\nis the answer when no slot frees within the bounded wait.",
        "operationId": "oidc_callback_api_session_oidc_callback_get",
        "parameters": [
          {
            "in": "query",
            "name": "code",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Code"
            }
          },
          {
            "in": "query",
            "name": "state",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "State"
            }
          }
        ],
        "responses": {
          "302": {
            "description": "Always. On success to the provider (start) or to `return_to` (callback); on failure to the login page carrying an opaque code. There is no JSON success branch."
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Oidc Callback",
        "tags": [
          "identity"
        ]
      }
    },
    "/api/session/oidc/start": {
      "get": {
        "description": "Mint an attempt, then redirect to the provider.\n\nThe `redirect_uri` comes from `callback_origin`, never from this request's\n`Host`: one derived from a header is one an attacker can choose, and it is\nthe value the token exchange has to repeat.\n\n`return_to` is accepted only when path-absolute and same-origin, and ignored\n**silently** otherwise \u2014 echoing a rejected value into an error is itself a\nreflection worth having.",
        "operationId": "oidc_start_api_session_oidc_start_get",
        "parameters": [
          {
            "in": "query",
            "name": "return_to",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Return To"
            }
          }
        ],
        "responses": {
          "302": {
            "description": "Always. On success to the provider (start) or to `return_to` (callback); on failure to the login page carrying an opaque code. There is no JSON success branch."
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            },
            "description": "Refused. `request_invalid` when the body or a parameter did not match the endpoint's schema, or a field it admits is one the route refuses, with `details.errors` carrying a per-field list (offending input removed); an operation may also refuse with its own code and no `details`."
          }
        },
        "summary": "Oidc Start",
        "tags": [
          "identity"
        ]
      }
    }
  }
}
