{
  "openapi": "3.1.0",
  "info": {
    "title": "Fichers TopoMetrika — native web module",
    "version": "1.0.0",
    "description": "Контракты реализованного bounded native web-модуля на реляционной БД, schema33. Автонастройка SDK и проверки релиза доступны только администраторам пространства. Яндекс Метрика/AppMetrica сохраняются как отдельные источники. Это не поставка enterprise Rust/Kafka/ClickHouse/Qdrant/mobile и не заявление SLA/обученной семантики. Admin paths используют /api/v1; collector PathItem переопределяет server на корень origin."
  },
  "servers": [
    {
      "url": "https://{workspace}.fichers.ru/api/v1",
      "variables": {
        "workspace": {
          "default": "demo"
        }
      }
    }
  ],
  "security": [
    {
      "sessionCookie": []
    }
  ],
  "tags": [
    {
      "name": "TopoMetrika",
      "description": "Управление и чтение по сессии пространства."
    },
    {
      "name": "TopoMetrika collector",
      "description": "Публичный SDK ingestion по ID счётчика с проверкой домена, пакетов и лимитов. Владелец сайта обеспечивает правовое основание и управляет моментом подключения скрипта; не финансовый источник истины и не подтверждение подлинности визита."
    }
  ],
  "paths": {
    "/topometrika/projects/{id}/discovery": {
      "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Проект текущего пространства; tenant определяется серверной сессией." }],
      "get": {
        "operationId": "topometrikaDiscoveryState",
        "tags": ["TopoMetrika"],
        "security": [{ "sessionCookie": [] }],
        "summary": "Администратор: карта одобренных действий и история проверок релизов",
        "description": "Только ORGANIZATION_ADMIN/PLATFORM_ADMIN текущего пространства. Возвращает накопленную карту явно одобренных действий и до 20 сводок проверок; ручные отзывы разрешений учитываются. Отсутствие действия в пройденных сценариях не удаляет его из политики. private, no-store; CORS и публичного discovery endpoint нет. До 60 чтений/мин на пользователя пространства.",
        "responses": {
          "200": { "description": "Текущая карта либо null до первого подтверждения.", "headers": { "Cache-Control": { "schema": { "type": "string", "const": "private, no-store" } } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DiscoveryState" } } } },
          "401": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" },
          "428": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" },
          "503": { "$ref": "#/components/responses/Error" }
        }
      },
      "post": {
        "operationId": "topometrikaConfirmDiscovery",
        "tags": ["TopoMetrika"],
        "security": [{ "sessionCookie": [] }],
        "summary": "Администратор: пакетно подтвердить обнаруженные действия и проверку релиза",
        "description": "Same-workspace сессия администратора и CSRF Origin обязательны. observed содержит только явно выбранные строки; approvedIds должен точно совпадать с их уникальными id. Для невыбранных предложений передаются только dismissedIds, без подписей и маршрутов. Общая сумма observed.length + dismissedIds.length <= 128; наборы непересекаются. Идентификаторы и безопасная форма каждого кандидата проверяются повторно на сервере. expectedVersion должен совпадать с project.version. Одной транзакцией дополняет политику, сохраняет накопленную карту, сводку релиза, аудит и receipt; версия увеличивается. Невидимые в сценарии действия не отзываются автоматически. Только явно одобренные подписи/действия сохраняются в карте; аудит и receipt не содержат текст предложений. Лимиты после слияния: 256 подписей, 128 действий, 384 записи карты. История ограничена 20 проверками. Повтор того же тела и Idempotency-Key идемпотентен. Короткий установочный блок остаётся прежним; новую политику действующая вкладка получает после перезагрузки. До 60 мутаций/мин на пользователя и 180 на пространство. Публичного discovery endpoint нет.",
        "parameters": [{ "$ref": "#/components/parameters/IdempotencyKey" }, { "$ref": "#/components/parameters/MutationOrigin" }],
        "requestBody": { "required": true, "x-max-body-bytes": 65536, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DiscoveryConfirmationInput" } } } },
        "responses": {
          "200": { "description": "Проект, актуальная карта и история. При повторе возможно idempotent:true; ключей и секретов нет.", "headers": { "Cache-Control": { "schema": { "type": "string", "const": "private, no-store" } } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DiscoveryConfirmation" } } } },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" },
          "403": { "description": "Нет прав настройки, чужое пространство или CSRF Origin отклонён.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "404": { "$ref": "#/components/responses/Error" },
          "409": { "description": "VERSION_CONFLICT при изменении версии/revision либо IDEMPOTENCY_CONFLICT при другом запросе с тем же ключом. Частичных изменений нет.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "413": { "$ref": "#/components/responses/Error" },
          "415": { "$ref": "#/components/responses/Error" },
          "422": { "description": "Некорректный кандидат/набор выбранных строк/Idempotency-Key либо DISCOVERY_CAPACITY. Политика и карта не изменяются.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "428": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" },
          "503": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/topometrika/projects/{id}/installation-status": {"get":{"tags":["TopoMetrika"],"summary":"Проверка подключения и обработки нового действия","operationId":"topometrika_installation_status","description":"Администратор текущего пространства. Без since возвращает checkedAt как начало проверки; дальше передавайте это время (не старше 10 минут). Конфигурация не является обработанным событием. 60 запросов/мин на пользователя пространства; private,no-store.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"since","in":"query","schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"Проверенный Origin, без URL/IP. Время обработки определяется по новым принятым событиям, не по повторным UUID.","content":{"application/json":{"schema":{"type":"object","required":["projectId","checkedAt","enabled","origins"],"additionalProperties":false,"properties":{"projectId":{"type":"string"},"checkedAt":{"type":"integer"},"enabled":{"type":"boolean"},"origins":{"type":"array","maxItems":16,"items":{"type":"object","additionalProperties":false,"required":["origin","configurationAt","processedAt"],"properties":{"origin":{"type":"string"},"configurationAt":{"type":["integer","null"]},"processedAt":{"type":["integer","null"]}}}}}}}}},"401":{"description":"Нужна сессия"},"403":{"description":"Нет прав настройки"},"404":{"description":"Проект не найден"},"422":{"description":"Недопустимый период"},"429":{"description":"Лимит запросов"}}}},
    "/topometrika/projects": {
      "get": {
        "operationId": "topometrikaListProjects",
        "tags": [
          "TopoMetrika"
        ],
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "summary": "Проекты нативной аналитики текущего пространства",
        "description": "Все участники с действующей сессией. Только администраторам выдаются policy/model/retention/keyVersion и organizationId. Product Owner/участнику — id,name,enabled,version,createdAt. Ключей записи нет.",
        "responses": {
          "200": {
            "description": "Проекты и возможности текущей роли",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "private, no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProjectList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "428": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "post": {
        "operationId": "topometrikaCreateProject",
        "tags": [
          "TopoMetrika"
        ],
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "summary": "Администратор: создать выключенный по умолчанию проект",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/MutationOrigin"
          }
        ],
        "requestBody": {
          "required": true,
          "x-max-body-bytes": 32768,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProjectConfig"
              },
              "example": {
                "name": "Marketplace",
                "approvedOrigins": [
                  "https://marketplace.example.test"
                ],
                "approvedLabels": [
                  "Купить"
                ],
                "approvedActions": [
                  "POST /checkout"
                ],
                "enabled": false,
                "retentionDays": 30
              }
            }
          }
        },
        "description": "Только ORGANIZATION_ADMIN/PLATFORM_ADMIN. До 10 проектов. Атомарные receipt/audit/outbox. Ответ содержит только project; секреты, writeKey и keyUnavailable не выдаются. Идемпотентный повтор не создаёт второй проект. В браузер устанавливается публичный ID счётчика.",
        "responses": {
          "201": {
            "description": "Созданный проект без секретов и постоянных ключей браузерного доступа",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "private, no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProjectMutation"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          },
          "415": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "428": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/topometrika/projects/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "Идентификатор объекта текущего пространства."
        }
      ],
      "patch": {
        "operationId": "topometrikaUpdateProject",
        "tags": [
          "TopoMetrika"
        ],
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "summary": "Администратор: заменить policy и retention проекта",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/MutationOrigin"
          }
        ],
        "requestBody": {
          "required": true,
          "x-max-body-bytes": 32768,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProjectUpdate"
              }
            }
          }
        },
        "description": "Все поля ProjectConfig обязательны кроме enabled/retentionDays; это не произвольный partial PATCH. expectedVersion — project.version. Версия и revision увеличиваются атомарно. Уменьшение retention сокращает сроки старых payload; увеличение не продлевает старые обещанные сроки. enabled:false останавливает приём, но не удаляет историю.",
        "responses": {
          "200": {
            "description": "Текущий project без секретов и постоянных ключей браузерного доступа",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "private, no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProjectMutation"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          },
          "415": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "428": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/topometrika/projects/{id}/rotate-key": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "Идентификатор объекта текущего пространства."
        }
      ],
      "post": {
        "operationId": "topometrikaRotateProjectKey",
        "tags": [
          "TopoMetrika"
        ],
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "summary": "Администратор: заменить публичный ключ записи",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/MutationOrigin"
          }
        ],
        "requestBody": {
          "required": true,
          "x-max-body-bytes": 32768,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VersionInput"
              }
            }
          }
        },
        "description": "Совместимая серверная операция ротации legacy key hash: увеличивает project.version,revision,keyVersion. Идемпотентный повтор не повторяет операцию. Ответ содержит только project: серверные секреты, writeKey и keyUnavailable не выдаются. Публичный ID счётчика не меняется; браузерный сборщик не использует legacy ключи и продолжает работу по ID, текущим доменам и политике проекта.",
        "responses": {
          "200": {
            "description": "Проект с обновлённой версией отзыва, без секретов и постоянного браузерного ключа",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "private, no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProjectMutation"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          },
          "415": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "428": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/topometrika/dashboard": {
      "get": {
        "operationId": "topometrikaDashboard",
        "tags": [
          "TopoMetrika"
        ],
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "summary": "TopoPrint, наблюдаемые пути и пять описательных представлений",
        "parameters": [
          {
            "$ref": "#/components/parameters/ProjectId"
          },
          {
            "$ref": "#/components/parameters/Start"
          },
          {
            "$ref": "#/components/parameters/End"
          },
          {
            "name": "clusterId",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Ограничить state/layout сравнение выбранным видимым намерением."
          },
          {
            "name": "nextClusterId",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Определить, какое следующее наблюдаемое действие считается продолжением."
          },
          {
            "name": "threshold",
            "in": "query",
            "schema": {
              "type": "number",
              "minimum": 0.75,
              "maximum": 0.99,
              "default": 0.85
            },
            "description": "Порог исследовательской regrouping; не меняет официальные кластеры."
          }
        ],
        "description": "До 2000 сохранённых событий, точное множество сессий (не людей). Учитываются retention и history-floor тарифа. Участнику доступны только кластеры опубликованных фич: скрытые/непривязанные/draft события не раскрываются через счётчики, skeleton, переходы или journeys. iris и journeys не зависят от фильтров таблиц. journeys содержит до 12 точных непрерывных наблюдаемых последовательностей длиной 2–64 шага; более длинные сегменты исключаются целиком, а не обрезаются. Это последовательности внутри доступного периода, не полные бизнес-воронки. Только администратор получает storage. Ретро ограничено первыми 500 подходящими событиями. Не UMAP, A/B-тест, причинный эффект или бизнес-конверсия. outcomeClusterId — только состояние интерфейса; API не определяет и не публикует бизнес-результат.",
        "responses": {
          "200": {
            "description": "Обзор, iris/journeys, states/layouts/transitions/retro и методика",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "private, no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Dashboard"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "428": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/topometrika/features/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "Идентификатор объекта текущего пространства."
        }
      ],
      "get": {
        "operationId": "topometrikaFeatureAnalytics",
        "tags": [
          "TopoMetrika"
        ],
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "summary": "Read-only native-аналитика существующей фичи",
        "parameters": [
          {
            "$ref": "#/components/parameters/Start"
          },
          {
            "$ref": "#/components/parameters/End"
          }
        ],
        "description": "Только обработанные неистёкшие наблюдения связанных кластеров, до 10000 событий из проектов пространства; сессия уникальна по projectId+s_id. Участник не видит DRAFT. Нет записей в ценовые метрики, AMM или рейтинг.",
        "responses": {
          "200": {
            "description": "Наблюдения фичи и дневные точки",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "private, no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureAnalytics"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "428": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/topometrika/clusters/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "Идентификатор объекта текущего пространства."
        }
      ],
      "patch": {
        "operationId": "topometrikaRenameCluster",
        "tags": [
          "TopoMetrika"
        ],
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "summary": "Product Owner/администратор: назвать наблюдаемое намерение",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/MutationOrigin"
          }
        ],
        "requestBody": {
          "required": true,
          "x-max-body-bytes": 32768,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RenameInput"
              }
            }
          }
        },
        "description": "expectedVersion — текущий project.version, не event-count или cluster ID. Увеличивает project.version и revision, сохраняет alias/audit/receipt/outbox. После успеха заново получите список проектов перед следующей версионной мутацией. Не меняет embedding и прошлые события.",
        "responses": {
          "200": {
            "description": "clusterId,name; idempotent:true на повторе",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "private, no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RenameResult"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          },
          "415": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "428": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/topometrika/clusters/{id}/feature": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "Идентификатор объекта текущего пространства."
        }
      ],
      "post": {
        "operationId": "topometrikaLinkFeature",
        "tags": [
          "TopoMetrika"
        ],
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "summary": "Product Owner/администратор: связать или создать фичу-черновик",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/MutationOrigin"
          }
        ],
        "requestBody": {
          "required": true,
          "x-max-body-bytes": 32768,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FeatureLinkInput"
              },
              "example": {
                "projectId": "project-one",
                "name": "Покупка в один клик",
                "ticker": "BUYCLICK"
              }
            }
          }
        },
        "description": "Нужен активный кластер с сохранёнными наблюдениями. Уже связанному кластеру возвращается 409, связь не перезаписывается. Одна привязка на кластер; фича может поддерживаться несколькими кластерами. Участвует внутренняя optimistic revision-транзакция; expectedVersion в этом JSON нет. Прямой сбор/кластер не публикует фичу автоматически.",
        "responses": {
          "201": {
            "description": "Связь и created; тот же результат с idempotent:true на повторе",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "private, no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureLinkResult"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          },
          "415": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "428": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "delete": {
        "operationId": "topometrikaUnlinkFeature",
        "tags": [
          "TopoMetrika"
        ],
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "summary": "Product Owner/администратор: снять только ожидаемую связь",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/MutationOrigin"
          }
        ],
        "requestBody": {
          "required": true,
          "x-max-body-bytes": 32768,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FeatureUnlinkInput"
              }
            }
          }
        },
        "description": "expectedFeatureId обязателен: изменившаяся связь даёт 409 VERSION_CONFLICT. Фича и события не удаляются; рыночная история не меняется.",
        "responses": {
          "200": {
            "description": "Связь снята; idempotent:true на повторе",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "private, no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureUnlinkResult"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          },
          "415": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "428": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/worker/topometrika": {
      "post": {
        "operationId": "topometrikaRetentionTick",
        "tags": [
          "TopoMetrika"
        ],
        "security": [
          {
            "maintenanceBearer": []
          }
        ],
        "summary": "Внутренний bounded retention tick",
        "description": "MAINTENANCE_TOKEN, не публичный ключ SDK и не сессия пользователя. До 10 проектов с истёкшими payload/layout. Worker-loop вызывает раз в 60000 ms, timeout 50000 ms. Не удаляет receipt-ID tombstones/links/aliases; не начисляет цену.",
        "responses": {
          "200": {
            "description": "Число обработанных проектов",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "private, no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "processedProjects"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "processedProjects": {
                      "type": "integer",
                      "minimum": 0,
                      "maximum": 10
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/topometrika/v1/config": {
      "servers": [{ "url": "https://{workspace}.fichers.ru", "variables": { "workspace": { "default": "demo" } } }],
      "post": {
        "operationId": "topometrikaInstallationConfig",
        "tags": ["TopoMetrika installation"],
        "description": "Read only the current installation policy using a public counterId and exact approved target Origin, without cookies, keys or tokens. No analytics event is created. No workspace identity, alternate origins, key, reports or quotas are returned. Response is no-store. The site owner ensures the required legal basis and controls script loading through their own consent mechanism; TopoMetrika neither displays a consent banner nor stores or expires a consent decision. Origin can be spoofed outside a browser: this public policy must contain no secrets. Limits: 300 per project/minute and 6000 global/minute, with the global budget consumed before project lookup.",
        "security": [],
        "parameters": [{ "$ref": "#/components/parameters/CollectorOrigin" }],
        "requestBody": {
          "required": true,
          "x-max-body-bytes": 512,
          "content": { "application/json": { "schema": {
            "type": "object", "additionalProperties": false, "required": ["v", "counterId"],
            "properties": { "v": { "const": 1 }, "counterId": { "type": "string", "pattern": "^[a-zA-Z0-9][a-zA-Z0-9_-]{2,63}$" } }
          }, "example": { "v": 1, "counterId": "project-one" } } }
        },
        "responses": {
          "200": {
            "description": "Current installation settings for this project and requesting approved origin only. Headers include Cache-Control: no-store and exact Origin CORS, never Access-Control-Allow-Credentials.",
            "content": { "application/json": { "schema": {
              "type": "object", "additionalProperties": false,
              "required": ["v", "projectId", "name", "enabled", "approvedOrigins", "approvedLabels", "approvedActions", "version"],
              "properties": {
                "v": { "const": 1 },
                "projectId": { "type": "string", "pattern": "^[a-zA-Z0-9][a-zA-Z0-9_-]{2,63}$" },
                "name": { "type": "string", "minLength": 1, "maxLength": 100 },
                "enabled": { "const": true },
                "approvedOrigins": { "type": "array", "minItems": 1, "maxItems": 1, "items": { "type": "string", "maxLength": 256 } },
                "approvedLabels": { "type": "array", "maxItems": 256, "items": { "type": "string", "minLength": 1, "maxLength": 72 } },
                "approvedActions": { "type": "array", "minItems": 1, "maxItems": 128, "items": { "type": "string", "minLength": 1, "maxLength": 160 }, "description": "Одобренные route templates и UI /tm/f<16hex>. UI-действие связано с канонической структурой без текстов, позиционным trigger и его одобренной подписью. UI_TRIGGER_ACTION остаётся общим сигналом без кластера." },
                "version": { "type": "integer", "minimum": 1 }
              }
            } } }
          },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "413": { "$ref": "#/components/responses/Error" },
          "415": { "$ref": "#/components/responses/Error" },
          "422": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" },
          "503": { "$ref": "#/components/responses/Error" }
        }
      },
      "options": {
        "operationId": "topometrikaInstallationConfigPreflight",
        "tags": ["TopoMetrika installation"], "security": [],
        "description": "Preflight permits POST with content-type only. Keys, tokens, Authorization and other requested headers are rejected. Preflight grants no project or workspace read access.",
        "parameters": [{ "$ref": "#/components/parameters/CollectorOrigin" }],
        "responses": { "204": { "description": "Exact-origin, credential-free POST preflight." }, "403": { "$ref": "#/components/responses/Error" } }
      }
    },
    "/api/topometrika/v1/pulse": {
      "servers": [
        {
          "url": "https://{workspace}.fichers.ru",
          "variables": {
            "workspace": {
              "default": "demo"
            }
          }
        }
      ],
      "post": {
        "operationId": "topometrikaSubmitPulse",
        "x-rate-limits": { "admissionGlobalPerMinute": 6000, "acceptedPerProjectPerMinute": 600, "acceptedGlobalPerMinute": 6000 },
        "x-admission-policy": "The fixed global admission bucket is charged before project lookup for a bounded request with a syntactically valid public counter ID, including unknown counters. It does not create attacker-selected per-counter/IP buckets. A separate project/global request budget is charged after enabled project and exact approved Origin checks, before protocol decoding and event storage.",
        "tags": [
          "TopoMetrika collector"
        ],
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/CollectorOrigin"
          }
        ],
        "summary": "Атомарно принять либо повторить один pulse",
        "description": "До 4096 UTF-8 bytes; UUIDv4 event_id и s_id. Только публичный p_id, enabled проект и exact approved Origin; без токенов, ключей и cookies. Владелец сайта обеспечивает правовое основание и управляет подключением скрипта через собственный механизм согласия. Legacy x-tm-project-key, x-tm-ingest-token и Authorization отклоняются. 202 означает durably stored PENDING плюс отсутствующий layout, не успешное использование фичи. Отправьте skeleton, затем повторите идентичный pulse с тем же event_id. 204 означает layoutKnown и durable accept/duplicate, а не публикацию фичи или подтверждённый бизнес-эффект. Canonical duplicate не увеличивает счётчики; другой payload того же event_id даёт 409 EVENT_CONFLICT. После удаления payload receipt-ID блокирует повтор как RETIRED_EVENT,409. Свежие ts проверяются также на повторе. Только JSON, Content-Encoding отсутствует/identity; read/admin API недоступны. Public ID и Origin не доказывают подлинность визита или транзакции: Origin может быть подделан вне браузера; per-project/global rate limits ограничивают, но не устраняют загрязнение данных.",
        "requestBody": {
          "required": true,
          "x-max-body-bytes": 4096,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Pulse"
              },
              "example": {
                "v": 1,
                "event_id": "12345678-1234-4123-8123-123456789abc",
                "p_id": "project-one",
                "s_id": "99999999-1234-4123-8123-123456789abc",
                "ts": 1792350000000,
                "l_hash": "50d6fc30f78591b3",
                "t_id": "n0.1",
                "act": "POST /checkout",
                "st": {
                  "n0.0": 4
                },
                "platform": "WEB_DOM"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Событие атомарно сохранено, требуется соответствующий skeleton; тело отсутствует.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Access-Control-Allow-Origin": {
                "schema": {
                  "type": "string"
                },
                "description": "Точный разрешённый Origin, не wildcard."
              },
              "Vary": {
                "schema": {
                  "type": "string",
                  "const": "Origin"
                }
              },
              "X-TM-Need-Skeleton": {
                "schema": {
                  "type": "string",
                  "pattern": "^[0-9a-f]{16}$"
                },
                "description": "Равен l_hash принятого pulse."
              }
            }
          },
          "204": {
            "description": "Событие сохранено/повторено; layout известен. Тело отсутствует.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Access-Control-Allow-Origin": {
                "schema": {
                  "type": "string"
                },
                "description": "Точный разрешённый Origin, не wildcard."
              },
              "Vary": {
                "schema": {
                  "type": "string",
                  "const": "Origin"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          },
          "415": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "options": {
        "operationId": "topometrikaPulsePreflight",
        "tags": [
          "TopoMetrika collector"
        ],
        "security": [],
        "summary": "CORS preflight без credentials/read доступа",
        "parameters": [
          {
            "$ref": "#/components/parameters/CollectorOrigin"
          },
          {
            "name": "Access-Control-Request-Method",
            "in": "header",
            "required": true,
            "schema": {
              "const": "POST"
            }
          },
          {
            "name": "Access-Control-Request-Headers",
            "in": "header",
            "schema": {
              "type": "string"
            },
            "description": "Только content-type; токены, ключи, Authorization и другие заголовки отклоняются."
          }
        ],
        "description": "Синтаксически допустимый origin получает preflight; это не авторизация проекта. Настоящий POST независимо проверяет enabled проект, публичный ID, exact approvedOrigin, политику пакета и лимиты. Access-Control-Allow-Credentials не выдаётся.",
        "responses": {
          "204": {
            "description": "Preflight разрешён",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Access-Control-Allow-Origin": {
                "schema": {
                  "type": "string"
                },
                "description": "Точный разрешённый Origin, не wildcard."
              },
              "Vary": {
                "schema": {
                  "type": "string",
                  "const": "Origin"
                }
              }
            }
          },
          "403": {
            "description": "Пустой ответ: недопустимый origin/method/requested headers"
          }
        }
      }
    },
    "/api/topometrika/v1/skeleton": {
      "servers": [
        {
          "url": "https://{workspace}.fichers.ru",
          "variables": {
            "workspace": {
              "default": "demo"
            }
          }
        }
      ],
      "post": {
        "operationId": "topometrikaSubmitSkeleton",
        "x-rate-limits": { "admissionGlobalPerMinute": 6000, "acceptedPerProjectPerMinute": 600, "acceptedGlobalPerMinute": 6000 },
        "x-admission-policy": "Shares the fixed global admission bucket with pulse before project lookup, including unknown public counters. No attacker-selected per-counter/IP buckets are created. Requests for enabled projects with exact approved Origin also consume the project/global budgets before decoding and event storage.",
        "tags": [
          "TopoMetrika collector"
        ],
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/CollectorOrigin"
          }
        ],
        "summary": "Принять проверенный канонический скелет",
        "description": "До 32768 UTF-8 bytes, 128 узлов, depth 12. Только публичный p_id, enabled проект и exact approved Origin; без ключей, токенов и cookies. Владелец сайта управляет правовым основанием и подключением скрипта. root n0/CONTAINER; точные positional children, approvedLabels, только container имеет детей. XXH64(JSON.stringify({v:\"tm-skeleton-v1\",skeleton:normalizedSkeleton})); canonical children:[] и порядок id,c_type,txt?,children важны. Верифицируется hash и сравниваются canonical bytes при коллизии. Обрабатывает сохранённые PENDING события; несовместимые target/state помечаются REJECTED и не становятся использованием. Повтор неизменного layout идемпотентен. 204 не означает, что всякая pending запись стала валидным результатом. Публичный сборщик не предоставляет read/admin доступ и не доказывает достоверность клиентского наблюдения; Origin может быть подделан вне браузера.",
        "requestBody": {
          "required": true,
          "x-max-body-bytes": 32768,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SkeletonPacket"
              },
              "example": {
                "v": 1,
                "p_id": "project-one",
                "l_hash": "50d6fc30f78591b3",
                "skeleton": {
                  "id": "n0",
                  "c_type": "CONTAINER",
                  "children": [
                    {
                      "id": "n0.0",
                      "c_type": "INPUT_ENUM",
                      "children": []
                    },
                    {
                      "id": "n0.1",
                      "c_type": "TRIGGER",
                      "txt": "Купить",
                      "children": []
                    }
                  ]
                }
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Скелет принят либо уже известен. Тело отсутствует.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Access-Control-Allow-Origin": {
                "schema": {
                  "type": "string"
                },
                "description": "Точный разрешённый Origin, не wildcard."
              },
              "Vary": {
                "schema": {
                  "type": "string",
                  "const": "Origin"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          },
          "415": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "options": {
        "operationId": "topometrikaSkeletonPreflight",
        "tags": [
          "TopoMetrika collector"
        ],
        "security": [],
        "summary": "CORS preflight без credentials/read доступа",
        "parameters": [
          {
            "$ref": "#/components/parameters/CollectorOrigin"
          },
          {
            "name": "Access-Control-Request-Method",
            "in": "header",
            "required": true,
            "schema": {
              "const": "POST"
            }
          },
          {
            "name": "Access-Control-Request-Headers",
            "in": "header",
            "schema": {
              "type": "string"
            },
            "description": "Только content-type; токены, ключи, Authorization и другие заголовки отклоняются."
          }
        ],
        "description": "Синтаксически допустимый origin получает preflight; это не авторизация проекта. Настоящий POST независимо проверяет enabled проект, публичный ID, exact approvedOrigin, политику пакета и лимиты. Access-Control-Allow-Credentials не выдаётся.",
        "responses": {
          "204": {
            "description": "Preflight разрешён",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              },
              "Access-Control-Allow-Origin": {
                "schema": {
                  "type": "string"
                },
                "description": "Точный разрешённый Origin, не wildcard."
              },
              "Vary": {
                "schema": {
                  "type": "string",
                  "const": "Origin"
                }
              }
            }
          },
          "403": {
            "description": "Пустой ответ: недопустимый origin/method/requested headers"
          }
        }
      }
    },
    "/topometrika/prints": {"get":{"operationId":"releasePrintList","tags":["TopoMetrika"],"summary":"List up to 1000 stored release passports for this tenant project","parameters":[{"name":"projectId","in":"query","required":true,"schema":{"type":"string","maxLength":64}}],"responses":{"200":{"description":"Success; no-store.","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"items":{"type":"array","maxItems":1000,"items":{"type":"object","description":"Private management summary with publicUrl and revokedAt; no source events."}}},"required":["items"]}}}},"400":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"401":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"402":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"403":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"404":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"409":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"413":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"415":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"422":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"429":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"503":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."}}},"post":{"operationId":"releasePrintCreate","tags":["TopoMetrika"],"summary":"Issue an immutable public release passport from a reviewed preview","description":"Tenant-cookie authentication. Publishing uses existing PRODUCT_OWNER/ORGANIZATION_ADMIN/PLATFORM_ADMIN roles, exact trusted workspace Origin, explicit publication confirmation, reviewed preview hash and UUID Idempotency-Key. Completed nonoverlapping UTC periods, >=100 receipt-backed processed assigned events each; incomplete retained history or >2000 rows is rejected. Observations do not establish causality or statistical significance.","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Origin","in":"header","required":true,"schema":{"type":"string","format":"uri"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"projectId":{"type":"string","maxLength":100,"minLength":1},"featureId":{"type":"string","maxLength":100},"title":{"type":"string","maxLength":160,"minLength":3},"summaryNote":{"type":"string","maxLength":280},"privacyMode":{"enum":["STRICT_NDA","NAMED_LISTED"],"default":"STRICT_NDA"},"mode":{"enum":["SINGLE","DIFF"]},"baselineStart":{"type":"string","format":"date"},"baselineEnd":{"type":"string","format":"date"},"targetStart":{"type":"string","format":"date"},"targetEnd":{"type":"string","format":"date"},"previewHash":{"type":"string","pattern":"^[a-f0-9]{64}$"},"publicationConfirmed":{"const":true}},"required":["projectId","title","mode","targetStart","targetEnd","previewHash","publicationConfirmed"]}}}},"responses":{"201":{"description":"Success; no-store.","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"print":{"$ref":"#/components/schemas/ReleasePrint"},"publicUrl":{"type":"string","format":"uri"},"shareText":{"type":"string","maxLength":2000}},"required":["print","publicUrl","shareText"]}}}},"400":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"401":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"402":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"403":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"404":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"409":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"413":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"415":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"422":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"429":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"503":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."}}}},
    "/topometrika/prints/preview": {"post":{"operationId":"releasePrintPreview","tags":["TopoMetrika"],"summary":"Compute a sanitized passport preview without publishing","description":"Tenant-cookie authentication. Publishing uses existing PRODUCT_OWNER/ORGANIZATION_ADMIN/PLATFORM_ADMIN roles, exact trusted workspace Origin, explicit publication confirmation, reviewed preview hash and UUID Idempotency-Key. Completed nonoverlapping UTC periods, >=100 receipt-backed processed assigned events each; incomplete retained history or >2000 rows is rejected. Observations do not establish causality or statistical significance.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"projectId":{"type":"string","maxLength":100,"minLength":1},"featureId":{"type":"string","maxLength":100},"title":{"type":"string","maxLength":160,"minLength":3},"summaryNote":{"type":"string","maxLength":280},"privacyMode":{"enum":["STRICT_NDA","NAMED_LISTED"],"default":"STRICT_NDA"},"mode":{"enum":["SINGLE","DIFF"]},"baselineStart":{"type":"string","format":"date"},"baselineEnd":{"type":"string","format":"date"},"targetStart":{"type":"string","format":"date"},"targetEnd":{"type":"string","format":"date"}},"required":["projectId","title","mode","targetStart","targetEnd"]}}}},"responses":{"200":{"description":"Success; no-store.","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"print":{"$ref":"#/components/schemas/ReleasePrint"},"previewHash":{"type":"string","pattern":"^[a-f0-9]{64}$"}},"required":["print","previewHash"]}}}},"400":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"401":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"402":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"403":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"404":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"409":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"413":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"415":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"422":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"429":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"503":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."}}}},
    "/topometrika/prints/{id}": {"delete":{"operationId":"releasePrintRevoke","tags":["TopoMetrika"],"summary":"Creator or tenant admin revokes future page/API/export reads immediately","description":"Requires exact workspace Origin and current tenant membership. Creator can revoke after publisher-role demotion. Remains available after billing expiry. Previously downloaded/social-cached files cannot be recalled.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^TM-R2-[A-F0-9]{16}$"}}],"responses":{"200":{"description":"Success; no-store.","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"id":{"type":"string","pattern":"^TM-R2-[A-F0-9]{16}$"},"revokedAt":{"type":"integer"}},"required":["id","revokedAt"]}}}},"400":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"401":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"402":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"403":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"404":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"409":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"413":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"415":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"422":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"429":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"503":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."}}}},
    "/api/public/prints/{id}": {"servers":[{"url":"https://fichers.ru","description":"Apex public passport; never a workspace address"}],"get":{"operationId":"releasePrintPublicRead","tags":["TopoMetrika"],"security":[],"summary":"Read one integrity-checked immutable public snapshot","description":"Apex only, no-store, rate-limited. Reads only stored sanitized passport, never live source events/projects. No tenant/user/session/source feature IDs, action templates, URLs or DRAFT feature names. SHA-256 checks integrity, not source truth, UX quality or NDA compliance. Revoked/missing passport returns404.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^TM-R2-[A-F0-9]{16}$"}}],"responses":{"200":{"description":"Success; no-store.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReleasePrint"}}}},"400":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"401":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"402":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"403":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"404":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"409":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"413":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"415":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"422":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"429":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"503":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."}}},"head":{"operationId":"releasePrintPublicReadHead","tags":["TopoMetrika"],"security":[],"summary":"Same availability/headers without body","description":"Apex only, no-store, rate-limited. Reads only stored sanitized passport, never live source events/projects. No tenant/user/session/source feature IDs, action templates, URLs or DRAFT feature names. SHA-256 checks integrity, not source truth, UX quality or NDA compliance. Revoked/missing passport returns404.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^TM-R2-[A-F0-9]{16}$"}}],"responses":{"200":{"description":"Success; no-store.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReleasePrint"}}}},"400":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"401":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"402":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"403":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"404":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"409":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"413":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"415":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"422":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"429":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"503":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."}}}},
    "/api/public/prints/{id}/og.svg": {"servers":[{"url":"https://fichers.ru","description":"Apex public passport; never a workspace address"}],"get":{"operationId":"releasePrintPublicSvg","tags":["TopoMetrika"],"security":[],"summary":"Render sanitized social card (1200x630 or 1080x1350)","description":"Apex only, no-store, rate-limited. Reads only stored sanitized passport, never live source events/projects. No tenant/user/session/source feature IDs, action templates, URLs or DRAFT feature names. SHA-256 checks integrity, not source truth, UX quality or NDA compliance. Revoked/missing passport returns404.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^TM-R2-[A-F0-9]{16}$"}},{"name":"format","in":"query","schema":{"enum":["landscape","portrait"],"default":"landscape"}},{"name":"download","in":"query","schema":{"const":"1"}}],"responses":{"200":{"description":"Self-contained social image; no-store.","content":{"image/svg+xml":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"401":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"402":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"403":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"404":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"409":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"413":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"415":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"422":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"429":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"503":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."}}},"head":{"operationId":"releasePrintPublicSvgHead","tags":["TopoMetrika"],"security":[],"summary":"Same availability/headers without body","description":"Apex only, no-store, rate-limited. Reads only stored sanitized passport, never live source events/projects. No tenant/user/session/source feature IDs, action templates, URLs or DRAFT feature names. SHA-256 checks integrity, not source truth, UX quality or NDA compliance. Revoked/missing passport returns404.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^TM-R2-[A-F0-9]{16}$"}},{"name":"format","in":"query","schema":{"enum":["landscape","portrait"],"default":"landscape"}},{"name":"download","in":"query","schema":{"const":"1"}}],"responses":{"200":{"description":"Self-contained social image; no-store.","content":{"image/svg+xml":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"401":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"402":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"403":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"404":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"409":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"413":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"415":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"422":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"429":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"503":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."}}}},
    "/api/public/prints/{id}/og.png": {"servers":[{"url":"https://fichers.ru","description":"Apex public passport; never a workspace address"}],"get":{"operationId":"releasePrintPublicPng","tags":["TopoMetrika"],"security":[],"summary":"Render sanitized social card (1200x630 or 1080x1350)","description":"Apex only, no-store, rate-limited. Reads only stored sanitized passport, never live source events/projects. No tenant/user/session/source feature IDs, action templates, URLs or DRAFT feature names. SHA-256 checks integrity, not source truth, UX quality or NDA compliance. Revoked/missing passport returns404.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^TM-R2-[A-F0-9]{16}$"}},{"name":"format","in":"query","schema":{"enum":["landscape","portrait"],"default":"landscape"}},{"name":"download","in":"query","schema":{"const":"1"}}],"responses":{"200":{"description":"Self-contained social image; no-store.","content":{"image/png":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"401":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"402":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"403":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"404":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"409":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"413":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"415":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"422":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"429":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"503":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."}}},"head":{"operationId":"releasePrintPublicPngHead","tags":["TopoMetrika"],"security":[],"summary":"Same availability/headers without body","description":"Apex only, no-store, rate-limited. Reads only stored sanitized passport, never live source events/projects. No tenant/user/session/source feature IDs, action templates, URLs or DRAFT feature names. SHA-256 checks integrity, not source truth, UX quality or NDA compliance. Revoked/missing passport returns404.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^TM-R2-[A-F0-9]{16}$"}},{"name":"format","in":"query","schema":{"enum":["landscape","portrait"],"default":"landscape"}},{"name":"download","in":"query","schema":{"const":"1"}}],"responses":{"200":{"description":"Self-contained social image; no-store.","content":{"image/png":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"401":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"402":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"403":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"404":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"409":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"413":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"415":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"422":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"429":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."},"503":{"description":"Safe error with code/detail; no-store. 409 requires a fresh reviewed preview or a new idempotency key."}}}}
  },
  "components": {
    "securitySchemes": {
      "sessionCookie": {
        "type": "apiKey",
        "in": "cookie",
        "name": "__Host-featurers_session"
      },
      "maintenanceBearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "MAINTENANCE_TOKEN; minimum32 chars; only internal worker."
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_-]{8,100}$"
        },
        "description": "Сфера ключа — organization + actor, общая для native-мутаций; тот же ключ с другим запросом даёт 409. Не помещать ключ в JSON."
      },
      "MutationOrigin": {
        "name": "Origin",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uri"
        },
        "description": "Same workspace Origin; браузер добавляет автоматически. Tenant определяется серверной сессией/доверенным Host, не body."
      },
      "CollectorOrigin": {
        "name": "Origin",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uri"
        },
        "description": "Точный origin из approvedOrigins проекта; браузер добавляет автоматически. Cookies не требуются и не разрешаются через CORS. Не является доказательством подлинности non-browser клиента: заголовок может быть подделан вне браузера."
      },
      "ProjectId": {
        "name": "projectId",
        "in": "query",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "Проект текущего пространства."
      },
      "Start": {
        "name": "start",
        "in": "query",
        "schema": {
          "type": "string",
          "format": "date"
        },
        "description": "UTC, включительно; default end−6 дней. Не более 90 календарных дней в API-запросе."
      },
      "End": {
        "name": "end",
        "in": "query",
        "schema": {
          "type": "string",
          "format": "date"
        },
        "description": "UTC, включительно; default текущий день, который может быть неполным."
      }
    },
    "responses": {
      "Error": {
        "description": "Ошибка; code описывает причину. Origin/auth могут вернуть application/problem+json. Ни ключи, ни body SDK не записываются в сообщение ошибки.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          },
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "DiscoveryCandidate": {
        "type": "object", "additionalProperties": false,
        "required": ["id", "label", "action", "kind", "count", "needsReview"],
        "description": "Только явно выбранное администратором предложение. id = xxhash64(action + newline + label); сервер проверяет идентификатор и допустимую форму. Значения полей, query, fragment, origin и сырые URL запрещены.",
        "properties": {
          "id": { "type": "string", "pattern": "^[a-f0-9]{16}$" },
          "label": { "type": "string", "minLength": 1, "maxLength": 72, "description": "Нормализованная и проверенная статическая подпись. Фильтр отклоняет очевидные адреса, URL, управляющие символы, длинные числовые значения и UUID; он не доказывает отсутствие персональных данных." },
          "action": { "type": "string", "maxLength": 160, "description": "Для kind:route — проверенный GET/POST/PUT/PATCH/DELETE/HEAD/OPTIONS/NAV /path/:id либо /path/{id}. Для kind:ui — только UI /tm/f и 16 lowercase hex; это идентичность конкретного канонического trigger, а не угаданный маршрут." },
          "kind": { "type": "string", "enum": ["route", "ui"] },
          "count": { "type": "integer", "minimum": 1, "maximum": 1000000 },
          "needsReview": { "type": "boolean", "description": "Предложенный маршрут требует проверки контекста; любой кандидат сохраняется только после явного выбора." }
        }
      },
      "DiscoveryRunSummary": {
        "type": "object", "additionalProperties": false,
        "required": ["id", "releaseName", "createdAt", "added", "observed", "notSeen"],
        "properties": {
          "id": { "type": "string", "format": "uuid" }, "releaseName": { "type": "string", "minLength": 1, "maxLength": 80 },
          "createdAt": { "type": "integer", "description": "Серверное Unix-время, миллисекунды." },
          "added": { "type": "integer", "minimum": 0, "maximum": 128 },
          "observed": { "type": "integer", "minimum": 0, "maximum": 128, "description": "Выбранные строки плюс идентификаторы невыбранных предложений." },
          "notSeen": { "type": "integer", "minimum": 0, "maximum": 384, "description": "Ранее одобренные действия, не встреченные в этой проверке; это не доказательство удаления из продукта." }
        }
      },
      "DiscoveryBaseline": {
        "type": "object", "additionalProperties": false,
        "required": ["id", "releaseName", "createdAt", "projectVersion", "items"],
        "properties": {
          "id": { "type": "string", "format": "uuid" }, "releaseName": { "type": "string", "minLength": 1, "maxLength": 80 },
          "createdAt": { "type": "integer" }, "projectVersion": { "type": "integer", "minimum": 1 },
          "items": { "type": "array", "maxItems": 384, "items": { "$ref": "#/components/schemas/DiscoveryCandidate" }, "description": "Накопленная карта явно одобренных действий, с учётом текущей ручной политики." },
          "dismissedIds": { "type": "array", "maxItems": 384, "uniqueItems": true, "items": { "type": "string", "pattern": "^[a-f0-9]{16}$" }, "description": "Только идентификаторы; невыбранные тексты и маршруты не хранятся." }
        }
      },
      "DiscoveryState": {
        "type": "object", "additionalProperties": false,
        "required": ["baseline", "history", "projectVersion"],
        "properties": {
          "baseline": { "oneOf": [{ "$ref": "#/components/schemas/DiscoveryBaseline" }, { "type": "null" }] },
          "history": { "type": "array", "maxItems": 20, "items": { "$ref": "#/components/schemas/DiscoveryRunSummary" } },
          "projectVersion": { "type": "integer", "minimum": 1 }
        }
      },
      "DiscoveryConfirmationInput": {
        "type": "object", "additionalProperties": false,
        "required": ["expectedVersion", "releaseName", "observed", "approvedIds", "dismissedIds"],
        "description": "observed — только выбранные предложения. Уникальные approvedIds точно равны их id. dismissedIds не пересекаются с approvedIds. Общая сумма observed и dismissedIds не превышает 128. Неодобренные тексты остаются в памяти браузера.",
        "properties": {
          "expectedVersion": { "type": "integer", "minimum": 1 },
          "releaseName": { "type": "string", "minLength": 1, "maxLength": 80, "description": "Название проверки после trim, без управляющих символов." },
          "observed": { "type": "array", "maxItems": 128, "items": { "$ref": "#/components/schemas/DiscoveryCandidate" } },
          "approvedIds": { "type": "array", "maxItems": 128, "uniqueItems": true, "items": { "type": "string", "pattern": "^[a-f0-9]{16}$" } },
          "dismissedIds": { "type": "array", "maxItems": 128, "uniqueItems": true, "items": { "type": "string", "pattern": "^[a-f0-9]{16}$" } }
        }
      },
      "DiscoveryConfirmation": {
        "type": "object", "additionalProperties": false,
        "required": ["project", "baseline", "history", "projectVersion"],
        "properties": {
          "project": { "$ref": "#/components/schemas/Project" },
          "baseline": { "$ref": "#/components/schemas/DiscoveryBaseline" },
          "history": { "type": "array", "maxItems": 20, "items": { "$ref": "#/components/schemas/DiscoveryRunSummary" } },
          "projectVersion": { "type": "integer", "minimum": 1 }, "idempotent": { "type": "boolean", "const": true }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "code"
        ],
        "additionalProperties": true,
        "properties": {
          "code": {
            "type": "string"
          },
          "status": {
            "type": "integer",
            "minimum": 400,
            "maximum": 599
          },
          "title": {
            "type": "string"
          },
          "detail": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "format": "uri"
          },
          "settingsUrl": {
            "type": "string"
          }
        }
      },
      "ProjectConfig": {
        "type": "object",
        "required": [
          "name",
          "approvedOrigins",
          "approvedLabels",
          "approvedActions"
        ],
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "description": "Непустое после trim название; управляющие символы запрещены."
          },
          "approvedOrigins": {
            "type": "array",
            "maxItems": 16,
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 256
            },
            "description": "Точное URL.origin без slash/path/query. HTTPS; HTTP только localhost, 127.0.0.1, [::1]. Дубликаты удаляются, порядок нормализуется."
          },
          "approvedLabels": {
            "type": "array",
            "maxItems": 256,
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 72
            },
            "description": "Только проверенные статические labels; управляющие символы запрещены. Это allowlist, не автоматическая PII-анонимизация."
          },
          "approvedActions": {
            "type": "array",
            "maxItems": 128,
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 160
            },
            "description": "Проверенные шаблоны METHOD /path/:id или METHOD /path/{id}; GET/POST/PUT/PATCH/DELETE/HEAD/OPTIONS/NAV. Одобренные UI /tm/f<16hex> идентифицируют конкретный канонический trigger с одобренной подписью и поддерживают кластеризацию. UI_TRIGGER_ACTION допустим, но не формирует намерение. Query/fragment/origin запрещены."
          },
          "enabled": {
            "type": "boolean",
            "default": false,
            "description": "true требует хотя бы одного origin и action."
          },
          "retentionDays": {
            "type": "integer",
            "minimum": 2,
            "maximum": 90,
            "default": 30
          }
        }
      },
      "ProjectUpdate": {
        "type": "object",
        "required": [
          "name",
          "approvedOrigins",
          "approvedLabels",
          "approvedActions",
          "expectedVersion"
        ],
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "description": "Непустое после trim название; управляющие символы запрещены."
          },
          "approvedOrigins": {
            "type": "array",
            "maxItems": 16,
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 256
            },
            "description": "Точное URL.origin без slash/path/query. HTTPS; HTTP только localhost, 127.0.0.1, [::1]. Дубликаты удаляются, порядок нормализуется."
          },
          "approvedLabels": {
            "type": "array",
            "maxItems": 256,
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 72
            },
            "description": "Только проверенные статические labels; управляющие символы запрещены. Это allowlist, не автоматическая PII-анонимизация."
          },
          "approvedActions": {
            "type": "array",
            "maxItems": 128,
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 160
            },
            "description": "Проверенные шаблоны METHOD /path/:id или METHOD /path/{id}; GET/POST/PUT/PATCH/DELETE/HEAD/OPTIONS/NAV. Одобренные UI /tm/f<16hex> идентифицируют конкретный канонический trigger с одобренной подписью и поддерживают кластеризацию. UI_TRIGGER_ACTION допустим, но не формирует намерение. Query/fragment/origin запрещены."
          },
          "enabled": {
            "type": "boolean",
            "default": false,
            "description": "true требует хотя бы одного origin и action."
          },
          "retentionDays": {
            "type": "integer",
            "minimum": 2,
            "maximum": 90,
            "default": 30
          },
          "expectedVersion": {
            "type": "integer",
            "minimum": 1
          }
        }
      },
      "VersionInput": {
        "type": "object",
        "required": [
          "expectedVersion"
        ],
        "additionalProperties": false,
        "properties": {
          "expectedVersion": {
            "type": "integer",
            "minimum": 1
          }
        }
      },
      "Project": {
        "type": "object",
        "required": [
          "id",
          "organizationId",
          "name",
          "enabled",
          "approvedOrigins",
          "approvedLabels",
          "approvedActions",
          "version",
          "revision",
          "keyVersion",
          "modelVersion",
          "retentionDays",
          "createdAt"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string",
            "minLength": 3,
            "maxLength": 64
          },
          "organizationId": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "enabled": {
            "type": "boolean"
          },
          "approvedOrigins": {
            "type": "array",
            "maxItems": 16,
            "items": {
              "type": "string"
            }
          },
          "approvedLabels": {
            "type": "array",
            "maxItems": 256,
            "items": {
              "type": "string",
              "maxLength": 72
            }
          },
          "approvedActions": {
            "type": "array",
            "maxItems": 128,
            "items": {
              "type": "string",
              "maxLength": 160
            }
          },
          "version": {
            "type": "integer",
            "minimum": 1
          },
          "revision": {
            "type": "integer",
            "minimum": 0
          },
          "keyVersion": {
            "type": "integer",
            "minimum": 1
          },
          "modelVersion": {
            "type": "string",
            "const": "topometrika-hash128-v1"
          },
          "retentionDays": {
            "type": "integer",
            "minimum": 2,
            "maximum": 90
          },
          "createdAt": {
            "type": "integer",
            "description": "Unix milliseconds"
          }
        }
      },
      "PublicProject": {
        "type": "object",
        "required": [
          "id",
          "name",
          "enabled",
          "version",
          "createdAt"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "enabled": {
            "type": "boolean"
          },
          "version": {
            "type": "integer",
            "minimum": 1
          },
          "createdAt": {
            "type": "integer"
          }
        }
      },
      "ProjectList": {
        "type": "object",
        "required": [
          "items",
          "canConfigure",
          "canManageFeatures"
        ],
        "additionalProperties": false,
        "properties": {
          "items": {
            "type": "array",
            "maxItems": 11,
            "items": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/Project"
                },
                {
                  "$ref": "#/components/schemas/PublicProject"
                }
              ]
            }
          },
          "canConfigure": {
            "type": "boolean"
          },
          "canManageFeatures": {
            "type": "boolean"
          }
        }
      },
      "ProjectMutation": {
        "type": "object",
        "required": [
          "project"
        ],
        "additionalProperties": false,
        "properties": {
          "project": {
            "$ref": "#/components/schemas/Project"
          }
        }
      },
      "Feature": {
        "type": "object",
        "required": [
          "id",
          "slug",
          "name",
          "listingStatus"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "listingStatus": {
            "type": "string"
          }
        }
      },
      "FeatureLinkInput": {
        "type": "object",
        "required": [
          "projectId"
        ],
        "additionalProperties": false,
        "properties": {
          "projectId": {
            "type": "string",
            "minLength": 3,
            "maxLength": 64
          },
          "featureId": {
            "type": "string",
            "maxLength": 100
          },
          "name": {
            "type": "string",
            "minLength": 2,
            "maxLength": 160
          },
          "ticker": {
            "type": "string",
            "pattern": "^[A-Za-z][A-Za-z0-9]{1,11}$"
          },
          "slug": {
            "type": "string",
            "maxLength": 80,
            "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
          },
          "description": {
            "type": "string",
            "minLength": 10,
            "maxLength": 10000
          },
          "ownerName": {
            "type": "string",
            "minLength": 2,
            "maxLength": 160
          }
        },
        "anyOf": [
          {
            "required": [
              "featureId"
            ],
            "properties": {
              "featureId": {
                "type": "string",
                "minLength": 1,
                "maxLength": 100
              }
            }
          },
          {
            "required": [
              "name",
              "ticker"
            ],
            "properties": {
              "featureId": {
                "type": "string",
                "maxLength": 0
              }
            }
          }
        ],
        "description": "Непустой featureId связывает существующую фичу пространства. Без него нужны name/ticker: создаётся только DRAFT/IDEA, slug по умолчанию ticker.toLowerCase(). Применяются тарифная квота и ёмкость каталога; публикации, финансового рынка и цены не создаёт."
      },
      "FeatureLinkResult": {
        "type": "object",
        "required": [
          "featureId",
          "slug",
          "name",
          "clusterId",
          "projectId",
          "created"
        ],
        "additionalProperties": false,
        "properties": {
          "featureId": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "clusterId": {
            "type": "string"
          },
          "projectId": {
            "type": "string"
          },
          "created": {
            "type": "boolean"
          },
          "idempotent": {
            "type": "boolean",
            "const": true
          }
        }
      },
      "FeatureUnlinkInput": {
        "type": "object",
        "required": [
          "projectId",
          "expectedFeatureId"
        ],
        "additionalProperties": false,
        "properties": {
          "projectId": {
            "type": "string",
            "minLength": 3,
            "maxLength": 64
          },
          "expectedFeatureId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          }
        }
      },
      "FeatureUnlinkResult": {
        "type": "object",
        "required": [
          "clusterId",
          "projectId",
          "unlinked"
        ],
        "additionalProperties": false,
        "properties": {
          "clusterId": {
            "type": "string"
          },
          "projectId": {
            "type": "string"
          },
          "unlinked": {
            "type": "boolean",
            "const": true
          },
          "idempotent": {
            "type": "boolean",
            "const": true
          }
        }
      },
      "RenameInput": {
        "type": "object",
        "required": [
          "projectId",
          "name",
          "expectedVersion"
        ],
        "additionalProperties": false,
        "properties": {
          "projectId": {
            "type": "string",
            "minLength": 3,
            "maxLength": 64
          },
          "name": {
            "type": "string",
            "minLength": 2,
            "maxLength": 160
          },
          "expectedVersion": {
            "type": "integer",
            "minimum": 1
          }
        }
      },
      "RenameResult": {
        "type": "object",
        "required": [
          "clusterId",
          "name"
        ],
        "additionalProperties": false,
        "properties": {
          "clusterId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "idempotent": {
            "type": "boolean",
            "const": true
          }
        }
      },
      "SkeletonNode": {
        "type": "object",
        "required": [
          "id",
          "c_type"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string",
            "maxLength": 64,
            "pattern": "^n0(?:\\.(?:0|[1-9]\\d{0,2})){0,12}$",
            "description": "Детерминированный positional path; дети n0.0, n0.1 и т.д., не DOM id/name."
          },
          "c_type": {
            "type": "string",
            "enum": [
              "CONTAINER",
              "STATIC_TEXT",
              "INPUT_ENUM",
              "INPUT_BOOL",
              "INPUT_NUM",
              "INPUT_TEXT",
              "TRIGGER"
            ]
          },
          "txt": {
            "type": "string",
            "maxLength": 72,
            "description": "Точное approvedLabels, только STATIC_TEXT/TRIGGER. Значения полей не передаются."
          },
          "children": {
            "type": "array",
            "maxItems": 128,
            "items": {
              "$ref": "#/components/schemas/SkeletonNode"
            }
          }
        },
        "allOf": [
          {
            "if": {
              "required": [
                "txt"
              ]
            },
            "then": {
              "properties": {
                "c_type": {
                  "enum": [
                    "STATIC_TEXT",
                    "TRIGGER"
                  ]
                }
              }
            }
          },
          {
            "if": {
              "required": [
                "c_type"
              ],
              "properties": {
                "c_type": {
                  "not": {
                    "const": "CONTAINER"
                  }
                }
              }
            },
            "then": {
              "properties": {
                "children": {
                  "maxItems": 0
                }
              }
            }
          }
        ]
      },
      "SkeletonPacket": {
        "type": "object",
        "required": [
          "v",
          "p_id",
          "l_hash",
          "skeleton"
        ],
        "additionalProperties": false,
        "properties": {
          "v": {
            "type": "integer",
            "const": 1
          },
          "p_id": {
            "type": "string",
            "pattern": "^[a-zA-Z0-9][a-zA-Z0-9_-]{2,63}$"
          },
          "l_hash": {
            "type": "string",
            "pattern": "^[0-9a-f]{16}$"
          },
          "skeleton": {
            "allOf": [
              {
                "$ref": "#/components/schemas/SkeletonNode"
              },
              {
                "properties": {
                  "id": {
                    "const": "n0"
                  },
                  "c_type": {
                    "const": "CONTAINER"
                  }
                }
              }
            ]
          }
        }
      },
      "Pulse": {
        "type": "object",
        "required": [
          "v",
          "event_id",
          "p_id",
          "s_id",
          "ts",
          "l_hash",
          "t_id",
          "act",
          "st",
          "platform"
        ],
        "additionalProperties": false,
        "properties": {
          "v": {
            "type": "integer",
            "const": 1
          },
          "event_id": {
            "type": "string",
            "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$",
            "description": "UUIDv4. Повтор того же event_id и канонического payload идемпотентен; другой payload даёт 409 EVENT_CONFLICT."
          },
          "p_id": {
            "type": "string",
            "pattern": "^[a-zA-Z0-9][a-zA-Z0-9_-]{2,63}$"
          },
          "s_id": {
            "type": "string",
            "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$",
            "description": "UUIDv4 сессии, не идентификатор человека."
          },
          "ts": {
            "type": "integer",
            "description": "Unix milliseconds: от now−86400000 до now+300000 при первом приёме."
          },
          "l_hash": {
            "type": "string",
            "pattern": "^[0-9a-f]{16}$"
          },
          "t_id": {
            "type": "string",
            "maxLength": 64,
            "pattern": "^n0(?:\\.(?:0|[1-9]\\d{0,2})){0,12}$",
            "description": "TRIGGER в соответствующем скелете."
          },
          "act": {
            "type": "string",
            "maxLength": 160,
            "description": "Точное значение approvedActions; без query, fragment, origin или CR/LF. Для UI /tm/f<16hex> сервер также проверяет связь с канонической структурой без подписей, позиционным TRIGGER и его разрешённой нормализованной подписью."
          },
          "st": {
            "type": "object",
            "maxProperties": 128,
            "propertyNames": {
              "maxLength": 64,
              "pattern": "^n0(?:\\.(?:0|[1-9]\\d{0,2})){0,12}$"
            },
            "additionalProperties": {
              "type": "integer",
              "enum": [
                0,
                1,
                2,
                3,
                4,
                5,
                6
              ]
            },
            "description": "Только positional input ID → код: 0 default,1 number modified,2 bool true,3 bool false,4 enum modified,5 text modified,6 empty. Код проверяется относительно типа input; значения/имена/хэши полей запрещены."
          },
          "platform": {
            "type": "string",
            "enum": [
              "WEB_REACT",
              "WEB_VUE",
              "WEB_DOM",
              "SDUI_MOBILE",
              "GENUI_DYNAMIC"
            ]
          },
          "dwell_ms": {
            "type": "integer",
            "minimum": 0,
            "maximum": 86400000,
            "description": "Необязательное явно измеренное время. Текущий Web SDK автоматически не измеряет."
          },
          "rage": {
            "type": "boolean",
            "description": "Необязательный явно измеренный признак. Отсутствие не является измеренным false; текущий Web SDK автоматически не измеряет."
          }
        }
      },
      "CanonicalSkeleton": {
        "type": "object",
        "required": [
          "v",
          "skeleton"
        ],
        "additionalProperties": false,
        "properties": {
          "v": {
            "type": "string",
            "const": "tm-skeleton-v1"
          },
          "skeleton": {
            "$ref": "#/components/schemas/SkeletonNode"
          }
        }
      },
      "Group": {
        "type": "object",
        "required": [
          "events",
          "sessions",
          "layouts",
          "platforms",
          "nextCount",
          "continuationRate",
          "avgDwellMs",
          "rageRate",
          "rageMeasuredEvents"
        ],
        "additionalProperties": true,
        "properties": {
          "events": {
            "type": "integer",
            "minimum": 0
          },
          "sessions": {
            "type": "integer",
            "minimum": 0
          },
          "layouts": {
            "type": "integer",
            "minimum": 0
          },
          "platforms": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "platform",
                "count"
              ],
              "additionalProperties": false,
              "properties": {
                "platform": {
                  "type": "string"
                },
                "count": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            }
          },
          "nextCount": {
            "type": "integer",
            "minimum": 0
          },
          "continuationRate": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1
          },
          "avgDwellMs": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0
          },
          "rageRate": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1,
            "description": "Доля true только среди явно измеренных rage, null при отсутствии измерений."
          },
          "rageMeasuredEvents": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "IntentSummary": {"description":"Фактические агрегаты одного намерения. Group содержит nextCount, continuationRate, avgDwellMs, rageRate и rageMeasuredEvents именно этого намерения, а не среднее по общим layout. Runtime возвращает эти ключи; null означает отсутствие измерения. Старые клиентские снимки могут не содержать дополнительных измерений — это также отсутствие данных, не ноль. UI Sludge fields describe the same action; missing legacy fields are unavailable, never inferred zero.","examples":[{"id":"checkout-intent","name":"Оформить заказ","feature":null,"events":10,"sessions":8,"layouts":2,"platforms":[{"platform":"WEB_DOM","count":10}],"nextCount":7,"continuationRate":0.7,"avgDwellMs":null,"rageRate":null,"rageMeasuredEvents":0}],"allOf":[{"$ref":"#/components/schemas/Group"},{"type":"object","required":["id","name","feature"],"additionalProperties":true,"properties":{"id":{"type":"string"},"name":{"type":"string"},"feature":{"anyOf":[{"$ref":"#/components/schemas/Feature"},{"type":"null"}]},"sludgeIndex":{"type":["number","null"],"minimum":0,"maximum":1},"rawSludgeIndex":{"type":["number","null"],"minimum":0,"maximum":1},"sludgeGrade":{"enum":["CLEAN","MODERATE_FRICTION","HIGH_SLUDGE","CRITICAL_SLUDGE",null]},"sludgeSecondsTotal":{"type":["number","null"],"minimum":0},"leontiefWeight":{"type":"number","minimum":0,"maximum":1},"directShare":{"type":"number","minimum":0,"maximum":1},"cascadeMultiplier":{"type":["number","null"],"minimum":0},"outgoingHhi":{"type":["number","null"],"minimum":0,"maximum":1},"effectiveNextSteps":{"type":["number","null"],"minimum":0},"observedOutgoingTransitions":{"type":"integer","minimum":0},"repeatShare":{"type":["number","null"],"minimum":0,"maximum":1},"systemicSludgeShare":{"type":["number","null"],"minimum":0,"maximum":1},"systemicContribution":{"type":["number","null"],"minimum":0,"maximum":1},"breakdown":{"type":"object","additionalProperties":false,"properties":{"emptyRatio":{"type":["number","null"],"minimum":0,"maximum":1},"loopRate":{"type":["number","null"],"minimum":0,"maximum":1},"rageRate":{"type":["number","null"],"minimum":0,"maximum":1},"dropRate":{"type":["number","null"],"minimum":0,"maximum":1},"dwellDrag":{"type":["number","null"],"minimum":0,"maximum":1}},"required":["emptyRatio","loopRate","rageRate","dropRate","dwellDrag"]},"weights":{"type":"object","additionalProperties":false,"properties":{"empty":{"type":"number","minimum":0,"maximum":1},"loop":{"type":"number","minimum":0,"maximum":1},"rage":{"type":"number","minimum":0,"maximum":1},"drop":{"type":"number","minimum":0,"maximum":1},"dwell":{"type":"number","minimum":0,"maximum":1}},"required":["empty","loop","rage","drop","dwell"]},"dominantFactor":{"enum":["emptyRatio","loopRate","rageRate","dropRate","dwellDrag",null]},"baselineSource":{"enum":["MEASURED_CLEAN_MEDIAN","FALLBACK_TRIGGER_8S","FALLBACK_FORM_12S","NOT_AVAILABLE"]},"cleanDwellSec":{"type":["number","null"],"minimum":0},"smoothed":{"type":"boolean"},"sampleQuality":{"enum":["READY","LOW_SAMPLE","NO_EVENTS"]},"coverage":{"type":"object","additionalProperties":false,"properties":{"sludge":{"type":"number","minimum":0,"maximum":1},"time":{"type":"number","minimum":0,"maximum":1},"dwell":{"type":"number","minimum":0,"maximum":1},"rage":{"type":"number","minimum":0,"maximum":1},"inputs":{"type":"number","minimum":0,"maximum":1}},"required":["sludge","time","dwell","rage","inputs"]},"bounds":{"type":"object","additionalProperties":false,"properties":{"sludge":{"type":"object","additionalProperties":false,"properties":{"lower":{"type":"number","minimum":0},"upper":{"type":["number","null"],"minimum":0}},"required":["lower","upper"]},"seconds":{"type":"object","additionalProperties":false,"properties":{"lower":{"type":["number","null"],"minimum":0},"upper":{"type":["number","null"],"minimum":0}},"required":["lower","upper"]}},"required":["sludge","seconds"]},"sludgeVersion":{"const":"ui-sludge-v1"},"sludgeCoverage":{"type":"object","additionalProperties":false,"properties":{"sludge":{"type":"number","minimum":0,"maximum":1},"time":{"type":"number","minimum":0,"maximum":1},"dwell":{"type":"number","minimum":0,"maximum":1},"rage":{"type":"number","minimum":0,"maximum":1},"inputs":{"type":"number","minimum":0,"maximum":1}},"required":["sludge","time","dwell","rage","inputs"]},"sludgeBaselineSource":{"enum":["MEASURED_CLEAN_MEDIAN","FALLBACK_TRIGGER_8S","FALLBACK_FORM_12S","NOT_AVAILABLE"]},"sludgeSampleQuality":{"enum":["READY","LOW_SAMPLE","NO_EVENTS"]},"sludgeSecondsPerSession":{"type":["number","null"],"minimum":0}}}]},
      "StateSummary": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Group"
          },
          {
            "type": "object",
            "required": [
              "clusterId",
              "signature",
              "emptyInputs",
              "modifiedCount"
            ],
            "additionalProperties": true,
            "properties": {
              "clusterId": {
                "type": "string"
              },
              "signature": {
                "type": "string"
              },
              "emptyInputs": {
                "type": "integer",
                "minimum": 0
              },
              "modifiedCount": {
                "type": "integer",
                "minimum": 0
              }
            }
          }
        ]
      },
      "LayoutSummary": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Group"
          },
          {
            "type": "object",
            "required": [
              "hash",
              "clusterIds",
              "skeleton"
            ],
            "additionalProperties": true,
            "properties": {
              "hash": {
                "type": "string"
              },
              "clusterIds": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "skeleton": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/CanonicalSkeleton"
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            }
          }
        ]
      },
      "IrisLayoutSummary": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Group"
          },
          {
            "type": "object",
            "required": [
              "hash",
              "clusterIds"
            ],
            "not": {
              "required": [
                "skeleton"
              ]
            },
            "properties": {
              "hash": {
                "type": "string"
              },
              "clusterIds": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          }
        ]
      },
      "Iris": {"type":"object","description":"Обзор продукта за тот же доступный период и из того же ограниченного набора событий, что и отчёт. Не зависит от clusterId/nextClusterId; сохраняет ограничения роли и арендатора. Структуры skeleton не передаются.","required":["intents","states","layouts","transitions","summary"],"additionalProperties":false,"properties":{"intents":{"type":"array","items":{"$ref":"#/components/schemas/IntentSummary"}},"states":{"type":"array","items":{"$ref":"#/components/schemas/StateSummary"}},"layouts":{"type":"array","items":{"$ref":"#/components/schemas/IrisLayoutSummary"}},"transitions":{"type":"array","items":{"$ref":"#/components/schemas/Transition"}},"summary":{"type":"object","required":["events","sessions","layouts","intents","pending","rejected","insufficientAction","candidates","sludgeVersion","topoScore","globalSludgeIndex","sludgeFreeRatio","totalSludgeHours","sludgeCoverage","sludgeBounds","sludgeObservedEvents","sludgeExcludedEvents"],"additionalProperties":false,"properties":{"events":{"type":"integer","minimum":0},"sessions":{"type":"integer","minimum":0},"layouts":{"type":"integer","minimum":0},"intents":{"type":"integer","minimum":0},"pending":{"type":"integer","minimum":0},"rejected":{"type":"integer","minimum":0},"insufficientAction":{"type":"integer","minimum":0},"candidates":{"type":"integer","minimum":0},"sludgeVersion":{"const":"ui-sludge-v1"},"topoScore":{"type":["integer","null"],"minimum":5,"maximum":99},"globalSludgeIndex":{"type":["number","null"],"minimum":0,"maximum":1},"sludgeFreeRatio":{"type":["number","null"],"minimum":0,"maximum":1},"totalSludgeHours":{"type":["number","null"],"minimum":0},"sludgeCoverage":{"type":"object","additionalProperties":false,"properties":{"sludge":{"type":"number","minimum":0,"maximum":1},"time":{"type":"number","minimum":0,"maximum":1}},"required":["sludge","time"]},"sludgeBounds":{"type":"object","additionalProperties":false,"properties":{"globalSludgeIndex":{"type":"object","additionalProperties":false,"properties":{"lower":{"type":"number","minimum":0,"maximum":1},"upper":{"type":"number","minimum":0,"maximum":1}},"required":["lower","upper"]},"totalSludgeHours":{"type":"object","additionalProperties":false,"properties":{"lower":{"type":"number","minimum":0},"upper":{"type":["number","null"],"minimum":0}},"required":["lower","upper"]}},"required":["globalSludgeIndex","totalSludgeHours"]},"sludgeObservedEvents":{"type":"integer","minimum":0},"sludgeExcludedEvents":{"type":"integer","minimum":0}}}}},
      "ObservedJourney": {
        "type": "object",
        "description": "Точная последовательность распознанных действий в непрерывном наблюдаемом сегменте одной сессии. Повторные шаги сохраняются. Это не склейка частых рёбер и не доказательство достижения бизнес-результата. Время — среднее фактических интервалов от первого до последнего события каждого сегмента.",
        "required": [
          "id",
          "clusterIds",
          "sessions",
          "occurrences",
          "avgDurationMs"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^tm-path-[0-9a-f]{16}$",
            "description": "Детерминированный некриптографический XXH64 от JSON массива clusterIds; не идентификатор пользователя или сессии."
          },
          "clusterIds": {
            "type": "array",
            "minItems": 2,
            "maxItems": 64,
            "items": {
              "type": "string"
            },
            "description": "Порядок наблюдений; один clusterId может повторяться."
          },
          "sessions": {
            "type": "integer",
            "minimum": 1,
            "description": "Число уникальных сессий с данной точной последовательностью. Не суммируется с другими путями для получения аудитории."
          },
          "occurrences": {
            "type": "integer",
            "minimum": 1,
            "description": "Число непрерывных сегментов с данной последовательностью; одна сессия может содержать несколько таких сегментов."
          },
          "avgDurationMs": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "description": "Среднее по сегментам от первого до последнего timestamp, не сумма средних времён отдельных рёбер."
          }
        },
        "examples": [
          {
            "id": "tm-path-64d11fe043fe38c7",
            "clusterIds": [
              "checkout-intent",
              "payment-intent"
            ],
            "sessions": 8,
            "occurrences": 10,
            "avgDurationMs": 4200
          }
        ]
      },
      "Journeys": {
        "type": "object",
        "description": "Каталог непрерывных последовательностей по всему доступному роли проекту за период, независимый от clusterId/nextClusterId таблиц. Неизвестные, скрытые, PENDING и REJECTED события, смена сессии и промежуток больше 30 минут разрывают сегмент. Период и retention ограничивают наблюдения: начало/конец сегмента не доказывают начало/завершение бизнес-сценария. Сортировка: уникальные сессии по убыванию, затем occurrences, затем канонический порядок последовательности. Дополнительных запросов по узлам нет.",
        "required": [
          "paths",
          "limited",
          "maxPaths"
        ],
        "additionalProperties": false,
        "properties": {
          "paths": {
            "type": "array",
            "maxItems": 12,
            "items": {
              "$ref": "#/components/schemas/ObservedJourney"
            }
          },
          "limited": {
            "type": "boolean",
            "description": "true, если вариантов больше 12 или целиком исключён сегмент длиннее 64 шагов. Длинная последовательность никогда не превращается в обрезанный законченный путь."
          },
          "maxPaths": {
            "const": 12
          }
        },
        "examples": [
          {
            "paths": [],
            "limited": false,
            "maxPaths": 12
          },
          {
            "paths": [
              {
                "id": "tm-path-64d11fe043fe38c7",
                "clusterIds": [
                  "checkout-intent",
                  "payment-intent"
                ],
                "sessions": 8,
                "occurrences": 10,
                "avgDurationMs": 4200
              }
            ],
            "limited": false,
            "maxPaths": 12
          }
        ]
      },
      "Transition": {
        "type": "object",
        "required": [
          "from",
          "to",
          "count",
          "sessions",
          "probability",
          "avgStepMs",
          "loop"
        ],
        "additionalProperties": false,
        "properties": {
          "from": {
            "type": "string"
          },
          "to": {
            "type": "string"
          },
          "count": {
            "type": "integer",
            "minimum": 0
          },
          "sessions": {
            "type": "integer",
            "minimum": 0
          },
          "probability": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "avgStepMs": {
            "type": "number",
            "minimum": 0,
            "maximum": 1800000
          },
          "loop": {
            "type": "boolean"
          }
        }
      },
      "Daily": {
        "type": "object",
        "required": [
          "day",
          "events",
          "sessions"
        ],
        "additionalProperties": false,
        "properties": {
          "day": {
            "type": "string",
            "format": "date"
          },
          "events": {
            "type": "integer",
            "minimum": 0
          },
          "sessions": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "StorageCounts": {
        "type": "object",
        "required": [
          "events",
          "eventReceipts",
          "layouts",
          "clusters",
          "clusterHistory",
          "candidates"
        ],
        "additionalProperties": false,
        "properties": {
          "events": {
            "type": "integer",
            "minimum": 0
          },
          "eventReceipts": {
            "type": "integer",
            "minimum": 0
          },
          "layouts": {
            "type": "integer",
            "minimum": 0
          },
          "clusters": {
            "type": "integer",
            "minimum": 0
          },
          "clusterHistory": {
            "type": "integer",
            "minimum": 0
          },
          "candidates": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "StorageLimits": {
        "type": "object",
        "required": [
          "projectsPerOrganization",
          "events",
          "eventReceipts",
          "layouts",
          "clusters",
          "clusterHistory",
          "candidates",
          "retries"
        ],
        "additionalProperties": false,
        "properties": {
          "projectsPerOrganization": {
            "const": 10
          },
          "events": {
            "const": 2000
          },
          "eventReceipts": {
            "const": 10000
          },
          "layouts": {
            "const": 128
          },
          "clusters": {
            "const": 64
          },
          "clusterHistory": {
            "const": 256
          },
          "candidates": {
            "const": 128
          },
          "retries": {
            "const": 5
          }
        }
      },
      "StorageUsage": {
        "type": "object",
        "required": [
          "used",
          "remaining",
          "limits",
          "replayBounded",
          "receiptIdsRetained"
        ],
        "additionalProperties": false,
        "properties": {
          "used": {
            "$ref": "#/components/schemas/StorageCounts"
          },
          "remaining": {
            "$ref": "#/components/schemas/StorageCounts"
          },
          "limits": {
            "$ref": "#/components/schemas/StorageLimits"
          },
          "replayBounded": {
            "const": true
          },
          "receiptIdsRetained": {
            "const": true
          }
        }
      },
      "Dashboard": {"type":"object","required":["projectId","start","end","timezone","effectiveFrom","historyLimited","collectionEnabled","summary","iris","journeys","intents","states","layouts","transitions","daily","features","retro","limits","methodology"],"additionalProperties":true,"properties":{"projectId":{"type":"string"},"start":{"type":"string","format":"date"},"end":{"type":"string","format":"date"},"timezone":{"const":"UTC"},"effectiveFrom":{"type":"integer"},"historyLimited":{"type":"boolean"},"collectionEnabled":{"type":"boolean"},"iris":{"$ref":"#/components/schemas/Iris"},"journeys":{"$ref":"#/components/schemas/Journeys"},"summary":{"type":"object","required":["events","sessions","layouts","intents","pending","rejected","insufficientAction","candidates","sludgeVersion","topoScore","globalSludgeIndex","sludgeFreeRatio","totalSludgeHours","sludgeCoverage","sludgeBounds","sludgeObservedEvents","sludgeExcludedEvents"],"additionalProperties":false,"properties":{"events":{"type":"integer","minimum":0},"sessions":{"type":"integer","minimum":0},"layouts":{"type":"integer","minimum":0},"intents":{"type":"integer","minimum":0},"pending":{"type":"integer","minimum":0},"rejected":{"type":"integer","minimum":0},"insufficientAction":{"type":"integer","minimum":0},"candidates":{"type":"integer","minimum":0},"sludgeVersion":{"const":"ui-sludge-v1"},"topoScore":{"type":["integer","null"],"minimum":5,"maximum":99},"globalSludgeIndex":{"type":["number","null"],"minimum":0,"maximum":1},"sludgeFreeRatio":{"type":["number","null"],"minimum":0,"maximum":1},"totalSludgeHours":{"type":["number","null"],"minimum":0},"sludgeCoverage":{"type":"object","additionalProperties":false,"properties":{"sludge":{"type":"number","minimum":0,"maximum":1},"time":{"type":"number","minimum":0,"maximum":1}},"required":["sludge","time"]},"sludgeBounds":{"type":"object","additionalProperties":false,"properties":{"globalSludgeIndex":{"type":"object","additionalProperties":false,"properties":{"lower":{"type":"number","minimum":0,"maximum":1},"upper":{"type":"number","minimum":0,"maximum":1}},"required":["lower","upper"]},"totalSludgeHours":{"type":"object","additionalProperties":false,"properties":{"lower":{"type":"number","minimum":0},"upper":{"type":["number","null"],"minimum":0}},"required":["lower","upper"]}},"required":["globalSludgeIndex","totalSludgeHours"]},"sludgeObservedEvents":{"type":"integer","minimum":0},"sludgeExcludedEvents":{"type":"integer","minimum":0}}},"intents":{"type":"array","items":{"$ref":"#/components/schemas/IntentSummary"}},"states":{"type":"array","items":{"$ref":"#/components/schemas/StateSummary"}},"layouts":{"type":"array","items":{"$ref":"#/components/schemas/LayoutSummary"}},"transitions":{"type":"array","items":{"$ref":"#/components/schemas/Transition"}},"daily":{"type":"array","items":{"$ref":"#/components/schemas/Daily"}},"features":{"type":"array","items":{"$ref":"#/components/schemas/Feature"}},"retro":{"type":"object","required":["threshold","clusters","limited","sampledEvents","totalEvents","baselineOnly"],"additionalProperties":false,"properties":{"threshold":{"type":"number","minimum":0.75,"maximum":0.99},"clusters":{"type":"array","items":{"type":"object","required":["id","events"],"additionalProperties":false,"properties":{"id":{"type":"string"},"events":{"type":"integer","minimum":0}}}},"limited":{"type":"boolean"},"sampledEvents":{"type":"integer","minimum":0,"maximum":500},"totalEvents":{"type":"integer","minimum":0},"baselineOnly":{"const":false}}},"limits":{"type":"object","required":["maxEvents","observedEvents","limited"],"additionalProperties":false,"properties":{"maxEvents":{"const":2000},"observedEvents":{"type":"integer","minimum":0},"limited":{"const":false}}},"storage":{"$ref":"#/components/schemas/StorageUsage"},"methodology":{"type":"object","required":["sessionsNotPeople","transitionWindowMinutes","nextClusterId","causalEffect","automaticExperiment","retainedEventsOnly"],"additionalProperties":false,"properties":{"sessionsNotPeople":{"const":true},"transitionWindowMinutes":{"const":30},"nextClusterId":{"type":["string","null"]},"causalEffect":{"const":false},"automaticExperiment":{"const":false},"retainedEventsOnly":{"const":true},"sludgeVersion":{"const":"ui-sludge-v1"},"sludgeEstimatedTime":{"const":true},"sludgeRequiresObservedSignals":{"const":true},"sludgeInputTypes":{"type":"array","prefixItems":[{"const":"INPUT_TEXT"},{"const":"INPUT_ENUM"}],"minItems":2,"maxItems":2},"sludgeIndependentOfDrilldown":{"const":true}}}}},
      "FeatureAnalytics": {"type":"object","required":["period","events","sessions","clusters","daily","sessionsNotPeople","historyLimited","sludge","sludgeSecondsPerSession","sludgeUnavailableReason"],"additionalProperties":false,"properties":{"period":{"type":"object","required":["start","end"],"additionalProperties":false,"properties":{"start":{"type":"string","format":"date"},"end":{"type":"string","format":"date"}}},"events":{"type":"integer","minimum":0},"sessions":{"type":"integer","minimum":0},"clusters":{"type":"array","items":{"type":"object","required":["id","name","projectId","events","sessions"],"additionalProperties":false,"properties":{"id":{"type":"string"},"name":{"type":"string"},"projectId":{"type":"string"},"events":{"type":"integer","minimum":0},"sessions":{"type":"integer","minimum":0}}}},"daily":{"type":"array","items":{"$ref":"#/components/schemas/Daily"}},"sessionsNotPeople":{"const":true},"historyLimited":{"type":"boolean"},"sludge":{"anyOf":[{"$ref":"#/components/schemas/NativeFeatureSludge"},{"type":"null"}]},"sludgeSecondsPerSession":{"type":["number","null"],"minimum":0},"sludgeUnavailableReason":{"enum":["NO_OBSERVATIONS","NO_CLUSTERED_OBSERVATIONS","PROJECT_ANALYSIS_LIMIT",null]}}},
      "ReleasePrint": {"type":"object","additionalProperties":false,"properties":{"id":{"type":"string","pattern":"^TM-R2-[A-F0-9]{16}$"},"issuanceId":{"type":"string","format":"uuid"},"title":{"type":"string","maxLength":160},"summaryNote":{"type":"string","maxLength":280},"privacyMode":{"enum":["STRICT_NDA","NAMED_LISTED"],"default":"STRICT_NDA"},"mode":{"enum":["SINGLE","DIFF"]},"createdAt":{"type":"integer"},"baseline":{"anyOf":[{"type":"object","additionalProperties":false,"properties":{"start":{"type":"string","format":"date"},"end":{"type":"string","format":"date"},"score":{"type":["integer","null"],"minimum":5,"maximum":99},"metrics":{"type":"object","additionalProperties":false,"properties":{"continuation":{"type":["number","null"],"minimum":0,"maximum":1},"loop":{"type":["number","null"],"minimum":0,"maximum":1},"rage":{"type":["number","null"],"minimum":0,"maximum":1},"empty":{"type":["number","null"],"minimum":0,"maximum":1}},"required":["continuation","loop","rage","empty"]},"sample":{"type":"object","additionalProperties":false,"properties":{"events":{"type":"integer","minimum":100,"maximum":2000},"nodes":{"type":"integer","minimum":1,"maximum":128},"edges":{"type":"integer","minimum":0,"maximum":2000}},"required":["events","nodes","edges"]},"graph":{"type":"object","additionalProperties":false,"properties":{"nodes":{"type":"array","minItems":1,"maxItems":128,"items":{"type":"object","additionalProperties":false,"properties":{"id":{"type":"string","pattern":"^p[1-9]\\d{0,3}$"},"label":{"type":"string","maxLength":160},"kind":{"const":"INTENT"},"weight":{"type":"number","minimum":0,"maximum":1},"continuation":{"type":["number","null"],"minimum":0,"maximum":1},"loop":{"type":["number","null"],"minimum":0,"maximum":1},"rage":{"type":["number","null"],"minimum":0,"maximum":1},"empty":{"type":["number","null"],"minimum":0,"maximum":1},"sludge":{"type":"object","additionalProperties":false,"properties":{"sludgeIndex":{"type":["number","null"],"minimum":0,"maximum":1},"sludgeGrade":{"enum":["CLEAN","MODERATE_FRICTION","HIGH_SLUDGE","CRITICAL_SLUDGE",null]},"sludgeSecondsTotal":{"type":["number","null"],"minimum":0,"maximum":9007199254740991},"leontiefWeight":{"type":"number","minimum":0,"maximum":1},"cascadeMultiplier":{"type":["number","null"],"minimum":0,"maximum":9007199254740991},"systemicSludgeShare":{"type":["number","null"],"minimum":0,"maximum":1},"breakdown":{"type":"object","additionalProperties":false,"properties":{"emptyRatio":{"type":["number","null"],"minimum":0,"maximum":1},"loopRate":{"type":["number","null"],"minimum":0,"maximum":1},"rageRate":{"type":["number","null"],"minimum":0,"maximum":1},"dropRate":{"type":["number","null"],"minimum":0,"maximum":1},"dwellDrag":{"type":["number","null"],"minimum":0,"maximum":1}},"required":["emptyRatio","loopRate","rageRate","dropRate","dwellDrag"]},"dominantFactor":{"enum":["emptyRatio","loopRate","rageRate","dropRate","dwellDrag",null]},"baselineSource":{"enum":["MEASURED_CLEAN_MEDIAN","FALLBACK_TRIGGER_8S","FALLBACK_FORM_12S","NOT_AVAILABLE"]},"coverage":{"type":"object","additionalProperties":false,"properties":{"sludge":{"type":"number","minimum":0,"maximum":1},"time":{"type":"number","minimum":0,"maximum":1}},"required":["sludge","time"]}},"required":["sludgeIndex","sludgeGrade","sludgeSecondsTotal","leontiefWeight","cascadeMultiplier","systemicSludgeShare","breakdown","dominantFactor","baselineSource","coverage"]}},"required":["id","label","kind","weight","continuation","loop","rage","empty"]}},"edges":{"type":"array","maxItems":2000,"items":{"type":"object","additionalProperties":false,"properties":{"source":{"type":"string","maxLength":5},"target":{"type":"string","maxLength":5},"weight":{"type":"number","minimum":0,"maximum":1}},"required":["source","target","weight"]}}},"required":["nodes","edges"]},"sludge":{"type":"object","additionalProperties":false,"properties":{"version":{"const":"ui-sludge-v1"},"estimated":{"const":true},"globalSludgeIndex":{"type":["number","null"],"minimum":0,"maximum":1},"sludgeFreeRatio":{"type":["number","null"],"minimum":0,"maximum":1},"totalSludgeHours":{"type":["number","null"],"minimum":0,"maximum":9007199254740991},"coverage":{"type":"object","additionalProperties":false,"properties":{"sludge":{"type":"number","minimum":0,"maximum":1},"time":{"type":"number","minimum":0,"maximum":1}},"required":["sludge","time"]},"baseline":{"type":"object","additionalProperties":false,"properties":{"measuredNodes":{"type":"integer","minimum":0,"maximum":128},"fallbackNodes":{"type":"integer","minimum":0,"maximum":128},"unavailableNodes":{"type":"integer","minimum":0,"maximum":128}},"required":["measuredNodes","fallbackNodes","unavailableNodes"]}},"required":["version","estimated","globalSludgeIndex","sludgeFreeRatio","totalSludgeHours","coverage","baseline"]}},"required":["start","end","score","metrics","sample","graph"]},{"type":"null"}]},"target":{"type":"object","additionalProperties":false,"properties":{"start":{"type":"string","format":"date"},"end":{"type":"string","format":"date"},"score":{"type":["integer","null"],"minimum":5,"maximum":99},"metrics":{"type":"object","additionalProperties":false,"properties":{"continuation":{"type":["number","null"],"minimum":0,"maximum":1},"loop":{"type":["number","null"],"minimum":0,"maximum":1},"rage":{"type":["number","null"],"minimum":0,"maximum":1},"empty":{"type":["number","null"],"minimum":0,"maximum":1}},"required":["continuation","loop","rage","empty"]},"sample":{"type":"object","additionalProperties":false,"properties":{"events":{"type":"integer","minimum":100,"maximum":2000},"nodes":{"type":"integer","minimum":1,"maximum":128},"edges":{"type":"integer","minimum":0,"maximum":2000}},"required":["events","nodes","edges"]},"graph":{"type":"object","additionalProperties":false,"properties":{"nodes":{"type":"array","minItems":1,"maxItems":128,"items":{"type":"object","additionalProperties":false,"properties":{"id":{"type":"string","pattern":"^p[1-9]\\d{0,3}$"},"label":{"type":"string","maxLength":160},"kind":{"const":"INTENT"},"weight":{"type":"number","minimum":0,"maximum":1},"continuation":{"type":["number","null"],"minimum":0,"maximum":1},"loop":{"type":["number","null"],"minimum":0,"maximum":1},"rage":{"type":["number","null"],"minimum":0,"maximum":1},"empty":{"type":["number","null"],"minimum":0,"maximum":1},"sludge":{"type":"object","additionalProperties":false,"properties":{"sludgeIndex":{"type":["number","null"],"minimum":0,"maximum":1},"sludgeGrade":{"enum":["CLEAN","MODERATE_FRICTION","HIGH_SLUDGE","CRITICAL_SLUDGE",null]},"sludgeSecondsTotal":{"type":["number","null"],"minimum":0,"maximum":9007199254740991},"leontiefWeight":{"type":"number","minimum":0,"maximum":1},"cascadeMultiplier":{"type":["number","null"],"minimum":0,"maximum":9007199254740991},"systemicSludgeShare":{"type":["number","null"],"minimum":0,"maximum":1},"breakdown":{"type":"object","additionalProperties":false,"properties":{"emptyRatio":{"type":["number","null"],"minimum":0,"maximum":1},"loopRate":{"type":["number","null"],"minimum":0,"maximum":1},"rageRate":{"type":["number","null"],"minimum":0,"maximum":1},"dropRate":{"type":["number","null"],"minimum":0,"maximum":1},"dwellDrag":{"type":["number","null"],"minimum":0,"maximum":1}},"required":["emptyRatio","loopRate","rageRate","dropRate","dwellDrag"]},"dominantFactor":{"enum":["emptyRatio","loopRate","rageRate","dropRate","dwellDrag",null]},"baselineSource":{"enum":["MEASURED_CLEAN_MEDIAN","FALLBACK_TRIGGER_8S","FALLBACK_FORM_12S","NOT_AVAILABLE"]},"coverage":{"type":"object","additionalProperties":false,"properties":{"sludge":{"type":"number","minimum":0,"maximum":1},"time":{"type":"number","minimum":0,"maximum":1}},"required":["sludge","time"]}},"required":["sludgeIndex","sludgeGrade","sludgeSecondsTotal","leontiefWeight","cascadeMultiplier","systemicSludgeShare","breakdown","dominantFactor","baselineSource","coverage"]}},"required":["id","label","kind","weight","continuation","loop","rage","empty"]}},"edges":{"type":"array","maxItems":2000,"items":{"type":"object","additionalProperties":false,"properties":{"source":{"type":"string","maxLength":5},"target":{"type":"string","maxLength":5},"weight":{"type":"number","minimum":0,"maximum":1}},"required":["source","target","weight"]}}},"required":["nodes","edges"]},"sludge":{"type":"object","additionalProperties":false,"properties":{"version":{"const":"ui-sludge-v1"},"estimated":{"const":true},"globalSludgeIndex":{"type":["number","null"],"minimum":0,"maximum":1},"sludgeFreeRatio":{"type":["number","null"],"minimum":0,"maximum":1},"totalSludgeHours":{"type":["number","null"],"minimum":0,"maximum":9007199254740991},"coverage":{"type":"object","additionalProperties":false,"properties":{"sludge":{"type":"number","minimum":0,"maximum":1},"time":{"type":"number","minimum":0,"maximum":1}},"required":["sludge","time"]},"baseline":{"type":"object","additionalProperties":false,"properties":{"measuredNodes":{"type":"integer","minimum":0,"maximum":128},"fallbackNodes":{"type":"integer","minimum":0,"maximum":128},"unavailableNodes":{"type":"integer","minimum":0,"maximum":128}},"required":["measuredNodes","fallbackNodes","unavailableNodes"]}},"required":["version","estimated","globalSludgeIndex","sludgeFreeRatio","totalSludgeHours","coverage","baseline"]}},"required":["start","end","score","metrics","sample","graph"]},"deltas":{"type":"object","additionalProperties":false,"properties":{"continuationBp":{"type":["integer","null"],"minimum":-10000,"maximum":10000},"loopBp":{"type":["integer","null"],"minimum":-10000,"maximum":10000},"rageBp":{"type":["integer","null"],"minimum":-10000,"maximum":10000},"emptyBp":{"type":["integer","null"],"minimum":-10000,"maximum":10000},"sludgeBp":{"type":["integer","null"],"minimum":-10000,"maximum":10000}},"required":["continuationBp","loopBp","rageBp","emptyBp"]},"verificationHash":{"type":"string","pattern":"^[a-f0-9]{64}$"},"methodologyVersion":{"enum":["tm-release-index-v1","tm-release-sludge-v2"]}},"required":["id","issuanceId","title","summaryNote","privacyMode","mode","createdAt","baseline","target","deltas","verificationHash","methodologyVersion"],"description":"Immutable signed allowlist. V1 JSON and TopoScore remain unchanged; absent sludge means historical data, never measured zero. V2 requires ui-sludge-v1 metadata for each period and node. Hours are a model estimate, not measured time saved. SQL zero defaults are not measurement evidence. Runtime also validates score, delta, version and graph consistency.","allOf":[{"if":{"properties":{"methodologyVersion":{"const":"tm-release-sludge-v2"}}},"then":{"properties":{"target":{"type":"object","required":["sludge"],"properties":{"graph":{"type":"object","properties":{"nodes":{"type":"array","items":{"type":"object","required":["sludge"]}}}}}},"baseline":{"anyOf":[{"type":"object","required":["sludge"],"properties":{"graph":{"type":"object","properties":{"nodes":{"type":"array","items":{"type":"object","required":["sludge"]}}}}}},{"type":"null"}]},"deltas":{"required":["sludgeBp"]}}},"else":{"properties":{"target":{"type":"object","not":{"required":["sludge"]},"properties":{"graph":{"type":"object","properties":{"nodes":{"type":"array","items":{"type":"object","not":{"required":["sludge"]}}}}}}},"baseline":{"anyOf":[{"type":"object","not":{"required":["sludge"]},"properties":{"graph":{"type":"object","properties":{"nodes":{"type":"array","items":{"type":"object","not":{"required":["sludge"]}}}}}}},{"type":"null"}]},"deltas":{"not":{"required":["sludgeBp"]}}}}}]},
      "NativeActionSludge": {"type":"object","additionalProperties":false,"properties":{"sludgeIndex":{"type":["number","null"],"minimum":0,"maximum":1},"rawSludgeIndex":{"type":["number","null"],"minimum":0,"maximum":1},"sludgeGrade":{"enum":["CLEAN","MODERATE_FRICTION","HIGH_SLUDGE","CRITICAL_SLUDGE",null]},"sludgeSecondsTotal":{"type":["number","null"],"minimum":0},"leontiefWeight":{"type":"number","minimum":0,"maximum":1},"directShare":{"type":"number","minimum":0,"maximum":1},"cascadeMultiplier":{"type":["number","null"],"minimum":0},"outgoingHhi":{"type":["number","null"],"minimum":0,"maximum":1},"effectiveNextSteps":{"type":["number","null"],"minimum":0},"observedOutgoingTransitions":{"type":"integer","minimum":0},"repeatShare":{"type":["number","null"],"minimum":0,"maximum":1},"systemicSludgeShare":{"type":["number","null"],"minimum":0,"maximum":1},"systemicContribution":{"type":["number","null"],"minimum":0,"maximum":1},"breakdown":{"type":"object","additionalProperties":false,"properties":{"emptyRatio":{"type":["number","null"],"minimum":0,"maximum":1},"loopRate":{"type":["number","null"],"minimum":0,"maximum":1},"rageRate":{"type":["number","null"],"minimum":0,"maximum":1},"dropRate":{"type":["number","null"],"minimum":0,"maximum":1},"dwellDrag":{"type":["number","null"],"minimum":0,"maximum":1}},"required":["emptyRatio","loopRate","rageRate","dropRate","dwellDrag"]},"weights":{"type":"object","additionalProperties":false,"properties":{"empty":{"type":"number","minimum":0,"maximum":1},"loop":{"type":"number","minimum":0,"maximum":1},"rage":{"type":"number","minimum":0,"maximum":1},"drop":{"type":"number","minimum":0,"maximum":1},"dwell":{"type":"number","minimum":0,"maximum":1}},"required":["empty","loop","rage","drop","dwell"]},"dominantFactor":{"enum":["emptyRatio","loopRate","rageRate","dropRate","dwellDrag",null]},"baselineSource":{"enum":["MEASURED_CLEAN_MEDIAN","FALLBACK_TRIGGER_8S","FALLBACK_FORM_12S","NOT_AVAILABLE"]},"cleanDwellSec":{"type":["number","null"],"minimum":0},"smoothed":{"type":"boolean"},"sampleQuality":{"enum":["READY","LOW_SAMPLE","NO_EVENTS"]},"coverage":{"type":"object","additionalProperties":false,"properties":{"sludge":{"type":"number","minimum":0,"maximum":1},"time":{"type":"number","minimum":0,"maximum":1},"dwell":{"type":"number","minimum":0,"maximum":1},"rage":{"type":"number","minimum":0,"maximum":1},"inputs":{"type":"number","minimum":0,"maximum":1}},"required":["sludge","time","dwell","rage","inputs"]},"bounds":{"type":"object","additionalProperties":false,"properties":{"sludge":{"type":"object","additionalProperties":false,"properties":{"lower":{"type":"number","minimum":0},"upper":{"type":["number","null"],"minimum":0}},"required":["lower","upper"]},"seconds":{"type":"object","additionalProperties":false,"properties":{"lower":{"type":["number","null"],"minimum":0},"upper":{"type":["number","null"],"minimum":0}},"required":["lower","upper"]}},"required":["sludge","seconds"]},"sludgeVersion":{"const":"ui-sludge-v1"},"sludgeCoverage":{"type":"object","additionalProperties":false,"properties":{"sludge":{"type":"number","minimum":0,"maximum":1},"time":{"type":"number","minimum":0,"maximum":1},"dwell":{"type":"number","minimum":0,"maximum":1},"rage":{"type":"number","minimum":0,"maximum":1},"inputs":{"type":"number","minimum":0,"maximum":1}},"required":["sludge","time","dwell","rage","inputs"]},"sludgeBaselineSource":{"enum":["MEASURED_CLEAN_MEDIAN","FALLBACK_TRIGGER_8S","FALLBACK_FORM_12S","NOT_AVAILABLE"]},"sludgeSampleQuality":{"enum":["READY","LOW_SAMPLE","NO_EVENTS"]},"sludgeSecondsPerSession":{"type":["number","null"],"minimum":0}},"required":["sludgeIndex","rawSludgeIndex","sludgeGrade","sludgeSecondsTotal","leontiefWeight","directShare","cascadeMultiplier","outgoingHhi","effectiveNextSteps","observedOutgoingTransitions","repeatShare","systemicSludgeShare","systemicContribution","breakdown","weights","dominantFactor","baselineSource","cleanDwellSec","smoothed","sampleQuality","coverage","bounds","sludgeVersion","sludgeCoverage","sludgeBaselineSource","sludgeSampleQuality","sludgeSecondsPerSession"],"description":"Deterministic observational model ui-sludge-v1. Null means unavailable, not zero. Dwell time and SludgeSeconds are heuristic estimates, not proven wasted/saved hours, emotions, money or causal effects. Input EMPTY ratio uses only observed INPUT_TEXT/INPUT_ENUM states; no field values are returned."},
      "NativeSludgeSummary": {"type":"object","additionalProperties":false,"properties":{"sludgeVersion":{"const":"ui-sludge-v1"},"topoScore":{"type":["integer","null"],"minimum":5,"maximum":99},"globalSludgeIndex":{"type":["number","null"],"minimum":0,"maximum":1},"sludgeFreeRatio":{"type":["number","null"],"minimum":0,"maximum":1},"totalSludgeHours":{"type":["number","null"],"minimum":0},"sludgeCoverage":{"type":"object","additionalProperties":false,"properties":{"sludge":{"type":"number","minimum":0,"maximum":1},"time":{"type":"number","minimum":0,"maximum":1}},"required":["sludge","time"]},"sludgeBounds":{"type":"object","additionalProperties":false,"properties":{"globalSludgeIndex":{"type":"object","additionalProperties":false,"properties":{"lower":{"type":"number","minimum":0,"maximum":1},"upper":{"type":"number","minimum":0,"maximum":1}},"required":["lower","upper"]},"totalSludgeHours":{"type":"object","additionalProperties":false,"properties":{"lower":{"type":"number","minimum":0},"upper":{"type":["number","null"],"minimum":0}},"required":["lower","upper"]}},"required":["globalSludgeIndex","totalSludgeHours"]},"sludgeObservedEvents":{"type":"integer","minimum":0},"sludgeExcludedEvents":{"type":"integer","minimum":0}},"required":["sludgeVersion","topoScore","globalSludgeIndex","sludgeFreeRatio","totalSludgeHours","sludgeCoverage","sludgeBounds","sludgeObservedEvents","sludgeExcludedEvents"],"description":"Role-visible full-product calculation, independent of dashboard drilldown. Unknown components remain null; sludgeObservedEvents/sludgeExcludedEvents expose analytical coverage. No causal, financial or investment interpretation."},
      "NativeFeatureSludge": {"type":"object","additionalProperties":false,"properties":{"version":{"const":"ui-sludge-v1"},"scope":{"const":"FEATURE_EVENT_WEIGHTED"},"globalSludgeIndex":{"type":["number","null"],"minimum":0,"maximum":1},"sludgeIndex":{"type":["number","null"],"minimum":0,"maximum":1},"sludgeGrade":{"enum":["CLEAN","MODERATE_FRICTION","HIGH_SLUDGE","CRITICAL_SLUDGE",null]},"sludgeFreeRatio":{"type":["number","null"],"minimum":0,"maximum":1},"totalSludgeHours":{"type":["number","null"],"minimum":0},"sludgeSecondsTotal":{"type":["number","null"],"minimum":0},"observedEvents":{"type":"integer","minimum":0},"totalFeatureEvents":{"type":"integer","minimum":0},"coverage":{"type":"object","additionalProperties":false,"properties":{"sludge":{"type":"number","minimum":0,"maximum":1},"time":{"type":"number","minimum":0,"maximum":1}},"required":["sludge","time"]}},"required":["version","scope","globalSludgeIndex","sludgeIndex","sludgeGrade","sludgeFreeRatio","totalSludgeHours","sludgeSecondsTotal","observedEvents","totalFeatureEvents","coverage"]},
      "IssueSnapshotSludge": {"type":"object","additionalProperties":false,"properties":{"sludgeIndex":{"type":["number","null"],"minimum":0,"maximum":1},"rawSludgeIndex":{"type":["number","null"],"minimum":0,"maximum":1},"sludgeGrade":{"enum":["CLEAN","MODERATE_FRICTION","HIGH_SLUDGE","CRITICAL_SLUDGE",null]},"sludgeSecondsTotal":{"type":["number","null"],"minimum":0},"leontiefWeight":{"type":"number","minimum":0,"maximum":1},"directShare":{"type":"number","minimum":0,"maximum":1},"cascadeMultiplier":{"type":["number","null"],"minimum":0},"outgoingHhi":{"type":["number","null"],"minimum":0,"maximum":1},"effectiveNextSteps":{"type":["number","null"],"minimum":0},"observedOutgoingTransitions":{"type":"integer","minimum":0},"repeatShare":{"type":["number","null"],"minimum":0,"maximum":1},"systemicSludgeShare":{"type":["number","null"],"minimum":0,"maximum":1},"systemicContribution":{"type":["number","null"],"minimum":0,"maximum":1},"breakdown":{"type":"object","additionalProperties":false,"properties":{"emptyRatio":{"type":["number","null"],"minimum":0,"maximum":1},"loopRate":{"type":["number","null"],"minimum":0,"maximum":1},"rageRate":{"type":["number","null"],"minimum":0,"maximum":1},"dropRate":{"type":["number","null"],"minimum":0,"maximum":1},"dwellDrag":{"type":["number","null"],"minimum":0,"maximum":1}},"required":["emptyRatio","loopRate","rageRate","dropRate","dwellDrag"]},"weights":{"type":"object","additionalProperties":false,"properties":{"empty":{"type":"number","minimum":0,"maximum":1},"loop":{"type":"number","minimum":0,"maximum":1},"rage":{"type":"number","minimum":0,"maximum":1},"drop":{"type":"number","minimum":0,"maximum":1},"dwell":{"type":"number","minimum":0,"maximum":1}},"required":["empty","loop","rage","drop","dwell"]},"dominantFactor":{"enum":["emptyRatio","loopRate","rageRate","dropRate","dwellDrag",null]},"baselineSource":{"enum":["MEASURED_CLEAN_MEDIAN","FALLBACK_TRIGGER_8S","FALLBACK_FORM_12S","NOT_AVAILABLE"]},"cleanDwellSec":{"type":["number","null"],"minimum":0},"smoothed":{"type":"boolean"},"sampleQuality":{"enum":["READY","LOW_SAMPLE","NO_EVENTS"]},"coverage":{"type":"object","additionalProperties":false,"properties":{"sludge":{"type":"number","minimum":0,"maximum":1},"time":{"type":"number","minimum":0,"maximum":1},"dwell":{"type":"number","minimum":0,"maximum":1},"rage":{"type":"number","minimum":0,"maximum":1},"inputs":{"type":"number","minimum":0,"maximum":1}},"required":["sludge","time","dwell","rage","inputs"]},"bounds":{"type":"object","additionalProperties":false,"properties":{"sludge":{"type":"object","additionalProperties":false,"properties":{"lower":{"type":"number","minimum":0},"upper":{"type":["number","null"],"minimum":0}},"required":["lower","upper"]},"seconds":{"type":"object","additionalProperties":false,"properties":{"lower":{"type":["number","null"],"minimum":0},"upper":{"type":["number","null"],"minimum":0}},"required":["lower","upper"]}},"required":["sludge","seconds"]},"sludgeVersion":{"const":"ui-sludge-v1"},"sludgeCoverage":{"type":"object","additionalProperties":false,"properties":{"sludge":{"type":"number","minimum":0,"maximum":1},"time":{"type":"number","minimum":0,"maximum":1},"dwell":{"type":"number","minimum":0,"maximum":1},"rage":{"type":"number","minimum":0,"maximum":1},"inputs":{"type":"number","minimum":0,"maximum":1}},"required":["sludge","time","dwell","rage","inputs"]},"sludgeBaselineSource":{"enum":["MEASURED_CLEAN_MEDIAN","FALLBACK_TRIGGER_8S","FALLBACK_FORM_12S","NOT_AVAILABLE"]},"sludgeSampleQuality":{"enum":["READY","LOW_SAMPLE","NO_EVENTS"]},"sludgeSecondsPerSession":{"type":["number","null"],"minimum":0},"version":{"const":"ui-sludge-v1"},"scope":{"const":"SNAPSHOT_ACTION"}},"required":["sludgeIndex","rawSludgeIndex","sludgeGrade","sludgeSecondsTotal","leontiefWeight","directShare","cascadeMultiplier","outgoingHhi","effectiveNextSteps","observedOutgoingTransitions","repeatShare","systemicSludgeShare","systemicContribution","breakdown","weights","dominantFactor","baselineSource","cleanDwellSec","smoothed","sampleQuality","coverage","bounds","sludgeVersion","sludgeCoverage","sludgeBaselineSource","sludgeSampleQuality","sludgeSecondsPerSession","version","scope"],"description":"Private object-scoped immutable diagnostic snapshot. Local SI refers to the selected action/layout/state observations. A systemic share is available only when the selected population represents the whole action; unrelated product analytics are not exposed."}
    }
  },
  "x-runtime-limits": {
    "collectorAdmissionGlobalPerMinute": 6000,
    "configurationPerProjectPerMinute": 300,
    "configurationGlobalPerMinute": 6000,
    "adminMutationPerActorPerMinute": 60,
    "adminMutationPerOrganizationPerMinute": 180,
    "collectorPerProjectPerMinute": 600,
    "collectorGlobalPerMinute": 6000,
    "projectsPerOrganization": 10,
    "events": 2000,
    "eventReceipts": 10000,
    "layouts": 128,
    "clusters": 64,
    "clusterHistory": 256,
    "candidates": 128,
    "transactionRetries": 5
  },
  "x-privacy-and-interpretation": {
    "noInputValues": true,
    "administratorAllowlists": true,
    "sessionsNotPeople": true,
    "causalEffect": false,
    "automaticExperiment": false,
    "priceInfluence": false,
    "learnedSemanticModel": false,
    "receiptTombstones": "retained after payload erasure; lifetime project limit 10000; no LRU replay-forgetting"
  }
}
