{
  "openapi": "3.1.0",
  "info": {
    "title": "Obsero Scan API",
    "version": "1.12.0",
    "description": "API publique et d'espace pour sélectionner une tâche sûre, lancer, relire et rejouer exactement des diagnostics Obsero. resultContract est la source de vérité commune au rapport, aux exports, à REST, CI et MCP. Une réussite exige un parcours exécuté et une preuve référencée. Le score mesure uniquement la fondation technique et le test facultatif avec un modèle ne modifie jamais le diagnostic déterministe.",
    "x-rateLimitPolicy": "Les réponses 429 incluent un code stable et peuvent inclure Retry-After. Les campagnes agent réservent des budgets globaux et appliquent des quotas par instance, propriétaire, configuration et période, ainsi qu'un coupe-circuit fournisseur."
  },
  "paths": {
    "/api/v1/scans": {
      "post": {
        "operationId": "createScan",
        "summary": "Analyser une URL",
        "security": [{ "WorkspaceApiKey": [] }, {}],
        "x-codeSamples": [
          { "lang": "curl", "source": "curl -X POST https://obsero.example.com/api/v1/scans -H 'content-type: application/json' --data '{\"url\":\"https://example.com\",\"task\":{\"type\":\"public_information\"}}'" },
          { "lang": "TypeScript", "source": "const response = await fetch('https://obsero.example.com/api/v1/scans', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ url: 'https://example.com', task: { type: 'public_information' } }) });\nconst scan = await response.json();" },
          { "lang": "Python", "source": "import requests\nscan = requests.post('https://obsero.example.com/api/v1/scans', json={'url': 'https://example.com', 'task': {'type': 'public_information'}}, timeout=30).json()" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": ["url"],
                "properties": {
                  "url": { "type": "string", "maxLength": 2048, "examples": ["https://example.com"] },
                  "task": { "$ref": "#/components/schemas/ScanTaskInput" }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Scan terminé et enregistré",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Scan" } } }
          },
          "400": { "$ref": "#/components/responses/InvalidRequest" },
          "413": { "$ref": "#/components/responses/InvalidRequest" },
          "429": {
            "description": "Limite de scans atteinte. Respecter le header Retry-After.",
            "headers": {
              "Retry-After": {
                "schema": { "type": "integer" },
                "description": "Délai en secondes avant une nouvelle tentative."
              }
            },
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
          },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/api/v1/scans/{publicId}": {
      "get": {
        "operationId": "getScan",
        "summary": "Relire un scan public",
        "security": [{ "WorkspaceApiKey": [] }, {}],
        "parameters": [
          {
            "name": "publicId",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "pattern": "^[A-Za-z0-9_-]{16}$" }
          }
        ],
        "responses": {
          "200": {
            "description": "Résultat public du scan",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Scan" } } }
          },
          "404": {
            "description": "Scan introuvable",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
          },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/api/v1/scans/{publicId}/replay": {
      "post": {
        "operationId": "replayScanTask",
        "summary": "Rejouer exactement la tâche sélectionnée d'un scan",
        "description": "Clone le contrat de tâche versionné, ses étapes, ses oracles et sa page de départ. Aucun succès historique n'est réutilisé. Un scan privé exige un accès d'écriture au compte ou à l'espace.",
        "security": [{ "SessionCookie": [] }, {}],
        "parameters": [
          { "name": "publicId", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[A-Za-z0-9_-]{16}$" } }
        ],
        "responses": {
          "202": { "description": "Nouveau scan préparé avec le même contrat de tâche", "headers": { "Location": { "schema": { "type": "string" } } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScanReplay" } } } },
          "400": { "description": "Le scan historique ne contient pas de tâche sélectionnée rejouable", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "403": { "description": "Le rejeu d'un scan privé exige un accès d'écriture", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "404": { "description": "Scan introuvable ou inaccessible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "description": "Limite de scans atteinte", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/v1/scans/{publicId}/agent-tests": {
      "get": {
        "operationId": "listAgentTests",
        "summary": "Relire les exécutions agent facultatives d'un scan",
        "security": [{ "WorkspaceApiKey": [] }, {}],
        "parameters": [{ "name": "publicId", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[A-Za-z0-9_-]{16}$" } }],
        "responses": {
          "200": { "description": "Liste des exécutions", "content": { "application/json": { "schema": { "type": "object", "required": ["tests"], "properties": { "tests": { "type": "array", "items": { "$ref": "#/components/schemas/AgentTest" } } } } } } }
        }
      },
      "post": {
        "operationId": "createAgentTest",
        "summary": "Lancer explicitement une tâche agent sûre",
        "description": "Ne se lance jamais avec le scan de base. La tâche et l'oracle sont construits depuis une preuve navigateur déterministe ou un fait public stable ; aucune tâche libre n'est acceptée. Une clé machine exige agent:write et consomme les mêmes crédits que le propriétaire de l'espace. Un navigateur anonyme peut utiliser un crédit sur 24 heures uniquement pour le scan public qu'il vient de créer, avec la preuve HttpOnly signée et une clé de connexion pseudonymisée ; un compte vérifié conserve cinq crédits sur 24 heures.",
        "security": [{ "WorkspaceApiKey": [] }, { "SessionCookie": [] }, { "AnonymousScanClaim": [] }],
        "parameters": [
          { "name": "publicId", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[A-Za-z0-9_-]{16}$" } },
          { "name": "Idempotency-Key", "in": "header", "required": true, "description": "Valeur stable pour rejouer la même intention après une réponse perdue.", "schema": { "type": "string", "minLength": 8, "maxLength": 160, "pattern": "^[A-Za-z0-9._:-]+$" } }
        ],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "oneOf": [
          { "type": "object", "additionalProperties": false, "required": ["confirm", "taskType"], "properties": { "confirm": { "const": true }, "taskType": { "enum": ["public_information", "navigate_information_page", "internal_search"] } } },
          { "type": "object", "additionalProperties": false, "required": ["confirm", "targetTestId"], "properties": { "confirm": { "const": true }, "targetTestId": { "$ref": "#/components/schemas/TargetTestId" } } },
          { "type": "object", "additionalProperties": false, "required": ["confirm", "taskTemplatePublicId"], "properties": { "confirm": { "const": true }, "taskTemplatePublicId": { "type": "string", "pattern": "^[A-Za-z0-9_-]{24}$" } } }
        ] } } } },
        "responses": {
          "200": { "description": "Rejeu idempotent d'une exécution existante", "headers": { "Idempotency-Replayed": { "schema": { "const": "true" } } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AgentTest" } } } },
          "202": { "description": "Exécution réservée et mise en file", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AgentTest" } } } },
          "403": { "description": "Preuve du navigateur anonyme, origine ou confirmation CSRF invalide", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "409": { "description": "Scénarios déterministes incomplets ou tâche déjà lancée", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "422": { "description": "Oracle sûr ou référence déterministe indisponible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "description": "Limite par scan ou coupe-circuit journalier atteint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "503": { "description": "Fournisseur non configuré", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/v1/scans/{publicId}/test-plan": {
      "get": {
        "operationId": "getScanTestPlan",
        "summary": "Lire le plan déterministe avant toute dépense agent",
        "parameters": [{ "name": "publicId", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[A-Za-z0-9_-]{16}$" } }],
        "responses": {
          "200": { "description": "Tests applicables, non applicables, interdits et exécutés", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ScanTestPlan" } } } },
          "404": { "description": "Scan introuvable", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/v1/scans/{publicId}/agent-tests/{agentPublicId}": {
      "get": {
        "operationId": "getAgentTest",
        "summary": "Relire une exécution agent et chacune de ses observations",
        "parameters": [
          { "name": "publicId", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[A-Za-z0-9_-]{16}$" } },
          { "name": "agentPublicId", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[A-Za-z0-9_-]{24}$" } }
        ],
        "responses": {
          "200": { "description": "Exécution agent publique", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AgentTest" } } } },
          "404": { "description": "Exécution introuvable", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      },
      "delete": {
        "operationId": "cancelAgentTest",
        "summary": "Annuler une exécution agent en attente ou en cours",
        "security": [{ "WorkspaceApiKey": [] }, { "SessionCookie": [] }, { "AnonymousScanClaim": [] }],
        "parameters": [
          { "name": "publicId", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[A-Za-z0-9_-]{16}$" } },
          { "name": "agentPublicId", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[A-Za-z0-9_-]{24}$" } }
        ],
        "responses": {
          "200": { "description": "État courant, annulé si le run n'était pas terminal", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AgentTest" } } } },
          "404": { "description": "Exécution introuvable", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/v1/scans/{publicId}/agent-campaigns": {
      "get": {
        "operationId": "listAgentCampaigns",
        "summary": "Lister les campagnes agent répétées",
        "parameters": [{ "name": "publicId", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[A-Za-z0-9_-]{16}$" } }],
        "responses": {
          "200": { "description": "Campagnes et runs bruts", "content": { "application/json": { "schema": { "type": "object", "required": ["campaigns"], "properties": { "campaigns": { "type": "array", "items": { "$ref": "#/components/schemas/AgentCampaign" } } } } } } }
        }
      },
      "post": {
        "operationId": "createAgentCampaign",
        "summary": "Lancer explicitement une campagne bornée",
        "description": "Chaque run utilise une session neuve avec la même tâche, le même oracle et les mêmes limites. Une à quatre configurations peuvent être équilibrées dans la campagne. Les tâches personnalisées et environnements authentifiés exigent la session propriétaire et restent owner_only.",
        "parameters": [
          { "name": "publicId", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[A-Za-z0-9_-]{16}$" } },
          { "name": "Idempotency-Key", "in": "header", "required": true, "schema": { "type": "string", "minLength": 8, "maxLength": 120, "pattern": "^[a-zA-Z0-9._:-]+$" } }
        ],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateAgentCampaign" }, "examples": { "comparison": { "value": { "confirm": true, "taskType": "public_information", "repetitions": 3, "configurationIds": ["openrouter-a", "compatible-b"] } } } } }
        },
        "responses": {
          "200": { "description": "Rejeu idempotent d'une campagne existante", "headers": { "Idempotency-Replayed": { "schema": { "const": "true" } } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AgentCampaign" } } } },
          "202": { "description": "Campagne réservée et mise en file", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AgentCampaign" } } } },
          "400": { "$ref": "#/components/responses/InvalidRequest" },
          "409": { "description": "Scan déterministe incomplet", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "422": { "description": "Tâche, configuration, comparabilité ou limite de runs invalide", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "description": "Quota d'instance, propriétaire, configuration, campagne, runs ou coût atteint", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "503": { "description": "Fournisseur ou file indisponible", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/v1/scans/{publicId}/agent-campaigns/{campaignPublicId}": {
      "get": {
        "operationId": "getAgentCampaign",
        "summary": "Relire une campagne, ses agrégats et toutes ses traces",
        "parameters": [
          { "name": "publicId", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[A-Za-z0-9_-]{16}$" } },
          { "name": "campaignPublicId", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[A-Za-z0-9_-]{24}$" } }
        ],
        "responses": {
          "200": { "description": "Campagne publique", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AgentCampaign" } } } },
          "404": { "description": "Campagne introuvable", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      },
      "delete": {
        "operationId": "cancelAgentCampaign",
        "summary": "Arrêter une campagne sans perdre les runs terminés",
        "description": "Exige la session administrateur propriétaire. Les runs terminés restent intacts ; les autres sont annulés et toute session en cours est nettoyée.",
        "parameters": [
          { "name": "publicId", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[A-Za-z0-9_-]{16}$" } },
          { "name": "campaignPublicId", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[A-Za-z0-9_-]{24}$" } }
        ],
        "responses": {
          "200": { "description": "Campagne arrêtée et résultats acquis conservés", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AgentCampaign" } } } },
          "404": { "description": "Campagne introuvable", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/v1/scans/{publicId}/agent-campaigns/{campaignPublicId}/export": {
      "get": {
        "operationId": "exportAgentCampaign",
        "summary": "Exporter les runs bruts d'une campagne",
        "parameters": [
          { "name": "publicId", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[A-Za-z0-9_-]{16}$" } },
          { "name": "campaignPublicId", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[A-Za-z0-9_-]{24}$" } },
          { "name": "format", "in": "query", "schema": { "enum": ["json", "csv"], "default": "json" } }
        ],
        "responses": {
          "200": { "description": "Export JSON versionné ou CSV protégé contre l'injection de formules" },
          "404": { "description": "Campagne introuvable ou owner_only sans session propriétaire", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/v1/workflows/task-templates": {
      "get": {
        "operationId": "exportTaskTemplates",
        "summary": "Exporter des modèles de tâche versionnés",
        "security": [{ "WorkspaceApiKey": [] }],
        "x-requiredScopes": ["templates:read"],
        "parameters": [
          { "name": "scanPublicId", "in": "query", "required": true, "schema": { "type": "string", "pattern": "^[A-Za-z0-9_-]{16}$" } },
          { "name": "templatePublicId", "in": "query", "schema": { "type": "string", "pattern": "^[A-Za-z0-9_-]{24}$" } }
        ],
        "responses": { "200": { "description": "Documents expurgés et versionnés", "content": { "application/json": { "schema": { "type": "object", "required": ["documents"], "properties": { "documents": { "type": "array", "items": { "$ref": "#/components/schemas/TaskTemplateTransfer" } } } } } } }, "401": { "$ref": "#/components/responses/AuthenticationError" }, "403": { "$ref": "#/components/responses/AuthenticationError" } }
      },
      "post": {
        "operationId": "importTaskTemplate",
        "summary": "Importer un modèle après revalidation sur la preuve cible",
        "security": [{ "WorkspaceApiKey": [] }],
        "x-requiredScopes": ["templates:write"],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "additionalProperties": false, "required": ["scanPublicId", "document"], "properties": { "scanPublicId": { "type": "string" }, "document": { "$ref": "#/components/schemas/TaskTemplateTransfer" } } } } } },
        "responses": { "201": { "description": "Nouvelle famille immuable créée" }, "401": { "$ref": "#/components/responses/AuthenticationError" }, "403": { "$ref": "#/components/responses/AuthenticationError" }, "422": { "$ref": "#/components/responses/InvalidRequest" } }
      }
    },
    "/api/v1/workflows/baselines": {
      "get": {
        "operationId": "listOrExportBaselines",
        "summary": "Lister ou exporter des baselines versionnées",
        "security": [{ "WorkspaceApiKey": [] }],
        "x-requiredScopes": ["baselines:read"],
        "parameters": [{ "name": "baselinePublicId", "in": "query", "schema": { "type": "string" } }, { "name": "familyPublicId", "in": "query", "schema": { "type": "string" } }],
        "responses": { "200": { "description": "Baseline ou liste de versions" }, "401": { "$ref": "#/components/responses/AuthenticationError" }, "403": { "$ref": "#/components/responses/AuthenticationError" } }
      },
      "post": {
        "operationId": "importBaseline",
        "summary": "Créer ou importer une baseline comparable",
        "security": [{ "WorkspaceApiKey": [] }],
        "x-requiredScopes": ["baselines:write"],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "additionalProperties": false, "required": ["scanPublicId"], "properties": { "scanPublicId": { "type": "string" }, "monitorPublicId": { "type": "string" }, "name": { "type": "string" }, "familyPublicId": { "type": "string" }, "document": { "$ref": "#/components/schemas/BaselineTransfer" } } } } } },
        "responses": { "201": { "description": "Version immuable créée et baseline fixe activée si un moniteur est fourni" }, "401": { "$ref": "#/components/responses/AuthenticationError" }, "403": { "$ref": "#/components/responses/AuthenticationError" }, "422": { "$ref": "#/components/responses/InvalidRequest" } }
      }
    },
    "/api/v1/workflows/webhooks": {
      "get": { "operationId": "listWorkflowWebhooks", "summary": "Lister endpoints et livraisons de l’espace", "security": [{ "WorkspaceApiKey": [] }], "x-requiredScopes": ["webhooks:manage"], "responses": { "200": { "description": "Endpoints et cinquante dernières livraisons" }, "401": { "$ref": "#/components/responses/AuthenticationError" }, "403": { "$ref": "#/components/responses/AuthenticationError" } } },
      "post": { "operationId": "createWorkflowWebhook", "summary": "Créer un endpoint HTTPS signé", "security": [{ "WorkspaceApiKey": [] }], "x-requiredScopes": ["webhooks:manage"], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "additionalProperties": false, "required": ["name", "url", "events"], "properties": { "name": { "type": "string", "minLength": 3, "maxLength": 80 }, "url": { "type": "string", "format": "uri" }, "events": { "type": "array", "minItems": 1, "uniqueItems": true, "items": { "enum": ["scan.completed", "agent_test.completed", "regression.confirmed"] } } } } } } }, "responses": { "201": { "description": "Endpoint et secret affiché une fois" }, "401": { "$ref": "#/components/responses/AuthenticationError" }, "403": { "$ref": "#/components/responses/AuthenticationError" } } }
    },
    "/api/v1/workflows/webhooks/{endpointPublicId}": {
      "delete": { "operationId": "disableWorkflowWebhook", "summary": "Désactiver un endpoint", "security": [{ "WorkspaceApiKey": [] }], "x-requiredScopes": ["webhooks:manage"], "parameters": [{ "name": "endpointPublicId", "in": "path", "required": true, "schema": { "type": "string" } }], "responses": { "204": { "description": "Endpoint désactivé" }, "404": { "$ref": "#/components/responses/InvalidRequest" } } }
    },
    "/api/v1/workflows/webhooks/{endpointPublicId}/rotate": {
      "post": { "operationId": "rotateWorkflowWebhook", "summary": "Renouveler le secret de signature", "security": [{ "WorkspaceApiKey": [] }], "x-requiredScopes": ["webhooks:manage"], "parameters": [{ "name": "endpointPublicId", "in": "path", "required": true, "schema": { "type": "string" } }], "responses": { "200": { "description": "Nouveau secret affiché une fois" }, "404": { "$ref": "#/components/responses/InvalidRequest" } } }
    }
  },
  "components": {
    "securitySchemes": {
      "WorkspaceApiKey": { "type": "http", "scheme": "bearer", "bearerFormat": "obs_<publicId>_<secret>", "description": "Clé d'espace stockée sous forme de hash. Les scopes requis sont indiqués par opération." },
      "SessionCookie": { "type": "apiKey", "in": "cookie", "name": "obsero_session", "description": "Session de l'application ; les mutations exigent aussi le jeton CSRF." },
      "AnonymousScanClaim": { "type": "apiKey", "in": "cookie", "name": "obsero_scan_claim", "description": "Preuve HttpOnly signée, éphémère et limitée au scan public créé par ce navigateur. Elle n'autorise qu'un crédit agent anonyme sur 24 heures et ne remplace pas une clé d'espace pour les intégrations serveur." }
    },
    "responses": {
      "InvalidRequest": {
        "description": "Requête invalide",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "InternalError": {
        "description": "Erreur interne",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "AuthenticationError": { "description": "Clé absente, invalide ou scope insuffisant", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
    },
    "schemas": {
      "TaskTemplateTransfer": {
        "type": "object", "additionalProperties": false,
        "required": ["kind", "exportVersion", "familyPublicId", "version", "name", "actionType", "targetElementId", "objective", "startUrl", "allowedUrls", "allowedActions", "expectedOutcome", "budget", "source"],
        "properties": { "kind": { "const": "obsero.task-template" }, "exportVersion": { "const": 1 }, "familyPublicId": { "type": "string" }, "version": { "type": "integer" }, "name": { "type": "string" }, "actionType": { "enum": ["observe_element", "open_element", "enter_public_text", "select_local_choice"] }, "targetElementId": { "type": "string" }, "objective": { "type": "string" }, "startUrl": { "type": "string", "format": "uri" }, "allowedUrls": { "type": "array", "items": { "type": "string", "format": "uri" } }, "allowedActions": { "type": "array", "items": { "enum": ["observe", "click", "type", "press_enter", "navigate", "finish"] } }, "expectedOutcome": { "type": "object", "additionalProperties": true }, "budget": { "type": "object", "additionalProperties": true }, "source": { "type": "object", "additionalProperties": false, "required": ["engineVersion", "resultSchemaVersion"], "properties": { "engineVersion": { "type": "string" }, "resultSchemaVersion": { "type": "integer" } } } }
      },
      "BaselineTransfer": {
        "type": "object", "additionalProperties": false,
        "required": ["kind", "exportVersion", "familyPublicId", "version", "name", "normalizedUrl", "engineVersion", "resultSchemaVersion", "capabilities"],
        "properties": { "kind": { "const": "obsero.baseline" }, "exportVersion": { "const": 1 }, "familyPublicId": { "type": "string" }, "version": { "type": "integer" }, "name": { "type": "string" }, "normalizedUrl": { "type": "string", "format": "uri" }, "engineVersion": { "type": "string" }, "resultSchemaVersion": { "type": "integer" }, "capabilities": { "type": "object", "additionalProperties": true } }
      },
      "Error": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "required": ["type", "message"],
            "properties": {
              "type": { "type": "string" },
              "message": { "type": "string" }
            }
          }
        }
      },
      "ScanTaskInput": {
        "type": "object",
        "additionalProperties": false,
        "required": ["type"],
        "properties": {
          "type": { "enum": ["technical_diagnostic", "public_information", "navigate_information_page", "internal_search"] },
          "startUrl": { "type": "string", "format": "uri", "maxLength": 2048, "description": "Page de départ précise, sur le même hôte public que url." }
        }
      },
      "ScanTaskStep": {
        "type": "object",
        "additionalProperties": false,
        "required": ["ref", "action", "expected", "oracle"],
        "properties": {
          "ref": { "type": "string", "pattern": "^step-[1-9][0-9]*$" },
          "action": { "enum": ["fetch_public_page", "inspect_technical_foundation", "read_public_content", "inventory_internal_links", "open_internal_link", "inventory_search", "use_internal_search", "restore_search_state"] },
          "expected": { "type": "string" },
          "oracle": { "type": "object", "additionalProperties": false, "required": ["code", "visibleOutcomeRequired"], "properties": { "code": { "type": "string", "pattern": "^[a-z0-9_]+$" }, "visibleOutcomeRequired": { "type": "boolean" } } }
        }
      },
      "ScanTask": {
        "type": "object",
        "additionalProperties": false,
        "required": ["kind", "schemaVersion", "taskVersion", "type", "selection", "siteUrl", "startUrl", "objective", "steps", "relevantCapabilities", "scenarioIds", "allowedActions", "forbiddenActions", "safety"],
        "properties": {
          "kind": { "const": "obsero.scan-task" },
          "schemaVersion": { "const": 1 },
          "taskVersion": { "type": "string" },
          "type": { "enum": ["technical_diagnostic", "public_information", "navigate_information_page", "internal_search"] },
          "selection": { "type": "object", "additionalProperties": false, "required": ["state", "source"], "properties": { "state": { "enum": ["selected", "not_measured"] }, "source": { "enum": ["user", "api_default", "monitor", "replay", "legacy_adapter"] } } },
          "siteUrl": { "type": "string", "format": "uri" },
          "startUrl": { "type": "string", "format": "uri" },
          "objective": { "type": "object", "additionalProperties": false, "required": ["code", "expectedOutcome"], "properties": { "code": { "type": "string", "pattern": "^[a-z0-9_]+$" }, "expectedOutcome": { "type": "string" } } },
          "steps": { "type": "array", "minItems": 1, "maxItems": 8, "items": { "$ref": "#/components/schemas/ScanTaskStep" } },
          "relevantCapabilities": { "type": "array", "uniqueItems": true, "items": { "enum": ["reading", "navigation", "action"] } },
          "scenarioIds": { "type": "array", "uniqueItems": true, "items": { "enum": ["open_information_page", "internal_search", "prepare_form", "modern_interactions", "ecommerce_product"] } },
          "allowedActions": { "type": "array", "minItems": 1, "items": { "type": "string" } },
          "forbiddenActions": { "type": "array", "minItems": 1, "items": { "type": "string" } },
          "safety": { "type": "object", "additionalProperties": false, "required": ["policy", "allowedMethods", "emittedMutativeRequests", "irreversibleActionsAllowed"], "properties": { "policy": { "const": "read_only" }, "allowedMethods": { "type": "array", "minItems": 2, "maxItems": 2, "prefixItems": [{ "const": "GET" }, { "const": "HEAD" }], "items": false }, "emittedMutativeRequests": { "const": 0 }, "irreversibleActionsAllowed": { "const": false } } }
        }
      },
      "ScanReplay": {
        "type": "object",
        "additionalProperties": false,
        "required": ["publicId", "createdAt", "normalizedUrl", "domain", "task", "replayOf", "links"],
        "properties": {
          "publicId": { "type": "string" },
          "createdAt": { "type": "string", "format": "date-time" },
          "normalizedUrl": { "type": "string", "format": "uri" },
          "domain": { "type": "string" },
          "task": { "$ref": "#/components/schemas/ScanTask" },
          "replayOf": { "type": "object", "additionalProperties": false, "required": ["publicId"], "properties": { "publicId": { "type": "string" } } },
          "links": { "type": "object", "additionalProperties": false, "required": ["resume", "source", "result"], "properties": { "resume": { "type": "string" }, "source": { "type": "string" }, "result": { "type": "string" } } }
        }
      },
      "Scan": {
        "type": "object",
        "required": ["apiVersion", "product", "publicId", "createdAt", "url", "domain", "task", "replayOf", "score", "rating", "durationMs", "success", "ownershipVerified", "resultContract", "browserProcessing", "p3Processing", "p3", "result", "verdict", "checks", "recommendations", "pageSamples", "artifacts", "links"],
        "properties": {
          "apiVersion": { "const": "v1" },
          "product": { "const": "Obsero" },
          "publicId": { "type": "string" },
          "createdAt": { "type": "string", "format": "date-time" },
          "url": { "type": "string", "format": "uri" },
          "domain": { "type": "string" },
          "task": { "$ref": "#/components/schemas/ScanTask" },
          "replayOf": { "oneOf": [{ "type": "null" }, { "type": "object", "additionalProperties": false, "required": ["publicId"], "properties": { "publicId": { "type": "string" } } }] },
          "score": { "type": "integer", "minimum": 0, "maximum": 100, "description": "Champ historique : score de fondation technique, jamais une preuve de navigation ou d'action." },
          "rating": { "type": "string" },
          "durationMs": { "type": "integer", "minimum": 0 },
          "httpStatus": { "type": ["integer", "null"] },
          "success": { "type": "boolean" },
          "errorType": { "type": ["string", "null"] },
          "ownershipVerified": { "type": "boolean", "description": "Vrai uniquement si le compte demandeur contrôlait ce domaine au moment de l'association ; ne change ni le score ni le droit de scanner." },
          "resultContract": { "$ref": "#/components/schemas/P4Result" },
          "browserProcessing": { "$ref": "#/components/schemas/BrowserProcessingState" },
          "p3Processing": { "$ref": "#/components/schemas/P3ProcessingState" },
          "p3": { "oneOf": [{ "type": "null" }, { "$ref": "#/components/schemas/P3PublicReport" }] },
          "result": { "$ref": "#/components/schemas/ScanResult" },
          "verdict": {
            "type": "object",
            "deprecated": true,
            "description": "Résumé historique de fondation conservé pour la compatibilité de l'API v1. Utiliser resultContract.deterministic pour toute conclusion.",
            "required": ["status", "score", "blockingIssues", "optimizations", "summary"],
            "properties": {
              "status": { "enum": ["compatible", "partial", "insufficient"] },
              "score": { "type": "integer" },
              "blockingIssues": { "type": "array", "items": { "$ref": "#/components/schemas/Check" } },
              "optimizations": { "type": "array", "items": { "$ref": "#/components/schemas/Check" } },
              "summary": { "type": "string" },
              "userFacingVerdict": { "type": "object", "additionalProperties": true }
            }
          },
          "checks": { "type": "array", "items": { "$ref": "#/components/schemas/Check" } },
          "recommendations": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/P4Recommendation" },
            "description": "Même liste que resultContract.deterministic.recommendations. Vide lors d'une panne Obsero, d'un résultat non testé ou non applicable."
          },
          "pageSamples": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
          "artifacts": { "type": "array", "items": { "$ref": "#/components/schemas/ScanArtifact" } },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": ["human", "json", "evidence", "replay", "result", "api"],
            "properties": {
              "human": { "type": "string" },
              "json": { "type": "string" },
              "evidence": { "type": "string" },
              "replay": { "type": ["string", "null"] },
              "result": { "type": "string" },
              "api": { "type": "string" }
            }
          }
        }
      },
      "Check": {
        "type": "object",
        "required": ["id", "label", "score", "maxScore", "status", "message"],
        "properties": {
          "id": { "type": "string" },
          "label": { "type": "string" },
          "score": { "type": "integer" },
          "maxScore": { "type": "integer" },
          "status": { "enum": ["pass", "partial", "fail", "warning"] },
          "message": { "type": "string" },
          "details": { "type": "object", "additionalProperties": true }
        }
      },
      "LocalizedText": {
        "type": "object",
        "additionalProperties": false,
        "required": ["fr", "en"],
        "properties": { "fr": { "type": "string" }, "en": { "type": "string" } }
      },
      "P4ResultState": {
        "enum": ["works", "blocked", "not_tested", "not_applicable", "obsero_failure"]
      },
      "P4Evidence": {
        "type": "object",
        "additionalProperties": false,
        "required": ["ref", "capability", "kind", "executed", "observed", "summary"],
        "properties": {
          "ref": { "type": "string" },
          "capability": { "enum": ["reading", "navigation", "action", "ecommerce"] },
          "kind": { "enum": ["execution", "observation", "limit"] },
          "executed": { "type": "boolean" },
          "observed": { "type": "boolean" },
          "summary": { "$ref": "#/components/schemas/LocalizedText" }
        }
      },
      "P4BlockingStep": {
        "type": "object",
        "additionalProperties": false,
        "required": ["ref", "capability", "testRef", "label", "observation", "evidenceRefs"],
        "properties": {
          "ref": { "type": "string" },
          "capability": { "enum": ["reading", "navigation", "action", "ecommerce"] },
          "testRef": { "type": "string" },
          "label": { "$ref": "#/components/schemas/LocalizedText" },
          "observation": { "$ref": "#/components/schemas/LocalizedText" },
          "evidenceRefs": { "type": "array", "minItems": 1, "items": { "type": "string" } }
        }
      },
      "P4TestOutcome": {
        "type": "object",
        "additionalProperties": false,
        "required": ["ref", "capability", "label", "state", "executed", "applicable", "expected", "observed", "summary", "evidenceRefs", "blockingStepRef"],
        "properties": {
          "ref": { "type": "string" },
          "capability": { "enum": ["reading", "navigation", "action", "ecommerce"] },
          "label": { "$ref": "#/components/schemas/LocalizedText" },
          "state": { "$ref": "#/components/schemas/P4ResultState" },
          "executed": { "type": "boolean" },
          "applicable": { "type": ["boolean", "null"] },
          "expected": { "oneOf": [{ "type": "null" }, { "$ref": "#/components/schemas/LocalizedText" }] },
          "observed": { "oneOf": [{ "type": "null" }, { "$ref": "#/components/schemas/LocalizedText" }] },
          "summary": { "$ref": "#/components/schemas/LocalizedText" },
          "evidenceRefs": { "type": "array", "items": { "type": "string" } },
          "blockingStepRef": { "type": ["string", "null"] }
        }
      },
      "P4CapabilityOutcome": {
        "type": "object",
        "additionalProperties": false,
        "required": ["id", "state", "tested", "summary", "testRefs", "evidenceRefs", "firstBlockingStepRef"],
        "properties": {
          "id": { "enum": ["reading", "navigation", "action", "ecommerce"] },
          "state": { "$ref": "#/components/schemas/P4ResultState" },
          "tested": { "type": "boolean" },
          "summary": { "$ref": "#/components/schemas/LocalizedText" },
          "testRefs": { "type": "array", "items": { "type": "string" } },
          "evidenceRefs": { "type": "array", "items": { "type": "string" } },
          "firstBlockingStepRef": { "type": ["string", "null"] }
        }
      },
      "P4TaskContext": {
        "type": "object",
        "additionalProperties": false,
        "required": ["ref", "type", "version", "selectionState", "objective", "siteUrl", "startUrl", "observedPages", "executionContext", "expectedOutcome", "allowedActions", "forbiddenActions", "plannedStepRefs"],
        "properties": {
          "ref": { "const": "task.primary" },
          "type": { "enum": ["technical_diagnostic", "public_information", "navigate_information_page", "internal_search"] },
          "version": { "type": "string" },
          "selectionState": { "enum": ["selected", "not_measured"] },
          "objective": { "$ref": "#/components/schemas/LocalizedText" },
          "siteUrl": { "type": "string", "format": "uri" },
          "startUrl": { "type": "string", "format": "uri" },
          "observedPages": { "type": "array", "items": { "type": "string", "format": "uri" } },
          "executionContext": { "type": "object", "additionalProperties": false, "required": ["surface", "viewport", "networkPolicy"], "properties": { "surface": { "enum": ["http_and_browser", "http_only"] }, "viewport": { "enum": ["desktop", null] }, "networkPolicy": { "const": "read_only" } } },
          "expectedOutcome": { "$ref": "#/components/schemas/LocalizedText" },
          "allowedActions": { "type": "array", "items": { "type": "string" } },
          "forbiddenActions": { "type": "array", "items": { "type": "string" } },
          "plannedStepRefs": { "type": "array", "minItems": 1, "items": { "type": "string" } }
        }
      },
      "P4JourneyStep": {
        "type": "object",
        "additionalProperties": false,
        "required": ["ref", "taskStepRef", "index", "action", "state", "executed", "expected", "observed", "evidenceRefs", "blockingStepRef"],
        "properties": {
          "ref": { "type": "string" },
          "taskStepRef": { "type": "string" },
          "index": { "type": "integer", "minimum": 1 },
          "action": { "$ref": "#/components/schemas/LocalizedText" },
          "state": { "enum": ["works", "blocked", "not_executed", "restored", "not_applicable", "obsero_failure"] },
          "executed": { "type": "boolean" },
          "expected": { "$ref": "#/components/schemas/LocalizedText" },
          "observed": { "oneOf": [{ "type": "null" }, { "$ref": "#/components/schemas/LocalizedText" }] },
          "evidenceRefs": { "type": "array", "items": { "type": "string" } },
          "blockingStepRef": { "type": ["string", "null"] }
        }
      },
      "P4Journey": {
        "type": "object",
        "additionalProperties": false,
        "required": ["taskRef", "steps", "firstBlockingStepRef", "primaryEvidenceRef", "primaryRecommendationRef"],
        "properties": {
          "taskRef": { "const": "task.primary" },
          "steps": { "type": "array", "minItems": 1, "items": { "$ref": "#/components/schemas/P4JourneyStep" } },
          "firstBlockingStepRef": { "type": ["string", "null"] },
          "primaryEvidenceRef": { "type": ["string", "null"] },
          "primaryRecommendationRef": { "type": ["string", "null"] }
        }
      },
      "P4Recommendation": {
        "type": "object",
        "additionalProperties": false,
        "required": ["ref", "capability", "blockingStepRef", "evidenceRefs", "title", "observation", "consequence", "correction", "verification", "probableOwner"],
        "properties": {
          "ref": { "type": "string" },
          "capability": { "enum": ["reading", "navigation", "action", "ecommerce"] },
          "blockingStepRef": { "type": "string" },
          "evidenceRefs": { "type": "array", "minItems": 1, "items": { "type": "string" } },
          "title": { "$ref": "#/components/schemas/LocalizedText" },
          "observation": { "$ref": "#/components/schemas/LocalizedText" },
          "consequence": { "$ref": "#/components/schemas/LocalizedText" },
          "correction": {
            "type": "object",
            "additionalProperties": false,
            "required": ["fr", "en"],
            "properties": {
              "fr": { "type": "array", "minItems": 1, "items": { "type": "string" } },
              "en": { "type": "array", "minItems": 1, "items": { "type": "string" } }
            }
          },
          "verification": { "$ref": "#/components/schemas/LocalizedText" },
          "probableOwner": { "enum": ["content", "front_end", "infrastructure", "accessibility", null] }
        }
      },
      "P4AgentTest": {
        "type": "object",
        "additionalProperties": false,
        "required": ["requested", "status", "task", "model", "budget", "usage", "trace", "limits"],
        "properties": {
          "requested": { "const": true },
          "status": { "type": "string" },
          "task": { "type": "object", "additionalProperties": false, "required": ["type", "version", "objective"], "properties": { "type": { "type": "string" }, "version": { "type": "string" }, "objective": { "type": "string" } } },
          "model": { "type": "object", "additionalProperties": false, "required": ["provider", "name", "version"], "properties": { "provider": { "type": "string" }, "name": { "type": "string" }, "version": { "type": "string" } } },
          "budget": {
            "type": "object",
            "additionalProperties": false,
            "required": ["maxSteps", "maxDurationMs", "maxInputTokens", "maxOutputTokens", "maxEstimatedCostMicros"],
            "properties": {
              "maxSteps": { "type": ["integer", "null"], "minimum": 0 },
              "maxDurationMs": { "type": ["integer", "null"], "minimum": 0 },
              "maxInputTokens": { "type": ["integer", "null"], "minimum": 0 },
              "maxOutputTokens": { "type": ["integer", "null"], "minimum": 0 },
              "maxEstimatedCostMicros": { "type": ["integer", "null"], "minimum": 0 }
            }
          },
          "usage": { "type": "object", "additionalProperties": false, "required": ["inputTokens", "outputTokens", "estimatedCostMicros"], "properties": { "inputTokens": { "type": "integer", "minimum": 0 }, "outputTokens": { "type": "integer", "minimum": 0 }, "estimatedCostMicros": { "type": "integer", "minimum": 0 } } },
          "trace": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": ["index", "status", "action", "observation", "durationMs", "errorType"],
              "properties": {
                "index": { "type": "integer", "minimum": 0 },
                "status": { "type": "string" },
                "action": { "type": "object", "additionalProperties": false, "required": ["type"], "properties": { "type": { "type": "string" }, "elementId": { "type": "string" }, "elementName": { "type": "string" }, "textLength": { "type": "integer", "minimum": 0 }, "url": { "type": "string" } } },
                "observation": { "type": "object", "additionalProperties": false, "required": ["url", "title", "textLength", "controlCount"], "properties": { "url": { "type": "string" }, "title": { "type": ["string", "null"] }, "textLength": { "type": "integer", "minimum": 0 }, "controlCount": { "type": "integer", "minimum": 0 } } },
                "durationMs": { "type": "integer", "minimum": 0 },
                "errorType": { "type": ["string", "null"] }
              }
            }
          },
          "limits": { "type": "array", "items": { "$ref": "#/components/schemas/LocalizedText" } }
        }
      },
      "P4Result": {
        "type": "object",
        "additionalProperties": false,
        "required": ["contract", "schemaVersion", "engineVersion", "task", "links", "previousResult", "deterministic", "agentTest"],
        "properties": {
          "contract": { "const": "obsero.result" },
          "schemaVersion": { "const": 2 },
          "engineVersion": { "const": "obsero-p4-2.2.0" },
          "task": { "$ref": "#/components/schemas/P4TaskContext" },
          "links": { "type": "object", "additionalProperties": false, "required": ["human", "json", "evidence", "replay"], "properties": { "human": { "type": ["string", "null"] }, "json": { "type": ["string", "null"] }, "evidence": { "type": ["string", "null"] }, "replay": { "type": ["string", "null"] } } },
          "previousResult": { "oneOf": [{ "type": "null" }, { "type": "object", "additionalProperties": false, "required": ["publicId"], "properties": { "publicId": { "type": "string" } } }] },
          "deterministic": {
            "type": "object",
            "additionalProperties": false,
            "required": ["conclusion", "capabilities", "journey", "tests", "blockingSteps", "evidence", "recommendations", "limits", "technicalFoundation"],
            "properties": {
              "conclusion": { "type": "object", "additionalProperties": false, "required": ["state", "summary", "firstBlockingStepRef", "recommendationRef"], "properties": { "state": { "$ref": "#/components/schemas/P4ResultState" }, "summary": { "$ref": "#/components/schemas/LocalizedText" }, "firstBlockingStepRef": { "type": ["string", "null"] }, "recommendationRef": { "type": ["string", "null"] } } },
              "capabilities": { "type": "object", "additionalProperties": false, "required": ["reading", "navigation", "action", "ecommerce"], "properties": { "reading": { "$ref": "#/components/schemas/P4CapabilityOutcome" }, "navigation": { "$ref": "#/components/schemas/P4CapabilityOutcome" }, "action": { "$ref": "#/components/schemas/P4CapabilityOutcome" }, "ecommerce": { "$ref": "#/components/schemas/P4CapabilityOutcome" } } },
              "journey": { "$ref": "#/components/schemas/P4Journey" },
              "tests": { "type": "array", "items": { "$ref": "#/components/schemas/P4TestOutcome" } },
              "blockingSteps": { "type": "array", "items": { "$ref": "#/components/schemas/P4BlockingStep" } },
              "evidence": { "type": "array", "items": { "$ref": "#/components/schemas/P4Evidence" } },
              "recommendations": { "type": "array", "items": { "$ref": "#/components/schemas/P4Recommendation" } },
              "limits": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["ref", "summary"], "properties": { "ref": { "type": "string" }, "summary": { "$ref": "#/components/schemas/LocalizedText" } } } },
              "technicalFoundation": { "type": "object", "additionalProperties": false, "required": ["measured", "score", "maximum", "statement"], "properties": { "measured": { "type": "boolean" }, "score": { "type": ["integer", "null"], "minimum": 0, "maximum": 100 }, "maximum": { "const": 100 }, "statement": { "$ref": "#/components/schemas/LocalizedText" } } }
            }
          },
          "agentTest": { "oneOf": [{ "type": "null" }, { "$ref": "#/components/schemas/P4AgentTest" }] }
        }
      },
      "ScanResult": {
        "oneOf": [
          { "$ref": "#/components/schemas/ScanResultV2" },
          { "$ref": "#/components/schemas/ScanResultV3" },
          { "$ref": "#/components/schemas/ScanResultV4" },
          { "$ref": "#/components/schemas/ScanResultV5" }
        ],
        "discriminator": { "propertyName": "schemaVersion" }
      },
      "ScanResultV2": {
        "type": "object",
        "deprecated": true,
        "required": ["schemaVersion", "engineVersion", "technicalFoundation", "capabilities", "coverage", "evidence", "scenarios", "limitations", "browserProcessing", "interfaceMap"],
        "properties": {
          "schemaVersion": { "const": 2 },
          "engineVersion": { "type": "string" },
          "technicalFoundation": {
            "type": "object",
            "required": ["label", "score", "maxScore", "rating", "groups"],
            "properties": {
              "label": { "const": "Fondation technique" },
              "score": { "type": "integer", "minimum": 0, "maximum": 100 },
              "maxScore": { "const": 100 },
              "rating": { "type": "string" },
              "groups": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": ["id", "label", "checkIds"],
                  "properties": {
                    "id": { "enum": ["access", "discovery", "understanding"] },
                    "label": { "type": "string" },
                    "checkIds": { "type": "array", "items": { "type": "string" } }
                  }
                }
              }
            }
          },
          "capabilities": {
            "type": "object",
            "required": ["reading", "navigation", "action"],
            "properties": {
              "reading": { "$ref": "#/components/schemas/CapabilityAssessment" },
              "navigation": { "$ref": "#/components/schemas/CapabilityAssessment" },
              "action": { "$ref": "#/components/schemas/CapabilityAssessment" }
            }
          },
          "coverage": {
            "type": "object",
            "required": ["capabilities", "scenarios"],
            "properties": {
              "capabilities": {
                "type": "object",
                "required": ["total", "tested", "notTested"],
                "properties": {
                  "total": { "const": 3 },
                  "tested": { "type": "integer", "minimum": 0 },
                  "notTested": { "type": "integer", "minimum": 0 }
                }
              },
              "scenarios": {
                "type": "object",
                "required": ["detected", "executed", "succeeded"],
                "properties": {
                  "detected": { "type": "integer", "minimum": 0 },
                  "executed": { "type": "integer", "minimum": 0 },
                  "succeeded": { "type": "integer", "minimum": 0 }
                }
              }
            }
          },
          "evidence": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["id", "kind", "capability", "source", "summary"],
              "properties": {
                "id": { "type": "string" },
                "kind": { "enum": ["observed", "inferred"] },
                "capability": { "enum": ["reading", "navigation", "action"] },
                "source": { "enum": ["http_response", "html_extraction", "static_analysis", "browser_render", "browser_inventory", "browser_scenario"] },
                "summary": { "type": "string" },
                "data": { "type": "object", "additionalProperties": true }
              }
            }
          },
          "scenarios": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
          "limitations": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["code", "summary"],
              "properties": {
                "code": { "enum": ["static_html_only", "browser_unavailable", "browser_pending", "browser_failed", "browser_disabled", "browser_inventory_only", "no_browser_scenario", "no_irreversible_actions"] },
                "summary": { "type": "string" }
              }
            }
          },
          "browserProcessing": { "$ref": "#/components/schemas/BrowserProcessingState" },
          "interfaceMap": {
            "oneOf": [
              { "type": "null" },
              { "$ref": "#/components/schemas/BrowserInterfaceMap" }
            ]
          }
        }
      },
      "ScanResultV3": {
        "type": "object",
        "deprecated": true,
        "description": "Inventaire d’interface et scénarios déterministes de navigation, recherche et préparation/réinitialisation de formulaire, avec preuves expurgées.",
        "required": ["schemaVersion", "engineVersion", "technicalFoundation", "capabilities", "coverage", "evidence", "scenarios", "limitations", "browserProcessing", "interfaceMap"],
        "properties": {
          "schemaVersion": { "const": 3 },
          "engineVersion": { "type": "string" },
          "technicalFoundation": { "type": "object", "additionalProperties": true },
          "capabilities": {
            "type": "object",
            "required": ["reading", "navigation", "action"],
            "properties": {
              "reading": { "$ref": "#/components/schemas/CapabilityAssessment" },
              "navigation": { "$ref": "#/components/schemas/CapabilityAssessment" },
              "action": { "$ref": "#/components/schemas/CapabilityAssessment" }
            }
          },
          "coverage": { "type": "object", "additionalProperties": true },
          "evidence": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
          "scenarios": { "type": "array", "items": { "$ref": "#/components/schemas/BrowserScenarioResult" } },
          "limitations": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
          "browserProcessing": { "$ref": "#/components/schemas/BrowserProcessingState" },
          "interfaceMap": {
            "oneOf": [
              { "type": "null" },
              { "$ref": "#/components/schemas/BrowserInterfaceMap" }
            ]
          }
        }
      },
      "ScanResultV4": {
        "type": "object",
        "description": "Découverte passive et additive des interfaces structurées explicitement publiées. Une déclaration ne constitue jamais une exécution et ne modifie pas les résultats déterministes ou agent.",
        "required": ["schemaVersion", "engineVersion", "technicalFoundation", "capabilities", "coverage", "evidence", "scenarios", "limitations", "browserProcessing", "interfaceMap", "structuredInterfaces"],
        "properties": {
          "schemaVersion": { "const": 4 },
          "engineVersion": { "type": "string" },
          "technicalFoundation": { "type": "object", "additionalProperties": true },
          "capabilities": {
            "type": "object",
            "required": ["reading", "navigation", "action"],
            "properties": {
              "reading": { "$ref": "#/components/schemas/CapabilityAssessment" },
              "navigation": { "$ref": "#/components/schemas/CapabilityAssessment" },
              "action": { "$ref": "#/components/schemas/CapabilityAssessment" }
            }
          },
          "coverage": { "type": "object", "additionalProperties": true },
          "evidence": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
          "scenarios": { "type": "array", "items": { "$ref": "#/components/schemas/BrowserScenarioResult" } },
          "limitations": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
          "browserProcessing": { "$ref": "#/components/schemas/BrowserProcessingState" },
          "interfaceMap": {
            "oneOf": [
              { "type": "null" },
              { "$ref": "#/components/schemas/BrowserInterfaceMap" }
            ]
          },
          "structuredInterfaces": { "$ref": "#/components/schemas/StructuredInterfacesResult" }
        }
      },
      "ScanResultV5": {
        "type": "object",
        "description": "Résultat courant ajoutant la preuve e-commerce déterministe sans transaction.",
        "required": ["schemaVersion", "engineVersion", "technicalFoundation", "capabilities", "coverage", "evidence", "scenarios", "limitations", "browserProcessing", "interfaceMap", "structuredInterfaces"],
        "properties": {
          "schemaVersion": { "const": 5 },
          "engineVersion": { "type": "string" },
          "technicalFoundation": { "type": "object", "additionalProperties": true },
          "capabilities": {
            "type": "object",
            "required": ["reading", "navigation", "action", "ecommerce"],
            "properties": {
              "reading": { "$ref": "#/components/schemas/CapabilityAssessment" },
              "navigation": { "$ref": "#/components/schemas/CapabilityAssessment" },
              "action": { "$ref": "#/components/schemas/CapabilityAssessment" },
              "ecommerce": { "$ref": "#/components/schemas/CapabilityAssessment" }
            }
          },
          "coverage": {
            "type": "object",
            "required": ["capabilities", "scenarios"],
            "properties": {
              "capabilities": { "type": "object", "required": ["total", "tested", "notTested"], "properties": { "total": { "const": 4 }, "tested": { "type": "integer", "minimum": 0, "maximum": 4 }, "notTested": { "type": "integer", "minimum": 0, "maximum": 4 } } },
              "scenarios": { "type": "object", "required": ["detected", "executed", "succeeded"], "properties": { "detected": { "type": "integer", "minimum": 0 }, "executed": { "type": "integer", "minimum": 0 }, "succeeded": { "type": "integer", "minimum": 0 } } }
            }
          },
          "evidence": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
          "scenarios": { "type": "array", "items": { "$ref": "#/components/schemas/BrowserScenarioResult" } },
          "limitations": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
          "browserProcessing": { "$ref": "#/components/schemas/BrowserProcessingState" },
          "interfaceMap": { "oneOf": [{ "type": "null" }, { "$ref": "#/components/schemas/BrowserInterfaceMap" }] },
          "structuredInterfaces": { "$ref": "#/components/schemas/StructuredInterfacesResult" },
          "networkSafety": { "$ref": "#/components/schemas/NetworkSafetyEvidence" }
        }
      },
      "NetworkSafetyEvidence": {
        "type": "object",
        "additionalProperties": false,
        "required": ["policy", "allowedMethods", "emittedMutativeRequests", "observedBlockedMutativeRequests"],
        "properties": {
          "policy": { "const": "read_only" },
          "allowedMethods": { "type": "array", "prefixItems": [{ "const": "GET" }, { "const": "HEAD" }], "minItems": 2, "maxItems": 2 },
          "emittedMutativeRequests": { "const": 0 },
          "observedBlockedMutativeRequests": { "type": "integer", "minimum": 0 }
        }
      },
      "StructuredInterfacesResult": {
        "type": "object",
        "additionalProperties": false,
        "required": ["status", "summary", "declarations"],
        "properties": {
          "status": { "enum": ["present", "invalid", "absent", "not_tested"] },
          "summary": { "type": "string", "maxLength": 500 },
          "declarations": { "type": "array", "maxItems": 8, "items": { "$ref": "#/components/schemas/StructuredInterfaceDeclaration" } }
        }
      },
      "StructuredInterfaceDeclaration": {
        "type": "object",
        "additionalProperties": false,
        "description": "Contrat explicitement déclaré. executed reste toujours false pendant un scan public.",
        "required": ["id", "kind", "source", "relation", "url", "mediaType", "declaredVersion", "experimental", "validation", "title", "operations", "issues", "executed"],
        "properties": {
          "id": { "type": "string", "pattern": "^structured-interface-[1-8]$" },
          "kind": { "enum": ["openapi", "api", "mcp", "webmcp", "ucp", "unknown"] },
          "source": { "enum": ["http_link", "html_link", "html_meta", "web_app_manifest", "robots_txt", "well_known_ucp"] },
          "relation": { "type": "string", "maxLength": 80 },
          "url": { "type": "string", "format": "uri", "maxLength": 2048 },
          "mediaType": { "type": ["string", "null"], "maxLength": 120 },
          "declaredVersion": { "type": ["string", "null"], "maxLength": 80 },
          "experimental": { "type": "boolean" },
          "discoveryMethod": { "enum": ["GET", "POST"], "description": "Méthode réseau utilisée uniquement pour lire le contrat ou le catalogue MCP." },
          "discoveryOperation": { "enum": ["contract_fetch", "tools/list"], "description": "Opération de découverte bornée ; aucune fonction métier n'est exécutée." },
          "validation": { "enum": ["valid", "invalid", "unverified", "unknown"] },
          "title": { "type": ["string", "null"], "maxLength": 160 },
          "operations": { "type": "array", "maxItems": 40, "items": { "$ref": "#/components/schemas/StructuredOperation" } },
          "issues": { "type": "array", "maxItems": 20, "items": { "type": "string", "maxLength": 120 } },
          "executed": { "const": false }
        }
      },
      "StructuredOperation": {
        "type": "object",
        "additionalProperties": false,
        "description": "Description statique bornée ; elle ne prouve ni l'autorisation ni la réussite de l'opération.",
        "required": ["name", "description", "kind", "method", "inputSchema", "requiredFields", "confirmationRequired", "riskSignals"],
        "properties": {
          "name": { "type": "string", "maxLength": 160 },
          "description": { "type": ["string", "null"], "maxLength": 280 },
          "kind": { "enum": ["read", "action", "unknown"] },
          "method": { "type": ["string", "null"], "enum": ["GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH", "DELETE", "TRACE", null] },
          "inputSchema": { "enum": ["valid", "missing", "invalid", "unknown"] },
          "requiredFields": { "type": "array", "maxItems": 20, "items": { "type": "string" } },
          "confirmationRequired": { "type": "boolean" },
          "riskSignals": { "type": "array", "uniqueItems": true, "items": { "enum": ["instruction_override", "secret_request", "prompt_extraction", "tool_escalation"] } }
        }
      },
      "TargetTestId": {
        "enum": ["public_information", "open_internal_page", "internal_search", "desktop_menu", "mobile_menu", "breadcrumb", "tabs", "accordion", "dialog", "pagination", "load_more", "spa_route", "language_selector", "autocomplete", "reversible_filter", "sort", "zero_results", "category_to_detail", "content_comparison", "prepare_reset_form", "local_validation", "multi_step_form", "local_choice", "cookie_banner", "captcha", "paywall", "login_wall", "anti_bot", "slow_dynamic_content"]
      },
      "ScanTestPlan": {
        "type": "object", "additionalProperties": false, "required": ["schemaVersion", "generatedAt", "pages", "limits", "tests", "forbiddenActions"],
        "properties": {
          "schemaVersion": { "const": 1 }, "generatedAt": { "type": "string", "format": "date-time" },
          "pages": { "type": "array", "maxItems": 6, "items": { "type": "object", "required": ["url", "role"], "properties": { "url": { "type": "string", "format": "uri" }, "role": { "enum": ["home", "content", "listing", "detail", "search", "form", "other"] } } } },
          "limits": { "type": "object", "required": ["pageCount", "agentCreditsIfAllEligible", "methods"], "properties": { "pageCount": { "type": "integer", "minimum": 0, "maximum": 6 }, "agentCreditsIfAllEligible": { "type": "integer", "minimum": 0 }, "methods": { "type": "array", "prefixItems": [{ "const": "GET" }, { "const": "HEAD" }], "items": false } } },
          "tests": { "type": "array", "minItems": 29, "maxItems": 29, "items": { "type": "object", "required": ["id", "suite", "applicability", "executionStatus", "pageUrl", "targetElementId", "accessibleName", "reason", "deterministicAvailable", "agentEligible", "creditCost"], "properties": { "id": { "$ref": "#/components/schemas/TargetTestId" }, "suite": { "enum": ["essential", "navigation", "catalog", "form", "obstacle"] }, "applicability": { "enum": ["applicable", "not_applicable", "forbidden"] }, "executionStatus": { "enum": ["passed", "partial", "blocked", "not_tested"] }, "pageUrl": { "type": ["string", "null"] }, "targetElementId": { "type": ["string", "null"] }, "accessibleName": { "type": ["string", "null"] }, "reason": { "type": "string" }, "deterministicAvailable": { "type": "boolean" }, "agentEligible": { "type": "boolean" }, "creditCost": { "type": "integer", "minimum": 0, "maximum": 1 } } } },
          "forbiddenActions": { "type": "array", "items": { "type": "string" } }
        }
      },
      "BrowserScenarioResult": {
        "type": "object",
        "required": ["id", "capability", "status", "startedUrl", "finalUrl", "durationMs", "steps", "summary"],
        "properties": {
          "id": { "enum": ["open_information_page", "internal_search", "prepare_form", "modern_interactions", "ecommerce_product"] },
          "version": { "type": "string", "maxLength": 40, "description": "Version stable de la définition de scénario ; absente sur certains scans schemaVersion 3 antérieurs." },
          "capability": { "enum": ["navigation", "action", "ecommerce"] },
          "status": { "enum": ["passed", "partial", "blocked", "not_applicable"] },
          "startedUrl": { "type": "string", "format": "uri" },
          "finalUrl": { "type": "string", "format": "uri" },
          "durationMs": { "type": "integer", "minimum": 0, "maximum": 22000 },
          "steps": { "type": "array", "maxItems": 40, "items": { "$ref": "#/components/schemas/BrowserScenarioStep" } },
          "summary": { "type": "string" },
          "evidence": {
            "oneOf": [
              { "$ref": "#/components/schemas/SearchScenarioEvidence" },
              { "$ref": "#/components/schemas/FormScenarioEvidence" },
              { "$ref": "#/components/schemas/ModernScenarioEvidence" },
              { "$ref": "#/components/schemas/EcommerceBrowserScenarioEvidence" }
            ]
          }
        }
      },
      "SearchScenarioEvidence": {
        "type": "object",
        "required": ["query", "resultSignal", "resultLinkCount"],
        "properties": {
          "query": { "type": ["string", "null"], "maxLength": 24, "description": "Requête synthétique issue du contenu public, bornée et expurgée." },
          "resultSignal": { "type": ["string", "null"], "enum": ["url_change", "named_region", "new_links", "no_results", "ambiguous_change", null] },
          "resultLinkCount": { "type": ["integer", "null"], "minimum": 0, "maximum": 100 }
        }
      },
      "FormScenarioEvidence": {
        "type": "object",
        "description": "Compteurs bornés du formulaire ; aucune valeur synthétique saisie n'est exposée.",
        "required": ["fieldCount", "filledFieldCount", "validationErrorCount", "domSubmitBlockedCount", "outboundRequestBlockedCount", "mutativeRequestBlockedCount", "resetVerified"],
        "properties": {
          "fieldCount": { "type": ["integer", "null"], "minimum": 0, "maximum": 12 },
          "filledFieldCount": { "type": ["integer", "null"], "minimum": 0, "maximum": 12 },
          "validationErrorCount": { "type": ["integer", "null"], "minimum": 0, "maximum": 12 },
          "domSubmitBlockedCount": { "type": "integer", "minimum": 0, "maximum": 100 },
          "outboundRequestBlockedCount": { "type": "integer", "minimum": 0, "maximum": 150, "description": "Requêtes réseau bloquées par la politique du scénario, méthodes sûres et mutatives confondues." },
          "mutativeRequestBlockedCount": { "type": "integer", "minimum": 0, "maximum": 150 },
          "resetVerified": { "type": ["boolean", "null"] }
        }
      },
      "ModernScenarioEvidence": {
        "type": "object",
        "additionalProperties": false,
        "required": ["targetResults"],
        "properties": { "targetResults": { "type": "array", "maxItems": 29, "items": { "$ref": "#/components/schemas/TargetTestExecution" } } }
      },
      "EcommerceBrowserScenarioEvidence": {
        "type": "object",
        "additionalProperties": false,
        "required": ["ecommerce"],
        "properties": { "ecommerce": { "$ref": "#/components/schemas/EcommerceScenarioEvidence" } }
      },
      "EcommerceScenarioEvidence": {
        "type": "object",
        "additionalProperties": false,
        "required": ["product", "variant", "catalogNavigation", "requestMethods", "requestDomains", "mutativeRequestBlockedCount"],
        "properties": {
          "product": {
            "type": "object",
            "required": ["detected", "schemaTypes", "schemaValid", "attributes", "visibleAttributeCount"],
            "properties": {
              "detected": { "type": "boolean" },
              "schemaTypes": { "type": "array", "maxItems": 8, "items": { "type": "string", "maxLength": 80 } },
              "schemaValid": { "type": ["boolean", "null"] },
              "attributes": {
                "type": "object",
                "required": ["name", "description", "price", "availability"],
                "properties": {
                  "name": { "$ref": "#/components/schemas/ProductAttributeEvidence" },
                  "description": { "$ref": "#/components/schemas/ProductAttributeEvidence" },
                  "price": { "$ref": "#/components/schemas/ProductAttributeEvidence" },
                  "availability": { "$ref": "#/components/schemas/ProductAttributeEvidence" }
                },
                "additionalProperties": false
              },
              "visibleAttributeCount": { "type": "integer", "minimum": 0, "maximum": 4 }
            }
          },
          "variant": { "type": "object", "required": ["detected", "attempted", "status", "controlType", "accessibleName", "initialValue", "selectedValue", "priceChanged", "imageChanged", "urlChanged", "resetVerified", "errorType"], "properties": { "detected": { "type": "boolean" }, "attempted": { "type": "boolean" }, "status": { "enum": ["passed", "partial", "blocked", "not_tested"] }, "controlType": { "type": ["string", "null"], "enum": ["select", "radio", "button", null] }, "accessibleName": { "type": ["string", "null"], "maxLength": 160 }, "initialValue": { "type": ["string", "null"], "maxLength": 120 }, "selectedValue": { "type": ["string", "null"], "maxLength": 120 }, "priceChanged": { "type": "boolean" }, "imageChanged": { "type": "boolean" }, "urlChanged": { "type": "boolean" }, "resetVerified": { "type": ["boolean", "null"] }, "errorType": { "type": ["string", "null"], "maxLength": 120 } } },
          "catalogNavigation": { "type": "object", "required": ["detected", "attempted", "status", "catalogUrl", "accessibleName", "listingItemCount", "returnedToProduct", "errorType"], "properties": { "detected": { "type": "boolean" }, "attempted": { "type": "boolean" }, "status": { "enum": ["passed", "partial", "blocked", "not_tested"] }, "catalogUrl": { "type": ["string", "null"] }, "accessibleName": { "type": ["string", "null"], "maxLength": 160 }, "listingItemCount": { "type": "integer", "minimum": 0, "maximum": 100 }, "returnedToProduct": { "type": "boolean" }, "errorType": { "type": ["string", "null"], "maxLength": 120 } } },
          "requestMethods": { "type": "array", "maxItems": 8, "items": { "enum": ["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"] } },
          "requestDomains": { "type": "array", "maxItems": 20, "items": { "type": "string", "maxLength": 253 } },
          "mutativeRequestBlockedCount": { "type": "integer", "minimum": 0, "maximum": 150 },
          "postRequestClassifications": { "type": "array", "maxItems": 60, "description": "Trace agrégée et bornée de chaque POST observé. Les résultats historiques peuvent omettre ce champ.", "items": { "$ref": "#/components/schemas/EcommercePostRequestClassification" } }
        }
      },
      "EcommercePostRequestClassification": {
        "type": "object",
        "additionalProperties": false,
        "required": ["phase", "domain", "classification", "outcome", "count"],
        "properties": {
          "phase": { "enum": ["initial_render", "variant", "catalog_navigation"] },
          "domain": { "type": "string", "maxLength": 253 },
          "classification": { "enum": ["passive_telemetry", "mutative_or_opaque"] },
          "outcome": { "const": "blocked" },
          "count": { "type": "integer", "minimum": 1, "maximum": 220 }
        }
      },
      "ProductAttributeEvidence": {
        "type": "object",
        "additionalProperties": false,
        "required": ["value", "selector"],
        "properties": { "value": { "type": ["string", "null"], "maxLength": 500 }, "selector": { "type": ["string", "null"], "maxLength": 300 } }
      },
      "TargetTestExecution": {
        "type": "object",
        "additionalProperties": false,
        "required": ["testId", "capability", "attempted", "status", "targetElementId", "accessibleName", "startedUrl", "finalUrl", "oracleExpected", "oracleObserved", "resetVerified", "requestMethods", "requestDomains", "durationMs", "errorType", "stepIndexes"],
        "properties": {
          "testId": { "$ref": "#/components/schemas/TargetTestId" }, "capability": { "enum": ["reading", "navigation", "action"] }, "attempted": { "type": "boolean" }, "status": { "enum": ["passed", "partial", "blocked"] },
          "targetElementId": { "type": ["string", "null"] }, "accessibleName": { "type": ["string", "null"], "maxLength": 160 }, "startedUrl": { "type": "string", "format": "uri" }, "finalUrl": { "type": "string", "format": "uri" },
          "oracleExpected": { "type": "string", "maxLength": 500 }, "oracleObserved": { "type": "string", "maxLength": 500 }, "resetVerified": { "type": ["boolean", "null"] }, "contextClosed": { "type": ["boolean", "null"], "description": "Confirme la destruction de la session navigateur jetable après ce test cible ; absent sur les anciens résultats." },
          "requestMethods": { "type": "array", "maxItems": 12, "items": { "type": "string" } }, "requestDomains": { "type": "array", "maxItems": 30, "items": { "type": "string" } }, "durationMs": { "type": "integer", "minimum": 0, "maximum": 20000 }, "errorType": { "type": ["string", "null"] }, "stepIndexes": { "type": "array", "maxItems": 10, "items": { "type": "integer", "minimum": 1, "maximum": 40 } }
        }
      },
      "BrowserScenarioStep": {
        "type": "object",
        "required": ["index", "action", "target", "accessibleName", "selector", "status", "durationMs", "urlBefore", "urlAfter", "observation", "errorType"],
        "properties": {
          "index": { "type": "integer", "minimum": 1, "maximum": 40 },
          "testId": { "oneOf": [{ "$ref": "#/components/schemas/TargetTestId" }, { "type": "null" }] },
          "action": { "type": "string", "maxLength": 120 },
          "target": { "type": ["string", "null"] },
          "accessibleName": { "type": ["string", "null"], "maxLength": 160 },
          "selector": { "type": ["string", "null"], "description": "Référence expurgée ; ne contient pas le sélecteur CSS brut." },
          "status": { "enum": ["passed", "blocked"] },
          "durationMs": { "type": "integer", "minimum": 0, "maximum": 15000 },
          "urlBefore": { "type": "string" },
          "urlAfter": { "type": "string" },
          "observation": { "type": "string", "maxLength": 500 },
          "errorType": { "type": ["string", "null"] }
        }
      },
      "BrowserProcessingState": {
        "type": "object",
        "description": "État du rendu et des scénarios Playwright exécutés dans le worker séparé, hors requête HTTP.",
        "required": ["status", "queuedAt", "startedAt", "completedAt", "attempt", "errorType", "durationMs"],
        "properties": {
          "status": { "enum": ["queued", "running", "completed", "failed", "disabled", "not_tested"] },
          "queuedAt": { "type": ["string", "null"], "format": "date-time" },
          "startedAt": { "type": ["string", "null"], "format": "date-time" },
          "completedAt": { "type": ["string", "null"], "format": "date-time" },
          "attempt": { "type": "integer", "minimum": 0, "maximum": 2 },
          "errorType": { "type": ["string", "null"] },
          "durationMs": { "type": ["integer", "null"], "minimum": 0 }
        }
      },
      "P3ProcessingState": {
        "type": "object",
        "additionalProperties": false,
        "description": "État des tests P3 de recherche, formulaire et interactions réversibles, exécutés après le moteur P2 gelé.",
        "required": ["status", "startedAt", "completedAt", "attempt", "errorType", "durationMs"],
        "properties": {
          "status": { "enum": ["not_tested", "running", "completed", "failed", "disabled"] },
          "startedAt": { "type": ["string", "null"], "format": "date-time" },
          "completedAt": { "type": ["string", "null"], "format": "date-time" },
          "attempt": { "type": "integer", "minimum": 0 },
          "errorType": { "type": ["string", "null"] },
          "durationMs": { "type": ["integer", "null"], "minimum": 0 }
        }
      },
      "P3PublicReport": {
        "type": "object",
        "additionalProperties": false,
        "required": ["schemaVersion", "engineVersion", "generatedAt", "search", "form", "interactions", "privateWrites", "networkSafety"],
        "properties": {
          "schemaVersion": { "const": 1 },
          "engineVersion": { "const": "obsero-p3-1.0.0" },
          "generatedAt": { "type": "string", "format": "date-time" },
          "search": { "type": "object", "additionalProperties": true },
          "form": { "type": "object", "additionalProperties": true },
          "interactions": { "type": "array", "maxItems": 13, "items": { "type": "object", "additionalProperties": true } },
          "privateWrites": { "type": "object", "additionalProperties": true },
          "networkSafety": {
            "type": "object", "additionalProperties": false,
            "required": ["emittedMutativeRequests", "observedBlockedMutativeRequests"],
            "properties": {
              "emittedMutativeRequests": { "const": 0 },
              "observedBlockedMutativeRequests": { "type": "integer", "minimum": 0 }
            }
          }
        }
      },
      "ScanArtifact": {
        "type": "object",
        "description": "Métadonnées publiques d'une capture temporaire. La clé de stockage et le chemin local ne sont jamais exposés.",
        "required": ["publicId", "scenarioId", "stepIndex", "type", "sourceUrl", "format", "width", "height", "byteSize", "capturedAt", "expiresAt", "state", "imagePath"],
        "properties": {
          "publicId": { "type": "string", "pattern": "^[A-Za-z0-9_-]{24}$" },
          "scenarioId": { "enum": ["open_information_page", "internal_search", "prepare_form", "modern_interactions", "ecommerce_product"] },
          "stepIndex": { "type": "integer", "minimum": 0, "maximum": 40 },
          "type": { "enum": ["initial_render", "final_render"] },
          "sourceUrl": { "type": "string", "description": "URL expurgée des identifiants, paramètres et fragment." },
          "format": { "const": "webp" },
          "width": { "type": "integer", "minimum": 1, "maximum": 1440 },
          "height": { "type": "integer", "minimum": 1, "maximum": 900 },
          "byteSize": { "type": "integer", "minimum": 1, "maximum": 500000 },
          "capturedAt": { "type": "string", "format": "date-time" },
          "expiresAt": { "type": "string", "format": "date-time" },
          "state": { "enum": ["available", "expired", "deleted"] },
          "imagePath": { "type": ["string", "null"], "description": "Route Obsero contrôlée ; null après expiration ou suppression." }
        }
      },
      "CreateAgentCampaign": {
        "type": "object",
        "additionalProperties": false,
        "required": ["confirm"],
        "properties": {
          "confirm": { "const": true },
          "taskType": { "enum": ["public_information", "navigate_information_page", "internal_search"], "default": "public_information" },
          "repetitions": { "type": "integer", "minimum": 1, "maximum": 5, "default": 3 },
          "configurationId": { "type": "string", "deprecated": true, "pattern": "^[a-zA-Z0-9._:-]+$", "maxLength": 120 },
          "configurationIds": { "type": "array", "minItems": 1, "maxItems": 4, "uniqueItems": true, "items": { "type": "string", "pattern": "^[a-zA-Z0-9._:-]+$", "maxLength": 120 } },
          "taskTemplatePublicId": { "type": "string", "pattern": "^[A-Za-z0-9_-]{24}$", "description": "Version immuable prévalidée ; session propriétaire requise." },
          "environmentPublicId": { "type": "string", "pattern": "^[A-Za-z0-9_-]{24}$", "description": "Environnement non productif vérifié ; exige taskTemplatePublicId et une session propriétaire." }
        }
      },
      "AgentTest": {
        "type": "object",
        "description": "Exécution unique facultative, séparée du résultat déterministe.",
        "required": ["schemaVersion", "publicId", "createdAt", "task", "status", "provider", "budget", "result", "errorType", "usage", "timestamps", "executionKind", "irreversibleToolsProvided", "allowedActions", "providerCalls", "steps"],
        "properties": {
          "schemaVersion": { "const": 1 },
          "publicId": { "type": "string", "pattern": "^[A-Za-z0-9_-]{24}$" },
          "createdAt": { "type": "string", "format": "date-time" },
          "updatedAt": { "type": "string", "format": "date-time" },
          "task": { "type": "object", "required": ["type", "version", "objective"], "properties": { "type": { "enum": ["public_information", "navigate_information_page", "internal_search", "modern_target", "custom_safe"] }, "version": { "type": "string" }, "objective": { "type": "string" } } },
          "status": { "enum": ["created", "queued", "running", "passed", "blocked", "failed", "cancelled"] },
          "provider": { "type": "object", "description": "Fournisseur principal prévu au lancement.", "required": ["id", "model", "version", "configurationId"], "properties": { "id": { "enum": ["ollama", "openrouter", "openai_compatible"] }, "model": { "type": "string" }, "version": { "type": "string" }, "configurationId": { "type": "string" } } },
          "budget": { "type": "object", "additionalProperties": true },
          "result": { "type": ["object", "null"], "properties": { "comparison": { "$ref": "#/components/schemas/AgentComparison" } }, "additionalProperties": true },
          "errorType": { "type": ["string", "null"] },
          "usage": { "type": "object", "required": ["inputTokens", "outputTokens", "estimatedCostMicros"], "properties": { "inputTokens": { "type": "integer", "minimum": 0 }, "outputTokens": { "type": "integer", "minimum": 0 }, "estimatedCostMicros": { "type": "integer", "minimum": 0 } } },
          "timestamps": { "type": "object", "additionalProperties": true },
          "executionKind": { "const": "single_run" },
          "irreversibleToolsProvided": { "const": false },
          "allowedActions": { "type": "array", "minItems": 1, "maxItems": 6, "uniqueItems": true, "items": { "enum": ["observe", "click", "type", "press_enter", "navigate", "finish"] } },
          "providerCalls": { "type": "array", "items": { "$ref": "#/components/schemas/AgentProviderCall" } },
          "steps": { "type": "array", "items": { "type": "object", "additionalProperties": true } }
        }
      },
      "AgentProviderCall": {
        "type": "object",
        "description": "Trace expurgée d'un appel physique ; aucun prompt, réponse brute ou secret.",
        "required": ["attempt", "logicalCallId", "sequence", "provider", "model", "resolvedModelVersion", "status", "fallbackCause", "durationMs", "usage", "errorType"],
        "properties": {
          "attempt": { "type": "integer", "minimum": 1 },
          "logicalCallId": { "type": "string" },
          "sequence": { "type": "integer", "minimum": 1, "maximum": 2 },
          "provider": { "enum": ["ollama", "openrouter", "openai_compatible"] },
          "model": { "type": "string" },
          "resolvedModelVersion": { "type": ["string", "null"] },
          "status": { "enum": ["succeeded", "failed"] },
          "fallbackCause": { "type": ["string", "null"] },
          "durationMs": { "type": "integer", "minimum": 0 },
          "usage": { "type": "object", "required": ["inputTokens", "outputTokens", "estimatedCostMicros"], "properties": { "inputTokens": { "type": "integer", "minimum": 0 }, "outputTokens": { "type": "integer", "minimum": 0 }, "estimatedCostMicros": { "type": "integer", "minimum": 0 } } },
          "errorType": { "type": ["string", "null"] }
        },
        "additionalProperties": false
      },
      "AgentCampaign": {
        "type": "object",
        "description": "Ensemble versionné de runs isolés partageant exactement tâche, oracle, outils et limites. Les résultats bruts restent la source de vérité.",
        "required": ["schemaVersion", "publicId", "createdAt", "updatedAt", "task", "status", "visibility", "repetitions", "configurationIds", "policy", "budget", "aggregate", "usage", "environment", "timestamps", "runs"],
        "properties": {
          "schemaVersion": { "const": 1 },
          "publicId": { "type": "string", "pattern": "^[A-Za-z0-9_-]{24}$" },
          "createdAt": { "type": "string", "format": "date-time" },
          "updatedAt": { "type": "string", "format": "date-time" },
          "task": { "type": "object", "required": ["type", "version", "definition"], "properties": { "type": { "enum": ["public_information", "navigate_information_page", "internal_search", "custom_safe"] }, "version": { "type": "string" }, "definition": { "type": "object", "additionalProperties": true } } },
          "status": { "enum": ["created", "queued", "running", "completed", "partial", "blocked", "failed", "cancelled"] },
          "visibility": { "enum": ["public", "owner_only"] },
          "repetitions": { "type": "integer", "minimum": 1, "maximum": 5 },
          "configurationIds": { "type": "array", "minItems": 1, "maxItems": 4, "uniqueItems": true, "items": { "type": "string" } },
          "policy": { "type": "object", "required": ["comparable", "conditions", "allowedActions", "freshSessionPerRun", "irreversibleToolsProvided"], "properties": { "comparable": { "const": true }, "conditions": { "type": "array", "items": { "type": "string" } }, "allowedActions": { "type": "array", "items": { "type": "string" } }, "freshSessionPerRun": { "const": true }, "irreversibleToolsProvided": { "const": false } }, "additionalProperties": true },
          "budget": { "type": "object", "required": ["maxRuns", "maxEstimatedCostMicros", "perRun"], "properties": { "maxRuns": { "type": "integer", "minimum": 1, "maximum": 20 }, "maxEstimatedCostMicros": { "type": "integer", "minimum": 0 }, "perRun": { "type": "object", "additionalProperties": true } } },
          "aggregate": { "oneOf": [{ "type": "null" }, { "$ref": "#/components/schemas/AgentCampaignAggregate" }] },
          "usage": { "type": "object", "required": ["estimatedCostMicros", "reservedCostMicros", "remainingBudgetMicros"], "properties": { "estimatedCostMicros": { "type": "integer", "minimum": 0 }, "reservedCostMicros": { "type": "integer", "minimum": 0 }, "remainingBudgetMicros": { "type": "integer", "minimum": 0 } } },
          "environment": { "oneOf": [{ "type": "null" }, { "type": "object", "required": ["publicId", "kind", "baseUrl", "status", "credentialsExposed"], "properties": { "publicId": { "type": "string" }, "kind": { "enum": ["test", "staging", "demo", "sandbox"] }, "baseUrl": { "type": "string", "format": "uri" }, "status": { "type": "string" }, "verifiedAt": { "type": ["string", "null"], "format": "date-time" }, "revokedAt": { "type": ["string", "null"], "format": "date-time" }, "credentialsExposed": { "const": false } } }] },
          "timestamps": { "type": "object", "additionalProperties": true },
          "runs": { "type": "array", "minItems": 1, "maxItems": 20, "items": { "$ref": "#/components/schemas/AgentCampaignRun" } }
        }
      },
      "AgentCampaignAggregate": {
        "type": "object",
        "required": ["schemaVersion", "executionCount", "terminalCount", "sampleSize", "insufficientSample", "stability", "rates", "counts", "steps", "latencyMs", "costMicros", "variationAttribution", "completed", "configurations", "runs"],
        "properties": {
          "schemaVersion": { "const": 1 },
          "executionCount": { "type": "integer", "minimum": 0 },
          "terminalCount": { "type": "integer", "minimum": 0 },
          "sampleSize": { "type": "integer", "minimum": 0 },
          "insufficientSample": { "type": "boolean" },
          "stability": { "enum": ["insufficient_sample", "stable", "variable"] },
          "rates": { "type": "object", "additionalProperties": { "type": ["number", "null"], "minimum": 0, "maximum": 1 } },
          "counts": { "type": "object", "additionalProperties": { "type": "integer", "minimum": 0 } },
          "steps": { "type": "object", "additionalProperties": true },
          "latencyMs": { "type": "object", "additionalProperties": true },
          "costMicros": { "type": "object", "additionalProperties": true },
          "variationAttribution": { "type": "object", "required": ["modelOrDecision", "site", "infrastructureErrors", "observationWindow", "siteSignatureCount"], "properties": { "modelOrDecision": { "type": "boolean" }, "site": { "enum": ["possible_change", "not_observed", "not_measured"] }, "infrastructureErrors": { "type": "integer", "minimum": 0 }, "observationWindow": { "type": "object", "additionalProperties": true }, "siteSignatureCount": { "type": "integer", "minimum": 0 } } },
          "completed": { "type": "boolean" },
          "configurations": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
          "runs": { "type": "array", "items": { "type": "object", "additionalProperties": true } }
        }
      },
      "AgentCampaignRun": {
        "type": "object",
        "required": ["publicId", "runIndex", "executionOrder", "task", "status", "provider", "result", "errorType", "usage", "timestamps", "irreversibleToolsProvided", "providerCalls", "steps"],
        "properties": {
          "publicId": { "type": "string", "pattern": "^[A-Za-z0-9_-]{24}$" },
          "runIndex": { "type": "integer", "minimum": 1, "maximum": 20 },
          "executionOrder": { "type": "integer", "minimum": 1, "maximum": 20 },
          "task": { "type": "object", "additionalProperties": true },
          "status": { "enum": ["created", "queued", "running", "passed", "blocked", "failed", "cancelled"] },
          "provider": { "type": "object", "required": ["id", "model", "version", "configurationId", "parameters"], "properties": { "id": { "enum": ["ollama", "openrouter", "openai_compatible"] }, "model": { "type": "string" }, "version": { "type": "string" }, "configurationId": { "type": "string" }, "parameters": { "type": "object", "properties": { "temperature": { "type": "number" }, "topP": { "type": "number" }, "seed": { "type": "integer" } }, "additionalProperties": false } } },
          "result": { "type": ["object", "null"], "additionalProperties": true },
          "errorType": { "type": ["string", "null"] },
          "usage": { "type": "object", "additionalProperties": true },
          "timestamps": { "type": "object", "additionalProperties": true },
          "irreversibleToolsProvided": { "const": false },
          "providerCalls": { "type": "array", "items": { "$ref": "#/components/schemas/AgentProviderCall" } },
          "steps": { "type": "array", "items": { "type": "object", "additionalProperties": true } }
        }
      },
      "AgentComparison": {
        "type": "object",
        "description": "Comparaison post-exécution calculée par Obsero ; le modèle ne choisit ni le statut ni l'attribution.",
        "required": ["schemaVersion", "classification", "attribution", "sameFailurePoint", "dryRun", "deterministic", "agent"],
        "properties": {
          "schemaVersion": { "const": 1 },
          "classification": { "enum": ["convergent_success", "agent_test_failure", "convergent_failure", "deterministic_limit", "divergent_failure"] },
          "attribution": { "enum": ["none", "site", "model", "test_limit"] },
          "sameFailurePoint": { "type": "boolean" },
          "dryRun": {
            "type": "object",
            "required": ["status", "target", "oracleType", "providerContacted", "networkExecuted", "networkRequests", "allowedMethods", "errorType"],
            "properties": {
              "status": { "const": "contract_validated" },
              "target": { "type": ["string", "null"] },
              "oracleType": { "enum": ["text_contains", "url_equals", "search_results", "interface_change"] },
              "providerContacted": { "const": false },
              "networkExecuted": { "const": false },
              "networkRequests": { "type": "array", "maxItems": 0 },
              "allowedMethods": { "type": "array", "items": { "enum": ["GET", "HEAD"] } },
              "errorType": { "type": "null" }
            },
            "additionalProperties": false
          },
          "deterministic": { "$ref": "#/components/schemas/AgentComparisonSide" },
          "agent": { "$ref": "#/components/schemas/AgentComparisonSide" }
        }
      },
      "AgentComparisonSide": {
        "type": "object",
        "required": ["status", "target", "stepCount", "durationMs", "errorType"],
        "properties": {
          "status": { "type": "string" },
          "target": { "type": ["string", "null"] },
          "stepCount": { "type": "integer", "minimum": 0, "maximum": 20 },
          "durationMs": { "type": "integer", "minimum": 0 },
          "errorType": { "type": ["string", "null"] },
          "failureStage": { "type": ["string", "null"] }
        },
        "additionalProperties": true
      },
      "BrowserInterfaceMap": {
        "type": "object",
        "required": ["status", "finalUrl", "durationMs", "errorType", "htmlComparison", "inventory", "accessibility", "obstacles", "capabilities", "robotProfiles", "navigationCount", "blockedRequestCount"],
        "properties": {
          "status": { "enum": ["completed", "blocked", "unavailable", "disabled"] },
          "finalUrl": { "type": ["string", "null"] },
          "durationMs": { "type": "integer", "minimum": 0 },
          "errorType": { "type": ["string", "null"] },
          "htmlComparison": {
            "type": "object",
            "required": ["initialHtmlBytes", "renderedHtmlBytes", "initialTextLength", "renderedTextLength", "javascriptAddedTextLength"],
            "properties": {
              "initialHtmlBytes": { "type": "integer", "minimum": 0 },
              "renderedHtmlBytes": { "type": "integer", "minimum": 0 },
              "initialTextLength": { "type": "integer", "minimum": 0 },
              "renderedTextLength": { "type": "integer", "minimum": 0 },
              "javascriptAddedTextLength": { "type": "integer", "minimum": 0 }
            }
          },
          "inventory": {
            "type": "object",
            "required": ["total", "truncated", "byKind", "elements"],
            "properties": {
              "total": { "type": "integer", "minimum": 0 },
              "truncated": { "type": "boolean" },
              "byKind": { "type": "object", "additionalProperties": { "type": "integer" } },
              "elements": { "type": "array", "maxItems": 250, "items": { "$ref": "#/components/schemas/InteractiveElement" } }
            }
          },
          "accessibility": { "type": "object", "additionalProperties": { "type": "integer" } },
          "obstacles": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
          "capabilities": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
          "robotProfiles": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
          "navigationCount": { "type": "integer", "minimum": 0 },
          "blockedRequestCount": { "type": "integer", "minimum": 0 }
        }
      },
      "InteractiveElement": {
        "type": "object",
        "required": ["id", "kind", "tag", "selector", "role", "accessibleName", "label", "href", "inputType", "visible", "disabled", "focusable", "tabIndex", "describedBy", "errorMessageId"],
        "properties": {
          "id": { "type": "string" },
          "kind": { "enum": ["link", "button", "field", "form", "menu", "dialog", "disclosure", "custom"] },
          "tag": { "type": "string" },
          "selector": { "type": "string" },
          "framePath": { "type": "array", "maxItems": 4, "items": { "type": "string", "maxLength": 300 }, "description": "Chemin borné d'iframes même origine menant à la cible." },
          "role": { "type": ["string", "null"] },
          "accessibleName": { "type": ["string", "null"] },
          "label": { "type": ["string", "null"] },
          "href": { "type": ["string", "null"] },
          "inputType": { "type": ["string", "null"] },
          "formRole": { "type": ["string", "null"], "description": "Rôle du formulaire ancêtre lorsqu'il existe." },
          "formMethod": { "type": ["string", "null"], "description": "Méthode déclarée du formulaire ancêtre, sans donnée saisie." },
          "visible": { "type": "boolean" },
          "disabled": { "type": "boolean" },
          "focusable": { "type": "boolean" },
          "tabIndex": { "type": "integer" },
          "selected": { "type": ["boolean", "null"], "description": "État aria-selected observé, lorsqu'il est déclaré." },
          "describedBy": { "type": "array", "items": { "type": "string" } },
          "errorMessageId": { "type": ["string", "null"] }
        }
      },
      "CapabilityAssessment": {
        "type": "object",
        "required": ["status", "tested", "method", "summary", "evidenceIds"],
        "properties": {
          "status": { "enum": ["passed", "partial", "blocked", "not_tested"] },
          "tested": { "type": "boolean" },
          "method": { "enum": ["static_html", "browser_render", "browser_scenario"] },
          "summary": { "type": "string" },
          "evidenceIds": { "type": "array", "items": { "type": "string" } }
        }
      }
    }
  }
}
