{
 "openapi": "3.1.0",
 "info": {
  "title": "Fab Dispatch API",
  "description": "Allocates equipment engineers to tool-downs and PMs in a semiconductor fab. Five strategies, from greedy to PyVRP, plus live re-dispatch and a grounded assistant. Engine 2026.10.1. Sign in at the app, then call the API with the session's bearer token.",
  "version": "2.9.0"
 },
 "paths": {
  "/api/health": {
   "get": {
    "tags": [
     "system"
    ],
    "summary": "Health",
    "description": "Readiness: can we reach every database, and is each schema current? Public and\nunauthenticated, so the result is cached per instance for ``HEALTH_CACHE_S``: a client\npolling it can't make this instance query every tenant database on each call. Output\nstays minimal (a count of databases, never their names).",
    "operationId": "health_api_health_get",
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "type": "object",
         "title": "Response Health Api Health Get"
        }
       }
      }
     }
    }
   }
  },
  "/api/meta": {
   "get": {
    "tags": [
     "system"
    ],
    "summary": "Meta",
    "description": "Public (the sign-in screen reads it), so it describes the deployment, never its tenants:\nno fab or database names.",
    "operationId": "meta_api_meta_get",
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     }
    }
   }
  },
  "/api/auth/config": {
   "get": {
    "tags": [
     "auth"
    ],
    "summary": "Auth Config",
    "description": "Public: tells the UI how to sign in. The publishable key is designed to be public.",
    "operationId": "auth_config_api_auth_config_get",
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "type": "object",
         "title": "Response Auth Config Api Auth Config Get"
        }
       }
      }
     }
    }
   }
  },
  "/api/auth/demo": {
   "post": {
    "tags": [
     "auth"
    ],
    "summary": "Demo Sign In",
    "operationId": "demo_sign_in_api_auth_demo_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/DemoSignIn"
       }
      }
     },
     "required": true
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "type": "object",
         "title": "Response Demo Sign In Api Auth Demo Post"
        }
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    }
   }
  },
  "/api/auth/me": {
   "get": {
    "tags": [
     "auth"
    ],
    "summary": "Me",
    "operationId": "me_api_auth_me_get",
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/User"
        }
       }
      }
     }
    }
   },
   "delete": {
    "tags": [
     "auth"
    ],
    "summary": "Delete Me",
    "description": "Delete your account and personal data (GDPR Art. 17). Feedback, presence and pending\nreplays are deleted; your email in shared shift history becomes \"a deleted user\", so the\naudit trail keeps its shape without identifying you. On Supabase the sign-in account\nitself is removed too. Demo accounts are shared, so only their data is erased.",
    "operationId": "delete_me_api_auth_me_delete",
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "type": "object",
         "title": "Response Delete Me Api Auth Me Delete"
        }
       }
      }
     }
    }
   }
  },
  "/api/fabs": {
   "get": {
    "tags": [
     "fabs"
    ],
    "summary": "List Fabs",
    "operationId": "list_fabs_api_fabs_get",
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     }
    }
   }
  },
  "/api/fabs/{fab_id}": {
   "get": {
    "tags": [
     "fabs"
    ],
    "summary": "Fab",
    "operationId": "fab_api_fabs__fab_id__get",
    "parameters": [
     {
      "name": "fab_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "title": "Fab Id"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    }
   }
  },
  "/api/fabs/{fab_id}/engineers/{engineer_id}/assignments": {
   "get": {
    "tags": [
     "fabs"
    ],
    "summary": "Engineer Assignments",
    "description": "What an engineer has been given across this fab's live shifts, newest first: a query on\nthe assignments read model, not a scan of shift documents.",
    "operationId": "engineer_assignments_api_fabs__fab_id__engineers__engineer_id__assignments_get",
    "parameters": [
     {
      "name": "fab_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "title": "Fab Id"
      }
     },
     {
      "name": "engineer_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "title": "Engineer Id"
      }
     },
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "schema": {
       "type": "integer",
       "maximum": 500,
       "minimum": 1,
       "default": 100,
       "title": "Limit"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {
         "type": "array",
         "items": {
          "type": "object",
          "additionalProperties": true
         },
         "title": "Response Engineer Assignments Api Fabs  Fab Id  Engineers  Engineer Id  Assignments Get"
        }
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    }
   }
  },
  "/api/scenario": {
   "post": {
    "tags": [
     "planning"
    ],
    "summary": "Make Scenario",
    "operationId": "make_scenario_api_scenario_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/GenerateRequest"
       }
      }
     },
     "required": true
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Scenario"
        }
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    }
   }
  },
  "/api/plan": {
   "post": {
    "tags": [
     "planning"
    ],
    "summary": "Plan",
    "description": "Plan one shift with one strategy. Cached by content: identical requests are instant.",
    "operationId": "plan_api_plan_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/PlanRequest"
       }
      }
     },
     "required": true
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/AllocationResult"
        }
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    }
   }
  },
  "/api/allocate": {
   "post": {
    "tags": [
     "planning"
    ],
    "summary": "Allocate Many",
    "description": "Every requested strategy on the same shift (the UI calls /plan per strategy instead,\nso fast ones render first).",
    "operationId": "allocate_many_api_allocate_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/AllocateRequest"
       }
      }
     },
     "required": true
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {
         "items": {
          "$ref": "#/components/schemas/AllocationResult"
         },
         "type": "array",
         "title": "Response Allocate Many Api Allocate Post"
        }
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    }
   }
  },
  "/api/benchmark": {
   "post": {
    "tags": [
     "planning"
    ],
    "summary": "Benchmark",
    "description": "All strategies over many seeded shifts of one preset. Each request is capped at\n``seeds x n_jobs <= 1000`` (BENCHMARK_MAX_JOB_RUNS), which keeps the slowest admitted case\nto roughly 25 s against a 60 s serverless limit; the client calls it once per preset.",
    "operationId": "benchmark_api_benchmark_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/BenchmarkRequest"
       }
      }
     },
     "required": true
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "type": "object",
         "title": "Response Benchmark Api Benchmark Post"
        }
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    }
   }
  },
  "/api/optimality-gap": {
   "post": {
    "tags": [
     "planning"
    ],
    "summary": "Optimality Gap",
    "description": "Exact optimum (route enumeration + MILP) vs every heuristic, on small shifts.",
    "operationId": "optimality_gap_api_optimality_gap_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/GapRequest"
       }
      }
     },
     "required": true
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "type": "object",
         "title": "Response Optimality Gap Api Optimality Gap Post"
        }
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    }
   }
  },
  "/api/shifts": {
   "post": {
    "tags": [
     "live"
    ],
    "summary": "Create Shift",
    "operationId": "create_shift_api_shifts_post",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/CreateShift"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true,
         "title": "Response Create Shift Api Shifts Post"
        }
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    }
   },
   "get": {
    "tags": [
     "live"
    ],
    "summary": "List Shifts",
    "operationId": "list_shifts_api_shifts_get",
    "parameters": [
     {
      "name": "fab_id",
      "in": "query",
      "required": true,
      "schema": {
       "type": "string",
       "title": "Fab Id"
      }
     },
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "schema": {
       "type": "integer",
       "maximum": 100,
       "minimum": 1,
       "default": 20,
       "title": "Limit"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {
         "type": "array",
         "items": {
          "type": "object",
          "additionalProperties": true
         },
         "title": "Response List Shifts Api Shifts Get"
        }
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    }
   }
  },
  "/api/shifts/{shift_id}": {
   "get": {
    "tags": [
     "live"
    ],
    "summary": "Get Shift",
    "description": "The shift as clients see it. The ETag starts with the shift's version, so it works as an\nIf-Match for the write endpoints; the rest of it changes with presence and the clock lease,\nso a conditional GET still picks those up without a version bump.",
    "operationId": "get_shift_api_shifts__shift_id__get",
    "parameters": [
     {
      "name": "shift_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "title": "Shift Id"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    }
   }
  },
  "/api/shifts/{shift_id}/advance": {
   "post": {
    "tags": [
     "live"
    ],
    "summary": "Advance",
    "operationId": "advance_api_shifts__shift_id__advance_post",
    "parameters": [
     {
      "name": "shift_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "title": "Shift Id"
      }
     },
     {
      "name": "if-match",
      "in": "header",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "If-Match"
      }
     },
     {
      "name": "idempotency-key",
      "in": "header",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Idempotency-Key"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/Advance"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true,
         "title": "Response Advance Api Shifts  Shift Id  Advance Post"
        }
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    }
   }
  },
  "/api/shifts/{shift_id}/release": {
   "post": {
    "tags": [
     "live"
    ],
    "summary": "Release",
    "description": "Give up the clock (on pause), so another dispatcher can drive without waiting.",
    "operationId": "release_api_shifts__shift_id__release_post",
    "parameters": [
     {
      "name": "shift_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "title": "Shift Id"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true,
         "title": "Response Release Api Shifts  Shift Id  Release Post"
        }
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    }
   }
  },
  "/api/shifts/{shift_id}/jobs": {
   "post": {
    "tags": [
     "live"
    ],
    "summary": "Report Job",
    "operationId": "report_job_api_shifts__shift_id__jobs_post",
    "parameters": [
     {
      "name": "shift_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "title": "Shift Id"
      }
     },
     {
      "name": "if-match",
      "in": "header",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "If-Match"
      }
     },
     {
      "name": "idempotency-key",
      "in": "header",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Idempotency-Key"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ReportJob"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true,
         "title": "Response Report Job Api Shifts  Shift Id  Jobs Post"
        }
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    }
   }
  },
  "/api/shifts/{shift_id}/engineers/{engineer_id}/off": {
   "post": {
    "tags": [
     "live"
    ],
    "summary": "Engineer Off",
    "operationId": "engineer_off_api_shifts__shift_id__engineers__engineer_id__off_post",
    "parameters": [
     {
      "name": "shift_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "title": "Shift Id"
      }
     },
     {
      "name": "engineer_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "title": "Engineer Id"
      }
     },
     {
      "name": "if-match",
      "in": "header",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "If-Match"
      }
     },
     {
      "name": "idempotency-key",
      "in": "header",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Idempotency-Key"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true,
         "title": "Response Engineer Off Api Shifts  Shift Id  Engineers  Engineer Id  Off Post"
        }
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    }
   }
  },
  "/api/shifts/{shift_id}/assignments": {
   "get": {
    "tags": [
     "live"
    ],
    "summary": "Shift Assignments",
    "description": "The shift's current assignments from the read model: one row per known job.",
    "operationId": "shift_assignments_api_shifts__shift_id__assignments_get",
    "parameters": [
     {
      "name": "shift_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "title": "Shift Id"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {
         "type": "array",
         "items": {
          "type": "object",
          "additionalProperties": true
         },
         "title": "Response Shift Assignments Api Shifts  Shift Id  Assignments Get"
        }
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    }
   }
  },
  "/api/shifts/{shift_id}/events": {
   "get": {
    "tags": [
     "live"
    ],
    "summary": "Events",
    "operationId": "events_api_shifts__shift_id__events_get",
    "parameters": [
     {
      "name": "shift_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "title": "Shift Id"
      }
     },
     {
      "name": "after",
      "in": "query",
      "required": false,
      "schema": {
       "type": "integer",
       "minimum": 0,
       "default": 0,
       "title": "After"
      }
     },
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "schema": {
       "type": "integer",
       "maximum": 500,
       "minimum": 1,
       "default": 200,
       "title": "Limit"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {
         "type": "array",
         "items": {
          "type": "object",
          "additionalProperties": true
         },
         "title": "Response Events Api Shifts  Shift Id  Events Get"
        }
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    }
   }
  },
  "/api/shifts/{shift_id}/stream-ticket": {
   "post": {
    "tags": [
     "live"
    ],
    "summary": "Stream Ticket",
    "description": "A short-lived ticket that opens this shift's stream (``GET .../stream?ticket=``), since\n``EventSource`` can't send an Authorization header. Bound to the caller and this shift; ask for\na new one before every (re)connect, and reuse within its lifetime is fine.",
    "operationId": "stream_ticket_api_shifts__shift_id__stream_ticket_post",
    "parameters": [
     {
      "name": "shift_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "title": "Shift Id"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true,
         "title": "Response Stream Ticket Api Shifts  Shift Id  Stream Ticket Post"
        }
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    }
   }
  },
  "/api/shifts/{shift_id}/stream": {
   "get": {
    "tags": [
     "live"
    ],
    "summary": "Stream",
    "description": "Server-Sent Events. The stream tails the shift's event log by id, so it works with\nmany server instances and resumes on reconnect (EventSource sends Last-Event-ID).\nEach response closes after ``sse_window_s``, which stays inside serverless limits;\nthe browser reconnects on its own. Open streams double as presence heartbeats.\n\nAuthenticate with ``?ticket=`` from ``POST .../stream-ticket`` (``?access_token=`` is\naccepted for one more release and logs a deprecation warning).",
    "operationId": "stream_api_shifts__shift_id__stream_get",
    "parameters": [
     {
      "name": "shift_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "title": "Shift Id"
      }
     },
     {
      "name": "after",
      "in": "query",
      "required": false,
      "schema": {
       "type": "integer",
       "minimum": 0,
       "description": "resume after this event id (Last-Event-ID wins when sent)",
       "default": 0,
       "title": "After"
      },
      "description": "resume after this event id (Last-Event-ID wins when sent)"
     },
     {
      "name": "last-event-id",
      "in": "header",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Last-Event-Id"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    }
   }
  },
  "/api/repairs/similar": {
   "post": {
    "tags": [
     "repair history"
    ],
    "summary": "Similar",
    "operationId": "similar_api_repairs_similar_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/SimilarRequest"
       }
      }
     },
     "required": true
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "type": "object",
         "title": "Response Similar Api Repairs Similar Post"
        }
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    }
   }
  },
  "/api/repairs/predict-durations": {
   "post": {
    "tags": [
     "repair history"
    ],
    "summary": "Predict Durations",
    "description": "Replace each tool-down's standard estimate with the history-based estimate (k-NN mean).\nPMs keep their scheduled duration, and so does a tool-down whose symptom has no similar\nrepair history (listed in ``skipped`` with the reason). Returns the new scenario, what\nchanged (with each estimate's p10-p90 band, likely cause, match label and basis) and a\nplain-language ``basis``.",
    "operationId": "predict_durations_api_repairs_predict_durations_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/Scenario"
       }
      }
     },
     "required": true
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {
         "additionalProperties": true,
         "type": "object",
         "title": "Response Predict Durations Api Repairs Predict Durations Post"
        }
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    }
   }
  },
  "/api/help": {
   "get": {
    "tags": [
     "help"
    ],
    "summary": "Help Index",
    "operationId": "help_index_api_help_get",
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     }
    }
   }
  },
  "/api/help/search": {
   "get": {
    "tags": [
     "help"
    ],
    "summary": "Help Search",
    "operationId": "help_search_api_help_search_get",
    "parameters": [
     {
      "name": "q",
      "in": "query",
      "required": true,
      "schema": {
       "type": "string",
       "minLength": 1,
       "maxLength": 200,
       "title": "Q"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    }
   }
  },
  "/api/help/{slug}": {
   "get": {
    "tags": [
     "help"
    ],
    "summary": "Help Article",
    "operationId": "help_article_api_help__slug__get",
    "parameters": [
     {
      "name": "slug",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "title": "Slug"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    }
   }
  },
  "/api/assistant/feedback": {
   "post": {
    "tags": [
     "help"
    ],
    "summary": "Assistant Feedback",
    "description": "Thumbs up/down on an answer. Stored with the user id (not the email) for 180 days; a\nsecond rating of the same question by the same user within a day updates the first.\nUnhelpful answers with a comment are exported as candidate evaluation cases by\n``scripts/feedback_to_eval.py``, which is how answers get better.",
    "operationId": "assistant_feedback_api_assistant_feedback_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/Feedback"
       }
      }
     },
     "required": true
    },
    "responses": {
     "204": {
      "description": "Successful Response"
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    }
   }
  },
  "/api/assistant/stats": {
   "get": {
    "tags": [
     "help"
    ],
    "summary": "Assistant Stats",
    "description": "Answer quality in production: how often answers were marked helpful, by kind of question.",
    "operationId": "assistant_stats_api_assistant_stats_get",
    "parameters": [
     {
      "name": "days",
      "in": "query",
      "required": false,
      "schema": {
       "type": "integer",
       "maximum": 180,
       "minimum": 1,
       "default": 30,
       "title": "Days"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true,
         "title": "Response Assistant Stats Api Assistant Stats Get"
        }
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    }
   }
  },
  "/api/assistant/ask": {
   "post": {
    "tags": [
     "help"
    ],
    "summary": "Ask",
    "operationId": "ask_api_assistant_ask_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/AskRequest"
       }
      }
     },
     "required": true
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Answer"
        }
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    }
   }
  },
  "/api/ingest/tool-downs": {
   "post": {
    "tags": [
     "integration"
    ],
    "summary": "Tool Down",
    "operationId": "tool_down_api_ingest_tool_downs_post",
    "parameters": [
     {
      "name": "authorization",
      "in": "header",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Authorization"
      }
     },
     {
      "name": "idempotency-key",
      "in": "header",
      "required": true,
      "schema": {
       "type": "string",
       "description": "unique per message; retries reuse it",
       "title": "Idempotency-Key"
      },
      "description": "unique per message; retries reuse it"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ToolDown"
       }
      }
     }
    },
    "responses": {
     "202": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "additionalProperties": true,
         "title": "Response Tool Down Api Ingest Tool Downs Post"
        }
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    }
   }
  }
 },
 "components": {
  "schemas": {
   "Action": {
    "properties": {
     "label": {
      "type": "string",
      "title": "Label"
     },
     "kind": {
      "type": "string",
      "enum": [
       "job",
       "engineer",
       "tab",
       "help",
       "strategy"
      ],
      "title": "Kind"
     },
     "target": {
      "type": "string",
      "title": "Target"
     }
    },
    "type": "object",
    "required": [
     "label",
     "kind",
     "target"
    ],
    "title": "Action"
   },
   "Advance": {
    "properties": {
     "minutes": {
      "type": "number",
      "maximum": 1440.0,
      "exclusiveMinimum": 0.0,
      "title": "Minutes",
      "default": 30
     }
    },
    "type": "object",
    "title": "Advance"
   },
   "AllocateRequest": {
    "properties": {
     "scenario": {
      "$ref": "#/components/schemas/Scenario"
     },
     "weights": {
      "$ref": "#/components/schemas/Weights",
      "default": {
       "travel_100m": 4.0,
       "wait_min": 0.2,
       "overqualification": 8.0,
       "workload_balance": 5.0,
       "priority_reward": 60.0,
       "stability": 25.0
      }
     },
     "algorithms": {
      "items": {
       "type": "string",
       "maxLength": 40
      },
      "type": "array",
      "maxItems": 5,
      "minItems": 1,
      "title": "Algorithms"
     }
    },
    "type": "object",
    "required": [
     "scenario"
    ],
    "title": "AllocateRequest"
   },
   "AllocationResult": {
    "properties": {
     "algorithm": {
      "type": "string",
      "title": "Algorithm"
     },
     "label": {
      "type": "string",
      "title": "Label"
     },
     "assignments": {
      "items": {
       "$ref": "#/components/schemas/Assignment"
      },
      "type": "array",
      "title": "Assignments"
     },
     "unassigned": {
      "items": {
       "$ref": "#/components/schemas/Unassigned"
      },
      "type": "array",
      "title": "Unassigned"
     },
     "routes": {
      "items": {
       "$ref": "#/components/schemas/Route"
      },
      "type": "array",
      "title": "Routes"
     },
     "metrics": {
      "additionalProperties": {
       "anyOf": [
        {
         "type": "number"
        },
        {
         "type": "null"
        }
       ]
      },
      "type": "object",
      "title": "Metrics",
      "description": "null where a metric has no basis yet, e.g. critical_coverage_pct with no priority-3 work"
     },
     "solver": {
      "additionalProperties": true,
      "type": "object",
      "title": "Solver",
      "description": "search statistics, e.g. iterations"
     }
    },
    "type": "object",
    "required": [
     "algorithm",
     "label",
     "assignments",
     "unassigned",
     "routes",
     "metrics"
    ],
    "title": "AllocationResult"
   },
   "Answer": {
    "properties": {
     "answer": {
      "type": "string",
      "title": "Answer"
     },
     "intent": {
      "type": "string",
      "title": "Intent"
     },
     "citations": {
      "items": {
       "$ref": "#/components/schemas/Citation"
      },
      "type": "array",
      "title": "Citations",
      "default": []
     },
     "actions": {
      "items": {
       "$ref": "#/components/schemas/Action"
      },
      "type": "array",
      "title": "Actions",
      "default": []
     },
     "suggestions": {
      "items": {
       "type": "string"
      },
      "type": "array",
      "title": "Suggestions",
      "default": []
     },
     "grounded": {
      "type": "boolean",
      "title": "Grounded",
      "default": true
     },
     "provider": {
      "type": "string",
      "title": "Provider",
      "default": "local"
     },
     "fallback_reason": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Fallback Reason",
      "description": "why an optional LLM rewrite was not used and the grounded answer is shown instead"
     }
    },
    "type": "object",
    "required": [
     "answer",
     "intent"
    ],
    "title": "Answer"
   },
   "AskContext": {
    "properties": {
     "fab_id": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Fab Id"
     },
     "scenario": {
      "anyOf": [
       {
        "$ref": "#/components/schemas/Scenario"
       },
       {
        "type": "null"
       }
      ]
     },
     "weights": {
      "anyOf": [
       {
        "$ref": "#/components/schemas/Weights"
       },
       {
        "type": "null"
       }
      ]
     },
     "tab": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Tab"
     },
     "active": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Active",
      "description": "strategy shown on the floor plan"
     },
     "selection": {
      "anyOf": [
       {
        "additionalProperties": true,
        "type": "object"
       },
       {
        "type": "null"
       }
      ],
      "title": "Selection",
      "description": "{\"type\": \"job\"|\"engineer\", \"id\": \"...\"}"
     },
     "recommendation": {
      "anyOf": [
       {
        "additionalProperties": true,
        "type": "object"
       },
       {
        "type": "null"
       }
      ],
      "title": "Recommendation",
      "description": "what the UI recommends: algorithm and goal (checked and recomputed on the server)"
     }
    },
    "type": "object",
    "title": "AskContext",
    "description": "What the user is looking at. Everything is optional; more context, better answers."
   },
   "AskRequest": {
    "properties": {
     "question": {
      "type": "string",
      "maxLength": 500,
      "minLength": 1,
      "title": "Question"
     },
     "context": {
      "$ref": "#/components/schemas/AskContext",
      "default": {}
     }
    },
    "type": "object",
    "required": [
     "question"
    ],
    "title": "AskRequest"
   },
   "Assignment": {
    "properties": {
     "job_id": {
      "type": "string",
      "title": "Job Id"
     },
     "tech_id": {
      "type": "string",
      "title": "Tech Id"
     },
     "sequence": {
      "type": "integer",
      "title": "Sequence"
     },
     "arrival": {
      "type": "number",
      "title": "Arrival"
     },
     "start": {
      "type": "number",
      "title": "Start"
     },
     "end": {
      "type": "number",
      "title": "End"
     },
     "travel_m": {
      "type": "number",
      "title": "Travel M"
     },
     "cost": {
      "type": "number",
      "title": "Cost"
     },
     "cost_breakdown": {
      "additionalProperties": {
       "type": "number"
      },
      "type": "object",
      "title": "Cost Breakdown"
     },
     "explanation": {
      "type": "string",
      "title": "Explanation"
     },
     "alternatives": {
      "items": {
       "$ref": "#/components/schemas/Candidate"
      },
      "type": "array",
      "title": "Alternatives",
      "default": []
     },
     "rejections": {
      "additionalProperties": {
       "type": "integer"
      },
      "type": "object",
      "title": "Rejections",
      "default": {}
     }
    },
    "type": "object",
    "required": [
     "job_id",
     "tech_id",
     "sequence",
     "arrival",
     "start",
     "end",
     "travel_m",
     "cost",
     "cost_breakdown",
     "explanation"
    ],
    "title": "Assignment"
   },
   "BenchmarkRequest": {
    "properties": {
     "fab_id": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Fab Id"
     },
     "preset": {
      "type": "string",
      "title": "Preset",
      "default": "normal"
     },
     "seeds": {
      "type": "integer",
      "maximum": 30.0,
      "minimum": 1.0,
      "title": "Seeds",
      "default": 10
     },
     "n_engineers": {
      "type": "integer",
      "minimum": 1.0,
      "title": "N Engineers",
      "default": 14
     },
     "n_jobs": {
      "type": "integer",
      "minimum": 1.0,
      "title": "N Jobs",
      "default": 45
     },
     "weights": {
      "$ref": "#/components/schemas/Weights",
      "default": {
       "travel_100m": 4.0,
       "wait_min": 0.2,
       "overqualification": 8.0,
       "workload_balance": 5.0,
       "priority_reward": 60.0,
       "stability": 25.0
      }
     },
     "algorithms": {
      "items": {
       "type": "string",
       "maxLength": 40
      },
      "type": "array",
      "maxItems": 5,
      "minItems": 1,
      "title": "Algorithms"
     }
    },
    "type": "object",
    "title": "BenchmarkRequest"
   },
   "Candidate": {
    "properties": {
     "tech_id": {
      "type": "string",
      "title": "Tech Id"
     },
     "cost": {
      "type": "number",
      "title": "Cost"
     }
    },
    "type": "object",
    "required": [
     "tech_id",
     "cost"
    ],
    "title": "Candidate"
   },
   "Citation": {
    "properties": {
     "slug": {
      "type": "string",
      "title": "Slug"
     },
     "title": {
      "type": "string",
      "title": "Title"
     },
     "heading": {
      "type": "string",
      "title": "Heading"
     },
     "anchor": {
      "type": "string",
      "title": "Anchor",
      "default": ""
     }
    },
    "type": "object",
    "required": [
     "slug",
     "title",
     "heading"
    ],
    "title": "Citation"
   },
   "CreateShift": {
    "properties": {
     "scenario": {
      "$ref": "#/components/schemas/Scenario"
     },
     "weights": {
      "$ref": "#/components/schemas/Weights",
      "default": {
       "travel_100m": 4.0,
       "wait_min": 0.2,
       "overqualification": 8.0,
       "workload_balance": 5.0,
       "priority_reward": 60.0,
       "stability": 25.0
      }
     },
     "algorithm": {
      "type": "string",
      "title": "Algorithm",
      "default": "alns"
     }
    },
    "type": "object",
    "required": [
     "scenario"
    ],
    "title": "CreateShift"
   },
   "DemoSignIn": {
    "properties": {
     "role": {
      "type": "string",
      "enum": [
       "viewer",
       "dispatcher"
      ],
      "title": "Role",
      "default": "dispatcher"
     }
    },
    "type": "object",
    "title": "DemoSignIn"
   },
   "Engineer": {
    "properties": {
     "id": {
      "type": "string",
      "title": "Id"
     },
     "name": {
      "type": "string",
      "title": "Name"
     },
     "x": {
      "type": "number",
      "title": "X",
      "description": "metres east on the floor plan"
     },
     "y": {
      "type": "number",
      "title": "Y",
      "description": "metres north on the floor plan"
     },
     "skills": {
      "patternProperties": {
       "^[a-z][a-z0-9_]*$": {
        "type": "integer"
       }
      },
      "propertyNames": {
       "maxLength": 40
      },
      "type": "object",
      "title": "Skills",
      "description": "tool family -> certification level 1..3"
     },
     "shift_start": {
      "type": "integer",
      "title": "Shift Start",
      "default": 0
     },
     "shift_end": {
      "type": "integer",
      "title": "Shift End",
      "default": 720
     },
     "max_jobs": {
      "type": "integer",
      "title": "Max Jobs",
      "default": 5
     }
    },
    "type": "object",
    "required": [
     "id",
     "name",
     "x",
     "y",
     "skills"
    ],
    "title": "Engineer"
   },
   "Feedback": {
    "properties": {
     "question": {
      "type": "string",
      "maxLength": 500,
      "minLength": 1,
      "title": "Question"
     },
     "intent": {
      "type": "string",
      "maxLength": 40,
      "title": "Intent",
      "description": "the intent the answer carried; one of the assistant's intents"
     },
     "helpful": {
      "type": "boolean",
      "title": "Helpful"
     },
     "comment": {
      "type": "string",
      "maxLength": 500,
      "title": "Comment",
      "default": ""
     },
     "citations": {
      "items": {
       "type": "string"
      },
      "type": "array",
      "maxItems": 10,
      "title": "Citations",
      "description": "cited article slugs"
     },
     "provider": {
      "type": "string",
      "maxLength": 80,
      "pattern": "^(local|anthropic:[A-Za-z0-9._-]+|ollama:[A-Za-z0-9._:-]+)$",
      "title": "Provider",
      "default": "local"
     }
    },
    "type": "object",
    "required": [
     "question",
     "intent",
     "helpful"
    ],
    "title": "Feedback"
   },
   "GapRequest": {
    "properties": {
     "fab_id": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Fab Id"
     },
     "seeds": {
      "type": "integer",
      "maximum": 10.0,
      "minimum": 1.0,
      "title": "Seeds",
      "default": 5
     },
     "n_engineers": {
      "type": "integer",
      "maximum": 6.0,
      "minimum": 1.0,
      "title": "N Engineers",
      "default": 4
     },
     "n_jobs": {
      "type": "integer",
      "maximum": 16.0,
      "minimum": 1.0,
      "title": "N Jobs",
      "default": 12
     },
     "preset": {
      "type": "string",
      "title": "Preset",
      "default": "normal"
     },
     "weights": {
      "$ref": "#/components/schemas/Weights",
      "default": {
       "travel_100m": 4.0,
       "wait_min": 0.2,
       "overqualification": 8.0,
       "workload_balance": 5.0,
       "priority_reward": 60.0,
       "stability": 25.0
      }
     }
    },
    "type": "object",
    "title": "GapRequest"
   },
   "GenerateRequest": {
    "properties": {
     "fab_id": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Fab Id"
     },
     "seed": {
      "type": "integer",
      "maximum": 1000000.0,
      "minimum": 0.0,
      "title": "Seed",
      "default": 7
     },
     "n_engineers": {
      "type": "integer",
      "minimum": 1.0,
      "title": "N Engineers",
      "default": 14
     },
     "n_jobs": {
      "type": "integer",
      "minimum": 1.0,
      "title": "N Jobs",
      "default": 45
     },
     "preset": {
      "type": "string",
      "title": "Preset",
      "default": "normal"
     }
    },
    "type": "object",
    "title": "GenerateRequest"
   },
   "HTTPValidationError": {
    "properties": {
     "detail": {
      "items": {
       "$ref": "#/components/schemas/ValidationError"
      },
      "type": "array",
      "title": "Detail"
     }
    },
    "type": "object",
    "title": "HTTPValidationError"
   },
   "Job": {
    "properties": {
     "id": {
      "type": "string",
      "title": "Id"
     },
     "x": {
      "type": "number",
      "title": "X",
      "description": "metres east on the floor plan"
     },
     "y": {
      "type": "number",
      "title": "Y",
      "description": "metres north on the floor plan"
     },
     "skill": {
      "type": "string",
      "maxLength": 40,
      "pattern": "^[a-z][a-z0-9_]*$",
      "title": "Skill"
     },
     "min_level": {
      "type": "integer",
      "maximum": 3.0,
      "minimum": 1.0,
      "title": "Min Level",
      "default": 1
     },
     "priority": {
      "type": "integer",
      "maximum": 3.0,
      "minimum": 1.0,
      "title": "Priority",
      "description": "1 PM, 2 tool down, 3 bottleneck tool down",
      "default": 2
     },
     "kind": {
      "type": "string",
      "enum": [
       "down",
       "pm"
      ],
      "title": "Kind",
      "default": "down"
     },
     "earliest": {
      "type": "integer",
      "title": "Earliest",
      "description": "earliest start, minutes"
     },
     "latest": {
      "type": "integer",
      "title": "Latest",
      "description": "latest start, minutes"
     },
     "duration": {
      "type": "integer",
      "exclusiveMinimum": 0.0,
      "title": "Duration",
      "default": 60
     },
     "tool": {
      "type": "string",
      "title": "Tool",
      "default": ""
     },
     "symptom": {
      "type": "string",
      "title": "Symptom",
      "description": "free-text fault description, used for repair-history retrieval",
      "default": ""
     }
    },
    "type": "object",
    "required": [
     "id",
     "x",
     "y",
     "skill",
     "earliest",
     "latest"
    ],
    "title": "Job"
   },
   "PlanRequest": {
    "properties": {
     "scenario": {
      "$ref": "#/components/schemas/Scenario"
     },
     "weights": {
      "$ref": "#/components/schemas/Weights",
      "default": {
       "travel_100m": 4.0,
       "wait_min": 0.2,
       "overqualification": 8.0,
       "workload_balance": 5.0,
       "priority_reward": 60.0,
       "stability": 25.0
      }
     },
     "algorithm": {
      "type": "string",
      "title": "Algorithm",
      "default": "pyvrp"
     }
    },
    "type": "object",
    "required": [
     "scenario"
    ],
    "title": "PlanRequest"
   },
   "ReportJob": {
    "properties": {
     "job": {
      "$ref": "#/components/schemas/Job"
     }
    },
    "type": "object",
    "required": [
     "job"
    ],
    "title": "ReportJob"
   },
   "Route": {
    "properties": {
     "tech_id": {
      "type": "string",
      "title": "Tech Id"
     },
     "stops": {
      "items": {
       "$ref": "#/components/schemas/RouteStop"
      },
      "type": "array",
      "title": "Stops"
     },
     "metres": {
      "type": "number",
      "title": "Metres"
     },
     "end_time": {
      "type": "number",
      "title": "End Time"
     }
    },
    "type": "object",
    "required": [
     "tech_id",
     "stops",
     "metres",
     "end_time"
    ],
    "title": "Route"
   },
   "RouteStop": {
    "properties": {
     "job_id": {
      "type": "string",
      "title": "Job Id"
     },
     "arrival": {
      "type": "number",
      "title": "Arrival"
     },
     "start": {
      "type": "number",
      "title": "Start"
     },
     "end": {
      "type": "number",
      "title": "End"
     },
     "locked": {
      "type": "boolean",
      "title": "Locked",
      "default": false
     }
    },
    "type": "object",
    "required": [
     "job_id",
     "arrival",
     "start",
     "end"
    ],
    "title": "RouteStop"
   },
   "Scenario": {
    "properties": {
     "fab_id": {
      "type": "string",
      "title": "Fab Id",
      "description": "which fab profile this shift belongs to",
      "default": "fab1-300mm-logic"
     },
     "engineers": {
      "items": {
       "$ref": "#/components/schemas/Engineer"
      },
      "type": "array",
      "title": "Engineers"
     },
     "jobs": {
      "items": {
       "$ref": "#/components/schemas/Job"
      },
      "type": "array",
      "title": "Jobs"
     },
     "settings": {
      "$ref": "#/components/schemas/Settings",
      "default": {
       "walk_m_per_min": 60.0,
       "floor_width": 400.0,
       "floor_height": 240.0
      }
     }
    },
    "type": "object",
    "required": [
     "engineers",
     "jobs"
    ],
    "title": "Scenario"
   },
   "Settings": {
    "properties": {
     "walk_m_per_min": {
      "type": "number",
      "exclusiveMinimum": 0.0,
      "title": "Walk M Per Min",
      "description": "walking speed in cleanroom garb",
      "default": 60.0
     },
     "floor_width": {
      "type": "number",
      "title": "Floor Width",
      "default": 400.0
     },
     "floor_height": {
      "type": "number",
      "title": "Floor Height",
      "default": 240.0
     }
    },
    "type": "object",
    "title": "Settings"
   },
   "SimilarRequest": {
    "properties": {
     "fab_id": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Fab Id"
     },
     "family": {
      "type": "string",
      "title": "Family"
     },
     "symptom": {
      "type": "string",
      "maxLength": 500,
      "minLength": 3,
      "title": "Symptom"
     },
     "k": {
      "type": "integer",
      "maximum": 50.0,
      "minimum": 1.0,
      "title": "K",
      "default": 12
     },
     "roster": {
      "anyOf": [
       {
        "items": {
         "type": "string"
        },
        "type": "array",
        "maxItems": 500
       },
       {
        "type": "null"
       }
      ],
      "title": "Roster",
      "description": "engineer ids on shift; when given, experienced_engineers lists only these"
     }
    },
    "type": "object",
    "required": [
     "family",
     "symptom"
    ],
    "title": "SimilarRequest"
   },
   "ToolDown": {
    "properties": {
     "fab_id": {
      "type": "string",
      "title": "Fab Id"
     },
     "job": {
      "$ref": "#/components/schemas/Job"
     },
     "shift_id": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Shift Id"
     }
    },
    "type": "object",
    "required": [
     "fab_id",
     "job"
    ],
    "title": "ToolDown"
   },
   "Unassigned": {
    "properties": {
     "job_id": {
      "type": "string",
      "title": "Job Id"
     },
     "reason": {
      "type": "string",
      "title": "Reason"
     },
     "rejections": {
      "additionalProperties": {
       "type": "integer"
      },
      "type": "object",
      "title": "Rejections"
     }
    },
    "type": "object",
    "required": [
     "job_id",
     "reason",
     "rejections"
    ],
    "title": "Unassigned"
   },
   "User": {
    "properties": {
     "id": {
      "type": "string",
      "title": "Id"
     },
     "email": {
      "type": "string",
      "title": "Email"
     },
     "role": {
      "type": "string",
      "enum": [
       "viewer",
       "dispatcher"
      ],
      "title": "Role"
     },
     "fabs": {
      "items": {
       "type": "string"
      },
      "type": "array",
      "title": "Fabs"
     },
     "provider": {
      "type": "string",
      "enum": [
       "demo",
       "supabase",
       "guest",
       "integration"
      ],
      "title": "Provider"
     }
    },
    "type": "object",
    "required": [
     "id",
     "email",
     "role",
     "fabs",
     "provider"
    ],
    "title": "User"
   },
   "ValidationError": {
    "properties": {
     "loc": {
      "items": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "integer"
        }
       ]
      },
      "type": "array",
      "title": "Location"
     },
     "msg": {
      "type": "string",
      "title": "Message"
     },
     "type": {
      "type": "string",
      "title": "Error Type"
     },
     "input": {
      "title": "Input"
     },
     "ctx": {
      "type": "object",
      "title": "Context"
     }
    },
    "type": "object",
    "required": [
     "loc",
     "msg",
     "type"
    ],
    "title": "ValidationError"
   },
   "Weights": {
    "properties": {
     "travel_100m": {
      "type": "number",
      "minimum": 0.0,
      "title": "Travel 100M",
      "description": "per 100 m walked on the floor",
      "default": 4.0
     },
     "wait_min": {
      "type": "number",
      "minimum": 0.0,
      "title": "Wait Min",
      "description": "per minute idle at a tool before a window opens (not before the first job)",
      "default": 0.2
     },
     "overqualification": {
      "type": "number",
      "minimum": 0.0,
      "title": "Overqualification",
      "description": "per level above the job's need",
      "default": 8.0
     },
     "workload_balance": {
      "type": "number",
      "minimum": 0.0,
      "title": "Workload Balance",
      "description": "per job the engineer already holds",
      "default": 5.0
     },
     "priority_reward": {
      "type": "number",
      "minimum": 0.0,
      "title": "Priority Reward",
      "description": "reward per priority point served",
      "default": 60.0
     },
     "stability": {
      "type": "number",
      "minimum": 0.0,
      "title": "Stability",
      "description": "live mode: per job moved to a different engineer on re-plan",
      "default": 25.0
     }
    },
    "type": "object",
    "title": "Weights",
    "description": "Soft-constraint weights. Everything is expressed in 'cost points'."
   }
  }
 }
}