openapi: 3.1.1
info:
  title: Asmaliana — Melaka Travel Data & Tools API
  version: 1.0.0
  summary: Evidence-aware Melaka place facts, visit-window checks, weather advisory status, and deterministic single-day itinerary tools.
  description: Read-only P0 API. The default dataset is reviewed verified data and is not suitable for travel decisions. No booking, payment, inventory, email, inquiry, A2A, or other write action is performed.
  license:
    name: Project code licence only; source data rights are recorded per source.
servers:
  - url: https://asmaliana.com
tags:
  - name: Tools
  - name: Operations
paths:
  /api/v1/places:
    get:
      operationId: search_melaka_places
      summary: Search covered places
      description: Search covered Melaka place facts with deterministic filters and evidence-aware matching. Use it to discover stable place IDs. Do not treat missing evidence as a passed hard constraint or use it for live inventory.
      tags:
        - Tools
      security:
        - {}
        - apiKey: []
      x-read-only: true
      x-side-effects: No business-state side effects; quota accounting only.
      x-credits: 1
      x-required-scope: facts:read
      responses:
        "200":
          description: Search completed. Inspect result.items, result.page, evidence, and verification status.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SearchPlacesSuccessEnvelope"
        "400":
          description: Malformed request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "401":
          description: Invalid, expired, or revoked credential
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "403":
          description: Credential lacks the tool scope
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "409":
          description: Cursor data version conflict
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "422":
          description: Cursor is outside the current result range
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "429":
          description: A credit, per-tool, concurrency, or deployment-wide quota is exhausted
          headers:
            Retry-After:
              schema:
                type: integer
                minimum: 1
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "500":
          description: Internal error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
      parameters:
        - name: query
          in: query
          schema:
            type: string
            maxLength: 200
            default: ""
        - name: category
          in: query
          schema:
            type: string
            enum:
              - museum
              - heritage
              - food
              - park
              - market
              - activity
              - night
        - name: step_free_entry
          in: query
          description: When true, require positive reviewed step-free-entry evidence. Missing evidence does not count as a match.
          schema:
            type: boolean
        - name: accessible_toilet
          in: query
          description: When true, require positive reviewed accessible-toilet evidence. Missing evidence does not count as a match.
          schema:
            type: boolean
        - name: halal_official
          in: query
          description: When true, require applicable reviewed official halal-certificate evidence. It is not inferred from cuisine or prose.
          schema:
            type: boolean
        - name: locale
          in: query
          schema:
            type: string
            enum:
              - en
              - zh
            default: en
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 50
            default: 10
        - name: cursor
          in: query
          schema:
            type: string
            maxLength: 500
        - name: include_unverified
          in: query
          schema:
            type: boolean
            default: false
        - name: fields
          in: query
          description: Comma-separated projection whitelist.
          schema:
            type: string
  /api/v1/places/{place_id}:
    get:
      operationId: get_melaka_place_facts
      summary: Get one place fact view
      description: Retrieve rights-filtered facts, field evidence, schedules, offers, services, facilities, and revisions for one covered place ID. Do not use it to confirm live operation, ticket inventory, or a booking.
      tags:
        - Tools
      security:
        - {}
        - apiKey: []
      x-read-only: true
      x-side-effects: No business-state side effects; quota accounting only.
      x-credits: 1
      x-required-scope: facts:read
      responses:
        "200":
          description: Rights-filtered place projection completed. Inspect field status, evidence, and limitations.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlaceFactsSuccessEnvelope"
        "400":
          description: Malformed request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "401":
          description: Invalid, expired, or revoked credential
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "403":
          description: Credential lacks the tool scope
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "404":
          description: Place, service, or public entity not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "429":
          description: A credit, per-tool, concurrency, or deployment-wide quota is exhausted
          headers:
            Retry-After:
              schema:
                type: integer
                minimum: 1
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "500":
          description: Internal error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
      parameters:
        - name: place_id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-z0-9][a-z0-9_-]{2,79}$
        - name: locale
          in: query
          schema:
            type: string
            enum:
              - en
              - zh
            default: en
        - name: fields
          in: query
          schema:
            type: string
            description: Comma-separated projection whitelist.
  /api/v1/visit-window/check:
    post:
      operationId: check_melaka_visit_window
      summary: Check a proposed visit window
      description: Check whether an arrival and duration fit published schedules, exceptions, last entry, and known service status. Use before finalizing an itinerary. This is not a real-time opening, inventory, or booking confirmation.
      tags:
        - Tools
      security:
        - {}
        - apiKey: []
      x-read-only: true
      x-side-effects: No business-state side effects; quota accounting only.
      x-credits: 2
      x-required-scope: planner:run
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $schema: https://json-schema.org/draft/2020-12/schema
              type: object
              properties:
                place_id:
                  type: string
                  pattern: ^[a-z0-9][a-z0-9_-]{2,79}$
                service_id:
                  type: string
                  pattern: ^[a-z0-9][a-z0-9_-]{2,79}$
                arrival_at:
                  type: string
                  format: date-time
                  pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
                duration_minutes:
                  type: integer
                  minimum: 1
                  maximum: 720
                party:
                  default:
                    adults: 1
                    children: []
                  type: object
                  properties:
                    adults:
                      default: 1
                      type: integer
                      minimum: 0
                      maximum: 30
                    children:
                      default: []
                      maxItems: 20
                      type: array
                      items:
                        type: object
                        properties:
                          age:
                            anyOf:
                              - type: integer
                                minimum: 0
                                maximum: 17
                              - type: "null"
                        required:
                          - age
                constraints:
                  default: {}
                  type: object
                  properties:
                    step_free_entry:
                      type: boolean
                    accessible_toilet:
                      type: boolean
                    halal_official:
                      type: boolean
                  additionalProperties: false
              required:
                - place_id
                - arrival_at
                - duration_minutes
      responses:
        "200":
          description: Visit-window computation completed. Inspect result.assessment, evaluation_scope, unknowns, and unchecked dimensions.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VisitWindowSuccessEnvelope"
        "400":
          description: Malformed request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "401":
          description: Invalid, expired, or revoked credential
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "403":
          description: Credential lacks the tool scope
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "404":
          description: Place, service, or public entity not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "413":
          description: Request body is too large
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "429":
          description: A credit, per-tool, concurrency, or deployment-wide quota is exhausted
          headers:
            Retry-After:
              schema:
                type: integer
                minimum: 1
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "500":
          description: Internal error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /api/v1/weather/advisory:
    get:
      operationId: get_melaka_weather_advisory
      summary: Get Melaka-area weather advisory status
      description: Return the configured Melaka-area weather advisory state, forecast coverage, spatial granularity, validity, and evidence. Do not interpret area forecasts as venue conditions or absence of warnings as safety.
      tags:
        - Tools
      security:
        - {}
        - apiKey: []
      x-read-only: true
      x-side-effects: No business-state side effects; quota accounting only.
      x-credits: 1
      x-required-scope: weather:read
      responses:
        "200":
          description: Weather provider lookup completed. Inspect result.provider_state, advisory validity, and limitations.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WeatherAdvisorySuccessEnvelope"
        "400":
          description: Malformed request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "401":
          description: Invalid, expired, or revoked credential
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "403":
          description: Credential lacks the tool scope
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "429":
          description: A credit, per-tool, concurrency, or deployment-wide quota is exhausted
          headers:
            Retry-After:
              schema:
                type: integer
                minimum: 1
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "500":
          description: Internal error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "503":
          description: Required provider unavailable
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
      parameters:
        - name: location_id
          in: query
          required: true
          schema:
            const: melaka_central
        - name: date
          in: query
          required: true
          schema:
            type: string
            format: date
        - name: locale
          in: query
          schema:
            type: string
            enum:
              - en
              - zh
            default: en
  /api/v1/itineraries/plan:
    post:
      operationId: plan_melaka_day
      summary: Plan a single Melaka day
      description: Build a deterministic single-day candidate itinerary of at most eight stops from covered facts and explicit route assumptions. Use for a practical draft. It does not promise a globally optimal route or live availability.
      tags:
        - Tools
      security:
        - {}
        - apiKey: []
      x-read-only: true
      x-side-effects: No business-state side effects; quota accounting only.
      x-credits: 5
      x-required-scope: planner:run
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $schema: https://json-schema.org/draft/2020-12/schema
              type: object
              properties:
                start_at:
                  type: string
                  format: date-time
                  pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
                end_at:
                  type: string
                  format: date-time
                  pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
                start_place_id:
                  type: string
                  pattern: ^[a-z0-9][a-z0-9_-]{2,79}$
                start_coordinates:
                  type: object
                  properties:
                    latitude:
                      type: number
                      minimum: -90
                      maximum: 90
                    longitude:
                      type: number
                      minimum: -180
                      maximum: 180
                  required:
                    - latitude
                    - longitude
                party:
                  default:
                    adults: 1
                    children: []
                  type: object
                  properties:
                    adults:
                      default: 1
                      type: integer
                      minimum: 0
                      maximum: 30
                    children:
                      default: []
                      maxItems: 20
                      type: array
                      items:
                        type: object
                        properties:
                          age:
                            anyOf:
                              - type: integer
                                minimum: 0
                                maximum: 17
                              - type: "null"
                        required:
                          - age
                budget:
                  type: object
                  properties:
                    amount_minor:
                      type: integer
                      minimum: 0
                      maximum: 9007199254740991
                    currency:
                      type: string
                      minLength: 3
                      maxLength: 3
                  required:
                    - amount_minor
                    - currency
                hard_constraints:
                  default: {}
                  type: object
                  properties:
                    step_free_entry:
                      type: boolean
                    accessible_toilet:
                      type: boolean
                    halal_official:
                      type: boolean
                  additionalProperties: false
                preferences:
                  default: []
                  maxItems: 10
                  type: array
                  items:
                    type: string
                    enum:
                      - indoor
                      - outdoor
                      - heritage
                      - food
                      - low_cost
                candidate_place_ids:
                  maxItems: 20
                  type: array
                  items:
                    type: string
                    pattern: ^[a-z0-9][a-z0-9_-]{2,79}$
                max_stops:
                  default: 4
                  type: integer
                  minimum: 1
                  maximum: 8
              required:
                - start_at
                - end_at
              oneOf:
                - required:
                    - start_place_id
                  not:
                    required:
                      - start_coordinates
                - required:
                    - start_coordinates
                  not:
                    required:
                      - start_place_id
      responses:
        "200":
          description: Deterministic planning computation completed. Inspect result.assessment, route assumptions, unknowns, and unchecked dimensions.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlanDaySuccessEnvelope"
        "400":
          description: Malformed request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "401":
          description: Invalid, expired, or revoked credential
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "403":
          description: Credential lacks the tool scope
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "404":
          description: Start or candidate place not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "413":
          description: Request body is too large
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "422":
          description: Unsupported itinerary time range
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "429":
          description: A credit, per-tool, concurrency, or deployment-wide quota is exhausted
          headers:
            Retry-After:
              schema:
                type: integer
                minimum: 1
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "500":
          description: Internal error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "503":
          description: Required provider unavailable
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /api/v1/itineraries/validate:
    post:
      operationId: validate_melaka_itinerary
      summary: Validate a structured itinerary
      description: Validate up to eight structured stops against published windows, services, costs, constraints, and supplied transport estimates. Use for an existing itinerary. Free-text parsing, booking, and live operation checks are not supported.
      tags:
        - Tools
      security:
        - {}
        - apiKey: []
      x-read-only: true
      x-side-effects: No business-state side effects; quota accounting only.
      x-credits: 3
      x-required-scope: planner:run
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $schema: https://json-schema.org/draft/2020-12/schema
              type: object
              properties:
                start_at:
                  type: string
                  format: date-time
                  pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
                end_at:
                  type: string
                  format: date-time
                  pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
                stops:
                  minItems: 1
                  maxItems: 8
                  type: array
                  items:
                    type: object
                    properties:
                      place_id:
                        type: string
                        pattern: ^[a-z0-9][a-z0-9_-]{2,79}$
                      service_id:
                        type: string
                        pattern: ^[a-z0-9][a-z0-9_-]{2,79}$
                      arrival_at:
                        type: string
                        format: date-time
                        pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
                      duration_minutes:
                        type: integer
                        minimum: 1
                        maximum: 720
                    required:
                      - place_id
                      - arrival_at
                      - duration_minutes
                party:
                  default:
                    adults: 1
                    children: []
                  type: object
                  properties:
                    adults:
                      default: 1
                      type: integer
                      minimum: 0
                      maximum: 30
                    children:
                      default: []
                      maxItems: 20
                      type: array
                      items:
                        type: object
                        properties:
                          age:
                            anyOf:
                              - type: integer
                                minimum: 0
                                maximum: 17
                              - type: "null"
                        required:
                          - age
                budget:
                  type: object
                  properties:
                    amount_minor:
                      type: integer
                      minimum: 0
                      maximum: 9007199254740991
                    currency:
                      type: string
                      minLength: 3
                      maxLength: 3
                  required:
                    - amount_minor
                    - currency
                constraints:
                  default: {}
                  type: object
                  properties:
                    step_free_entry:
                      type: boolean
                    accessible_toilet:
                      type: boolean
                    halal_official:
                      type: boolean
                  additionalProperties: false
                transport_estimates:
                  default: []
                  maxItems: 8
                  type: array
                  items:
                    type: object
                    properties:
                      from_place_id:
                        type: string
                        pattern: ^[a-z0-9][a-z0-9_-]{2,79}$
                      to_place_id:
                        type: string
                        pattern: ^[a-z0-9][a-z0-9_-]{2,79}$
                      min_minutes:
                        type: integer
                        minimum: 0
                        maximum: 9007199254740991
                      max_minutes:
                        type: integer
                        minimum: 0
                        maximum: 9007199254740991
                      source:
                        type: string
                        const: user_supplied
                    required:
                      - from_place_id
                      - to_place_id
                      - min_minutes
                      - max_minutes
                      - source
              required:
                - start_at
                - end_at
                - stops
      responses:
        "200":
          description: Itinerary validation completed. Inspect result.assessment, stop checks, transport assumptions, and unchecked dimensions.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ItineraryValidationSuccessEnvelope"
        "400":
          description: Malformed request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "401":
          description: Invalid, expired, or revoked credential
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "403":
          description: Credential lacks the tool scope
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "404":
          description: Place, service, or public entity not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "413":
          description: Request body is too large
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "422":
          description: Unsupported itinerary time range
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "429":
          description: A credit, per-tool, concurrency, or deployment-wide quota is exhausted
          headers:
            Retry-After:
              schema:
                type: integer
                minimum: 1
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "500":
          description: Internal error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /api/v1/meta:
    get:
      operationId: get_service_metadata
      tags:
        - Operations
      responses:
        "200":
          description: Capability and data metadata, including immutable revision publication clocks
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ServiceMetadataSuccessEnvelope"
  /api/v1/health:
    get:
      operationId: get_health
      tags:
        - Operations
      responses:
        "200":
          description: Minimal process health
  /api/v1/status:
    get:
      operationId: get_public_status
      tags:
        - Operations
      responses:
        "200":
          description: Public capability, provider state, and immutable dataset publication clocks
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ServiceStatusSuccessEnvelope"
  /mcp:
    post:
      operationId: mcp_streamable_http
      tags:
        - Tools
      summary: Remote MCP Streamable HTTP transport
      description: Official MCP TypeScript SDK transport for initialize, tools/list, and tools/call. Protocol-level envelopes are not REST envelopes.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        "200":
          description: MCP protocol response (JSON or SSE according to negotiation)
        "400":
          description: MCP transport or JSON-RPC error
components:
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      bearerFormat: asm_live_…
  schemas:
    SuccessEnvelope:
      oneOf:
        - $ref: "#/components/schemas/SearchPlacesSuccessEnvelope"
        - $ref: "#/components/schemas/PlaceFactsSuccessEnvelope"
        - $ref: "#/components/schemas/VisitWindowSuccessEnvelope"
        - $ref: "#/components/schemas/WeatherAdvisorySuccessEnvelope"
        - $ref: "#/components/schemas/PlanDaySuccessEnvelope"
        - $ref: "#/components/schemas/ItineraryValidationSuccessEnvelope"
    SearchPlacesSuccessEnvelope:
      type: object
      additionalProperties: false
      required:
        - success
        - request_id
        - schema_version
        - data_version
        - data_mode
        - result
        - sources
        - timestamp
        - cache
        - warnings
        - next_actions
      properties:
        success:
          const: true
        request_id:
          type: string
          format: uuid
        schema_version:
          const: "1.0"
        data_version:
          type: string
        data_mode:
          enum:
            - demo
            - verified
        result:
          $ref: "#/components/schemas/SearchPlacesResult"
        sources:
          type: array
          items:
            $ref: "#/components/schemas/Source"
        timestamp:
          type: string
          format: date-time
        cache:
          type: object
          additionalProperties: false
          required:
            - hit
            - age_seconds
            - expires_at
          properties:
            hit:
              type: boolean
            age_seconds:
              type: integer
              minimum: 0
            expires_at:
              type:
                - string
                - "null"
              format: date-time
        warnings:
          type: array
          items:
            type: string
        next_actions:
          type: array
          items:
            type: object
            additionalProperties: false
            required:
              - tool
              - reason
            properties:
              tool:
                enum:
                  - search_melaka_places
                  - get_melaka_place_facts
                  - check_melaka_visit_window
                  - get_melaka_weather_advisory
                  - plan_melaka_day
                  - validate_melaka_itinerary
              reason:
                type: string
    PlaceFactsSuccessEnvelope:
      type: object
      additionalProperties: false
      required:
        - success
        - request_id
        - schema_version
        - data_version
        - data_mode
        - result
        - sources
        - timestamp
        - cache
        - warnings
        - next_actions
      properties:
        success:
          const: true
        request_id:
          type: string
          format: uuid
        schema_version:
          const: "1.0"
        data_version:
          type: string
        data_mode:
          enum:
            - demo
            - verified
        result:
          $ref: "#/components/schemas/PlaceFactsResult"
        sources:
          type: array
          items:
            $ref: "#/components/schemas/Source"
        timestamp:
          type: string
          format: date-time
        cache:
          type: object
          additionalProperties: false
          required:
            - hit
            - age_seconds
            - expires_at
          properties:
            hit:
              type: boolean
            age_seconds:
              type: integer
              minimum: 0
            expires_at:
              type:
                - string
                - "null"
              format: date-time
        warnings:
          type: array
          items:
            type: string
        next_actions:
          type: array
          items:
            type: object
            additionalProperties: false
            required:
              - tool
              - reason
            properties:
              tool:
                enum:
                  - search_melaka_places
                  - get_melaka_place_facts
                  - check_melaka_visit_window
                  - get_melaka_weather_advisory
                  - plan_melaka_day
                  - validate_melaka_itinerary
              reason:
                type: string
    VisitWindowSuccessEnvelope:
      type: object
      additionalProperties: false
      required:
        - success
        - request_id
        - schema_version
        - data_version
        - data_mode
        - result
        - sources
        - timestamp
        - cache
        - warnings
        - next_actions
      properties:
        success:
          const: true
        request_id:
          type: string
          format: uuid
        schema_version:
          const: "1.0"
        data_version:
          type: string
        data_mode:
          enum:
            - demo
            - verified
        result:
          $ref: "#/components/schemas/VisitWindowEvaluation"
        sources:
          type: array
          items:
            $ref: "#/components/schemas/Source"
        timestamp:
          type: string
          format: date-time
        cache:
          type: object
          additionalProperties: false
          required:
            - hit
            - age_seconds
            - expires_at
          properties:
            hit:
              type: boolean
            age_seconds:
              type: integer
              minimum: 0
            expires_at:
              type:
                - string
                - "null"
              format: date-time
        warnings:
          type: array
          items:
            type: string
        next_actions:
          type: array
          items:
            type: object
            additionalProperties: false
            required:
              - tool
              - reason
            properties:
              tool:
                enum:
                  - search_melaka_places
                  - get_melaka_place_facts
                  - check_melaka_visit_window
                  - get_melaka_weather_advisory
                  - plan_melaka_day
                  - validate_melaka_itinerary
              reason:
                type: string
    WeatherAdvisorySuccessEnvelope:
      type: object
      additionalProperties: false
      required:
        - success
        - request_id
        - schema_version
        - data_version
        - data_mode
        - result
        - sources
        - timestamp
        - cache
        - warnings
        - next_actions
      properties:
        success:
          const: true
        request_id:
          type: string
          format: uuid
        schema_version:
          const: "1.0"
        data_version:
          type: string
        data_mode:
          enum:
            - demo
            - verified
        result:
          $ref: "#/components/schemas/WeatherAdvisoryResult"
        sources:
          type: array
          items:
            $ref: "#/components/schemas/Source"
        timestamp:
          type: string
          format: date-time
        cache:
          type: object
          additionalProperties: false
          required:
            - hit
            - age_seconds
            - expires_at
          properties:
            hit:
              type: boolean
            age_seconds:
              type: integer
              minimum: 0
            expires_at:
              type:
                - string
                - "null"
              format: date-time
        warnings:
          type: array
          items:
            type: string
        next_actions:
          type: array
          items:
            type: object
            additionalProperties: false
            required:
              - tool
              - reason
            properties:
              tool:
                enum:
                  - search_melaka_places
                  - get_melaka_place_facts
                  - check_melaka_visit_window
                  - get_melaka_weather_advisory
                  - plan_melaka_day
                  - validate_melaka_itinerary
              reason:
                type: string
    PlanDaySuccessEnvelope:
      type: object
      additionalProperties: false
      required:
        - success
        - request_id
        - schema_version
        - data_version
        - data_mode
        - result
        - sources
        - timestamp
        - cache
        - warnings
        - next_actions
      properties:
        success:
          const: true
        request_id:
          type: string
          format: uuid
        schema_version:
          const: "1.0"
        data_version:
          type: string
        data_mode:
          enum:
            - demo
            - verified
        result:
          $ref: "#/components/schemas/PlanDayEvaluation"
        sources:
          type: array
          items:
            $ref: "#/components/schemas/Source"
        timestamp:
          type: string
          format: date-time
        cache:
          type: object
          additionalProperties: false
          required:
            - hit
            - age_seconds
            - expires_at
          properties:
            hit:
              type: boolean
            age_seconds:
              type: integer
              minimum: 0
            expires_at:
              type:
                - string
                - "null"
              format: date-time
        warnings:
          type: array
          items:
            type: string
        next_actions:
          type: array
          items:
            type: object
            additionalProperties: false
            required:
              - tool
              - reason
            properties:
              tool:
                enum:
                  - search_melaka_places
                  - get_melaka_place_facts
                  - check_melaka_visit_window
                  - get_melaka_weather_advisory
                  - plan_melaka_day
                  - validate_melaka_itinerary
              reason:
                type: string
    ItineraryValidationSuccessEnvelope:
      type: object
      additionalProperties: false
      required:
        - success
        - request_id
        - schema_version
        - data_version
        - data_mode
        - result
        - sources
        - timestamp
        - cache
        - warnings
        - next_actions
      properties:
        success:
          const: true
        request_id:
          type: string
          format: uuid
        schema_version:
          const: "1.0"
        data_version:
          type: string
        data_mode:
          enum:
            - demo
            - verified
        result:
          $ref: "#/components/schemas/ItineraryValidation"
        sources:
          type: array
          items:
            $ref: "#/components/schemas/Source"
        timestamp:
          type: string
          format: date-time
        cache:
          type: object
          additionalProperties: false
          required:
            - hit
            - age_seconds
            - expires_at
          properties:
            hit:
              type: boolean
            age_seconds:
              type: integer
              minimum: 0
            expires_at:
              type:
                - string
                - "null"
              format: date-time
        warnings:
          type: array
          items:
            type: string
        next_actions:
          type: array
          items:
            type: object
            additionalProperties: false
            required:
              - tool
              - reason
            properties:
              tool:
                enum:
                  - search_melaka_places
                  - get_melaka_place_facts
                  - check_melaka_visit_window
                  - get_melaka_weather_advisory
                  - plan_melaka_day
                  - validate_melaka_itinerary
              reason:
                type: string
    ServiceMetadataSuccessEnvelope:
      type: object
      additionalProperties: false
      required:
        - success
        - request_id
        - schema_version
        - data_version
        - data_mode
        - result
        - sources
        - timestamp
        - cache
        - warnings
        - next_actions
      properties:
        success:
          const: true
        request_id:
          type: string
          format: uuid
        schema_version:
          const: "1.0"
        data_version:
          type: string
        data_mode:
          enum:
            - demo
            - verified
        result:
          $ref: "#/components/schemas/ServiceMetadataResult"
        sources:
          type: array
          items:
            $ref: "#/components/schemas/Source"
        timestamp:
          type: string
          format: date-time
        cache:
          type: object
          additionalProperties: false
          required:
            - hit
            - age_seconds
            - expires_at
          properties:
            hit:
              type: boolean
            age_seconds:
              type: integer
              minimum: 0
            expires_at:
              type:
                - string
                - "null"
              format: date-time
        warnings:
          type: array
          items:
            type: string
        next_actions:
          type: array
          items:
            type: object
            additionalProperties: false
            required:
              - tool
              - reason
            properties:
              tool:
                enum:
                  - search_melaka_places
                  - get_melaka_place_facts
                  - check_melaka_visit_window
                  - get_melaka_weather_advisory
                  - plan_melaka_day
                  - validate_melaka_itinerary
              reason:
                type: string
    ServiceStatusSuccessEnvelope:
      type: object
      additionalProperties: false
      required:
        - success
        - request_id
        - schema_version
        - data_version
        - data_mode
        - result
        - sources
        - timestamp
        - cache
        - warnings
        - next_actions
      properties:
        success:
          const: true
        request_id:
          type: string
          format: uuid
        schema_version:
          const: "1.0"
        data_version:
          type: string
        data_mode:
          enum:
            - demo
            - verified
        result:
          $ref: "#/components/schemas/ServiceStatusResult"
        sources:
          type: array
          items:
            $ref: "#/components/schemas/Source"
        timestamp:
          type: string
          format: date-time
        cache:
          type: object
          additionalProperties: false
          required:
            - hit
            - age_seconds
            - expires_at
          properties:
            hit:
              type: boolean
            age_seconds:
              type: integer
              minimum: 0
            expires_at:
              type:
                - string
                - "null"
              format: date-time
        warnings:
          type: array
          items:
            type: string
        next_actions:
          type: array
          items:
            type: object
            additionalProperties: false
            required:
              - tool
              - reason
            properties:
              tool:
                enum:
                  - search_melaka_places
                  - get_melaka_place_facts
                  - check_melaka_visit_window
                  - get_melaka_weather_advisory
                  - plan_melaka_day
                  - validate_melaka_itinerary
              reason:
                type: string
    ErrorEnvelope:
      type: object
      additionalProperties: false
      required:
        - success
        - request_id
        - schema_version
        - data_version
        - data_mode
        - error
        - sources
        - timestamp
        - cache
        - warnings
        - next_actions
      properties:
        success:
          const: false
        request_id:
          type: string
          format: uuid
        schema_version:
          const: "1.0"
        data_version:
          type: string
        data_mode:
          enum:
            - demo
            - verified
        error:
          type: object
          additionalProperties: false
          required:
            - code
            - message
            - retryable
            - details
          properties:
            code:
              type: string
            message:
              type: string
            retryable:
              type: boolean
            details: {}
        sources:
          type: array
          items:
            $ref: "#/components/schemas/Source"
        timestamp:
          type: string
          format: date-time
        cache:
          type: object
          additionalProperties: false
          required:
            - hit
            - age_seconds
            - expires_at
          properties:
            hit:
              const: false
            age_seconds:
              const: 0
            expires_at:
              type: "null"
        warnings:
          type: array
          items:
            type: string
        next_actions:
          type: array
          maxItems: 0
    DatasetMetadata:
      type: object
      additionalProperties: false
      required:
        - revision_id
        - data_version
        - data_mode
        - created_at
        - published_at
        - places
        - assertions
        - sources
        - freshness
      properties:
        revision_id:
          type: string
        data_version:
          type: string
        data_mode:
          enum:
            - demo
            - verified
        created_at:
          type: string
          format: date-time
        published_at:
          type: string
          format: date-time
        places:
          type: integer
          minimum: 0
        assertions:
          type: integer
          minimum: 0
        sources:
          type: integer
          minimum: 0
        freshness:
          type: object
          additionalProperties: false
          required:
            - fresh
            - stale
            - unknown
            - overdue_rechecks
            - next_recheck_at
          properties:
            fresh:
              type: integer
              minimum: 0
            stale:
              type: integer
              minimum: 0
            unknown:
              type: integer
              minimum: 0
            overdue_rechecks:
              type: integer
              minimum: 0
            next_recheck_at:
              type:
                - string
                - "null"
              format: date-time
    Source:
      $schema: https://json-schema.org/draft/2020-12/schema
      type: object
      properties:
        source_id:
          type: string
        publisher:
          type: string
        source_url:
          type: string
          format: uri
        source_group_id:
          type: string
        source_type:
          type: string
        license_id:
          type:
            - string
            - "null"
        license_url:
          anyOf:
            - type: string
              format: uri
            - type: "null"
        permission_status:
          type: string
        attribution_text:
          type: string
        allowed_uses:
          type: array
          items:
            type: string
        retrieved_at:
          type: string
          format: date-time
          pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z))$
        published_at:
          anyOf:
            - type: string
              format: date-time
              pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z))$
            - type: "null"
        content_hash:
          type: string
        review_owner:
          type:
            - string
            - "null"
        cache_retention_limit:
          type:
            - number
            - "null"
        deletion_due_at:
          anyOf:
            - type: string
              format: date-time
              pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z))$
            - type: "null"
        contract_expiry:
          anyOf:
            - type: string
              format: date-time
              pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z))$
            - type: "null"
      required:
        - source_id
        - publisher
        - source_url
        - source_group_id
        - source_type
        - license_id
        - license_url
        - permission_status
        - attribution_text
        - allowed_uses
        - retrieved_at
        - published_at
        - content_hash
        - review_owner
        - cache_retention_limit
        - deletion_due_at
        - contract_expiry
      additionalProperties: false
    PublicAssertion:
      type: object
      additionalProperties: false
      required:
        - assertion_id
        - entity_id
        - field_path
        - value
        - value_type
        - evidence_kind
        - review_status
        - freshness_status
        - resolution_status
        - public_status
        - source_ids
        - source_group_ids
        - published_at
        - retrieved_at
        - observed_at
        - checked_at
        - valid_from
        - valid_to
        - recheck_due_at
        - schema_version
        - data_version
        - rule_version
        - input_assertion_ids
        - usable_for_decision
        - reasons
      properties:
        assertion_id:
          type: string
        entity_id:
          type: string
        field_path:
          type: string
        value: {}
        value_type:
          enum:
            - string
            - number
            - boolean
            - object
            - array
            - "null"
        evidence_kind:
          enum:
            - official_published
            - merchant_reported
            - independently_observed
            - derived
            - demo
        review_status:
          enum:
            - pending_review
            - reviewed
            - rejected
        freshness_status:
          enum:
            - fresh
            - stale
            - unknown
        resolution_status:
          enum:
            - known
            - unknown
            - conflicting
            - restricted
        public_status:
          enum:
            - known
            - stale
            - unknown
            - conflicting
            - restricted
            - pending_review
            - rejected
            - not_current
        source_ids:
          type: array
          items:
            type: string
        source_group_ids:
          type: array
          items:
            type: string
        published_at:
          type:
            - string
            - "null"
          format: date-time
        retrieved_at:
          type: string
          format: date-time
        observed_at:
          type:
            - string
            - "null"
          format: date-time
        checked_at:
          type:
            - string
            - "null"
          format: date-time
        valid_from:
          type:
            - string
            - "null"
          format: date-time
        valid_to:
          type:
            - string
            - "null"
          format: date-time
        recheck_due_at:
          type:
            - string
            - "null"
          format: date-time
        schema_version:
          const: "1.0"
        data_version:
          type: string
        rule_version:
          type:
            - string
            - "null"
        input_assertion_ids:
          type: array
          items:
            type: string
        usable_for_decision:
          type: boolean
        reasons:
          type: array
          items:
            type: string
    PublicFact:
      type: object
      additionalProperties: false
      required:
        - status
        - value
        - usable_for_decision
        - evidence
      properties:
        status:
          enum:
            - known
            - stale
            - unknown
            - conflicting
            - restricted
            - pending_review
            - rejected
            - not_current
        value: {}
        usable_for_decision:
          type: boolean
        evidence:
          type: array
          items:
            $ref: "#/components/schemas/PublicAssertion"
    PlaceFields:
      type: object
      additionalProperties: false
      properties:
        identity:
          $ref: "#/components/schemas/PublicFact"
        location:
          $ref: "#/components/schemas/PublicFact"
        summary:
          $ref: "#/components/schemas/PublicFact"
        schedule:
          type: object
          additionalProperties: false
          required:
            - timezone
            - rules
            - exceptions
          properties:
            timezone:
              const: Asia/Kuala_Lumpur
            rules:
              type: array
              items:
                $ref: "#/components/schemas/PublicFact"
            exceptions:
              type: array
              items:
                $ref: "#/components/schemas/PublicFact"
        offers:
          type: array
          items:
            $ref: "#/components/schemas/PublicFact"
        services:
          type: array
          items:
            $ref: "#/components/schemas/PublicFact"
        facilities:
          type: array
          items:
            $ref: "#/components/schemas/PublicFact"
        certificates:
          type: array
          items:
            $ref: "#/components/schemas/PublicFact"
    SearchPlacesResult:
      type: object
      additionalProperties: false
      required:
        - items
        - page
        - query
        - filters_applied
      properties:
        items:
          type: array
          items:
            type: object
            additionalProperties: false
            required:
              - place_id
              - slug
              - entity_revision
              - data_version
              - name
              - category
              - match_reasons
              - satisfied_conditions
              - unknown_conditions
              - verification_status
              - fields
            properties:
              place_id:
                type: string
              slug:
                type: string
              entity_revision:
                type: string
              data_version:
                type: string
              name:
                type: string
              category:
                enum:
                  - museum
                  - heritage
                  - food
                  - park
                  - market
                  - activity
                  - night
              match_reasons:
                type: array
                items:
                  type: string
              satisfied_conditions:
                type: array
                items:
                  type: string
              unknown_conditions:
                type: array
                items:
                  type: string
              verification_status:
                enum:
                  - reviewed_current
                  - needs_verification
              fields:
                $ref: "#/components/schemas/PlaceFields"
        page:
          type: object
          additionalProperties: false
          required:
            - limit
            - returned
            - has_more
            - next_cursor
          properties:
            limit:
              type: integer
              minimum: 1
              maximum: 50
            returned:
              type: integer
              minimum: 0
              maximum: 50
            has_more:
              type: boolean
            next_cursor:
              type:
                - string
                - "null"
        query:
          type: string
        filters_applied:
          type: object
          additionalProperties: false
          required:
            - category
            - constraints
            - include_unverified
          properties:
            category:
              type:
                - string
                - "null"
              enum:
                - museum
                - heritage
                - food
                - park
                - market
                - activity
                - night
                - null
            constraints:
              type: object
              additionalProperties: false
              properties:
                step_free_entry:
                  type: boolean
                accessible_toilet:
                  type: boolean
                halal_official:
                  type: boolean
            include_unverified:
              type: boolean
    PlaceFactsResult:
      type: object
      additionalProperties: false
      required:
        - place_id
        - slug
        - entity_type
        - entity_revision
        - data_version
        - locale
        - fields
        - limitations
      properties:
        place_id:
          type: string
        slug:
          type: string
        entity_type:
          const: place
        entity_revision:
          type: string
        data_version:
          type: string
        locale:
          enum:
            - en
            - zh
        fields:
          $ref: "#/components/schemas/PlaceFields"
        limitations:
          type: array
          items:
            type: string
    LocalDateTime:
      type: object
      additionalProperties: false
      required:
        - date
        - time
        - weekday
      properties:
        date:
          type: string
          format: date
        time:
          type: string
          pattern: ^([01]\d|2[0-3]):[0-5]\d$
        weekday:
          type: integer
          minimum: 0
          maximum: 6
    ScheduleEvaluation:
      type: object
      additionalProperties: false
      required:
        - assessment
        - evaluation_scope
        - arrival_local
        - departure_local
        - published_window_match
        - matched_interval
        - exception_applied
        - conflicts
        - blocking_unknowns
        - assumptions
        - warnings
        - assertion_ids
        - source_ids
        - checked_dimensions
        - unchecked_dimensions
      properties:
        assessment:
          enum:
            - feasible
            - infeasible
            - needs_verification
        evaluation_scope:
          const: published_schedule_only
        arrival_local:
          $ref: "#/components/schemas/LocalDateTime"
        departure_local:
          $ref: "#/components/schemas/LocalDateTime"
        published_window_match:
          type:
            - boolean
            - "null"
        matched_interval:
          oneOf:
            - type: object
              additionalProperties: false
              required:
                - opens_at
                - closes_at
                - last_entry_at
                - minimum_visit_minutes
                - origin
                - assertion_id
              properties:
                opens_at:
                  type: string
                  format: date-time
                closes_at:
                  type: string
                  format: date-time
                last_entry_at:
                  type:
                    - string
                    - "null"
                  format: date-time
                minimum_visit_minutes:
                  type:
                    - integer
                    - "null"
                origin:
                  enum:
                    - weekly_rule
                    - date_exception
                assertion_id:
                  type: string
            - type: "null"
        exception_applied:
          type: boolean
        conflicts:
          type: array
          items:
            type: string
        blocking_unknowns:
          type: array
          items:
            type: string
        assumptions:
          type: array
          items:
            type: string
        warnings:
          type: array
          items:
            type: string
        assertion_ids:
          type: array
          items:
            type: string
        source_ids:
          type: array
          items:
            type: string
        checked_dimensions:
          type: array
          items:
            type: string
        unchecked_dimensions:
          type: array
          items:
            type: string
    ServiceEvaluation:
      type: object
      additionalProperties: false
      required:
        - assessment
        - service_id
        - states
        - conflicts
        - blocking_unknowns
        - warnings
        - assertion_ids
        - source_ids
        - checked_dimensions
        - unchecked_dimensions
      properties:
        assessment:
          enum:
            - feasible
            - infeasible
            - needs_verification
        service_id:
          type:
            - string
            - "null"
        states:
          oneOf:
            - type: object
              additionalProperties: false
              required:
                - scheduled_open
                - operating_status
                - product_status
                - availability
                - booking_confirmation
              properties:
                scheduled_open:
                  enum:
                    - open
                    - closed
                    - unknown
                    - null
                operating_status:
                  enum:
                    - operating
                    - paused
                    - unknown
                    - null
                product_status:
                  enum:
                    - available
                    - unavailable
                    - unknown
                    - null
                availability:
                  enum:
                    - available
                    - sold_out
                    - unknown
                    - null
                booking_confirmation:
                  enum:
                    - not_requested
                    - requested
                    - confirmed
                    - declined
                    - null
            - type: "null"
        conflicts:
          type: array
          items:
            type: string
        blocking_unknowns:
          type: array
          items:
            type: string
        warnings:
          type: array
          items:
            type: string
        assertion_ids:
          type: array
          items:
            type: string
        source_ids:
          type: array
          items:
            type: string
        checked_dimensions:
          type: array
          items:
            type: string
        unchecked_dimensions:
          type: array
          items:
            type: string
    ConstraintEvaluation:
      type: object
      additionalProperties: false
      required:
        - assessment
        - satisfied_conditions
        - conflicts
        - blocking_unknowns
        - assertion_ids
        - source_ids
      properties:
        assessment:
          enum:
            - feasible
            - infeasible
            - needs_verification
        satisfied_conditions:
          type: array
          items:
            type: string
        conflicts:
          type: array
          items:
            type: string
        blocking_unknowns:
          type: array
          items:
            type: string
        assertion_ids:
          type: array
          items:
            type: string
        source_ids:
          type: array
          items:
            type: string
    CostEvaluation:
      type: object
      additionalProperties: false
      required:
        - status
        - currency
        - known_subtotal_minor
        - minimum_total_minor
        - maximum_total_minor
        - lines
        - unestimated_items
        - conflicts
        - blocking_unknowns
        - assertion_ids
        - source_ids
      properties:
        status:
          enum:
            - known
            - range
            - unknown
            - conflicting
        currency:
          type:
            - string
            - "null"
        known_subtotal_minor:
          type: integer
          minimum: 0
        minimum_total_minor:
          type:
            - integer
            - "null"
        maximum_total_minor:
          type:
            - integer
            - "null"
        lines:
          type: array
          items:
            type: object
            additionalProperties: false
            required:
              - visitor
              - age
              - audience
              - amount_minor
              - surcharge_minor
              - currency
              - offer_rule_id
              - status
              - reason
            properties:
              visitor:
                type: string
              age:
                type:
                  - integer
                  - "null"
              audience:
                enum:
                  - adult
                  - child
              amount_minor:
                type:
                  - integer
                  - "null"
              surcharge_minor:
                type:
                  - integer
                  - "null"
              currency:
                type:
                  - string
                  - "null"
              offer_rule_id:
                type:
                  - string
                  - "null"
              status:
                enum:
                  - known
                  - range
                  - unknown
                  - conflicting
              reason:
                type:
                  - string
                  - "null"
        unestimated_items:
          type: array
          items:
            type: string
        conflicts:
          type: array
          items:
            type: string
        blocking_unknowns:
          type: array
          items:
            type: string
        assertion_ids:
          type: array
          items:
            type: string
        source_ids:
          type: array
          items:
            type: string
    VisitWindowEvaluation:
      type: object
      additionalProperties: false
      required:
        - assessment
        - evaluation_scope
        - place_id
        - service_id
        - schedule
        - service
        - constraints
        - cost
        - conflicts
        - blocking_unknowns
        - assumptions
        - warnings
        - checked_dimensions
        - unchecked_dimensions
        - assertion_ids
        - source_ids
        - rule_version
      properties:
        assessment:
          enum:
            - feasible
            - infeasible
            - needs_verification
        evaluation_scope:
          const: published_constraints_only
        place_id:
          type: string
        service_id:
          type:
            - string
            - "null"
        schedule:
          $ref: "#/components/schemas/ScheduleEvaluation"
        service:
          $ref: "#/components/schemas/ServiceEvaluation"
        constraints:
          $ref: "#/components/schemas/ConstraintEvaluation"
        cost:
          $ref: "#/components/schemas/CostEvaluation"
        conflicts:
          type: array
          items:
            type: string
        blocking_unknowns:
          type: array
          items:
            type: string
        assumptions:
          type: array
          items:
            type: string
        warnings:
          type: array
          items:
            type: string
        checked_dimensions:
          type: array
          items:
            type: string
        unchecked_dimensions:
          type: array
          items:
            type: string
        assertion_ids:
          type: array
          items:
            type: string
        source_ids:
          type: array
          items:
            type: string
        rule_version:
          const: planner-1.0
    WeatherAdvisory:
      type: object
      additionalProperties: false
      required:
        - location_id
        - date
        - forecast_coverage
        - spatial_granularity
        - conditions
        - published_warnings
        - limitations
      properties:
        location_id:
          const: melaka_central
        date:
          type: string
          format: date
        forecast_coverage:
          type: object
          additionalProperties: false
          required:
            - starts_on
            - ends_on
          properties:
            starts_on:
              type: string
              format: date
            ends_on:
              type: string
              format: date
        spatial_granularity:
          const: melaka_area
        conditions:
          type: object
          additionalProperties: false
          required:
            - summary
            - precipitation_risk
            - temperature_celsius_min
            - temperature_celsius_max
          properties:
            summary:
              type: string
            precipitation_risk:
              enum:
                - low
                - medium
                - high
                - unknown
            temperature_celsius_min:
              type:
                - number
                - "null"
            temperature_celsius_max:
              type:
                - number
                - "null"
        published_warnings:
          type: array
          items:
            type: object
            additionalProperties: false
            required:
              - code
              - summary
              - severity
            properties:
              code:
                type: string
              summary:
                type: string
              severity:
                enum:
                  - info
                  - watch
                  - warning
                  - unknown
        limitations:
          type: array
          items:
            type: string
    WeatherAdvisoryResult:
      type: object
      additionalProperties: false
      required:
        - provider_state
        - advisory
        - retrieved_at
        - valid_from
        - valid_to
        - reason
        - evaluation_scope
      properties:
        provider_state:
          enum:
            - ok
            - stale
            - unavailable
            - disabled
            - out_of_coverage
        advisory:
          oneOf:
            - $ref: "#/components/schemas/WeatherAdvisory"
            - type: "null"
        retrieved_at:
          type:
            - string
            - "null"
          format: date-time
        valid_from:
          type:
            - string
            - "null"
          format: date-time
        valid_to:
          type:
            - string
            - "null"
          format: date-time
        reason:
          type:
            - string
            - "null"
        evaluation_scope:
          const: melaka_area_provider_record
    PlanCost:
      type: object
      additionalProperties: false
      required:
        - currency
        - known_subtotal_minor
        - maximum_total_minor
        - budget_amount_minor
        - budget_currency
      properties:
        currency:
          type:
            - string
            - "null"
        known_subtotal_minor:
          type: integer
          minimum: 0
        maximum_total_minor:
          type:
            - integer
            - "null"
        budget_amount_minor:
          type:
            - integer
            - "null"
        budget_currency:
          type:
            - string
            - "null"
    PlannedRouteLeg:
      type: object
      additionalProperties: false
      required:
        - from_place_id
        - to_place_id
        - state
        - min_minutes
        - max_minutes
        - buffer_minutes
        - basis
        - reason
        - assumption
      properties:
        from_place_id:
          type:
            - string
            - "null"
        to_place_id:
          type: string
        state:
          enum:
            - ok
            - stale
            - unavailable
            - disabled
            - out_of_coverage
        min_minutes:
          type:
            - integer
            - "null"
        max_minutes:
          type:
            - integer
            - "null"
        buffer_minutes:
          type: integer
          minimum: 0
        basis:
          enum:
            - provider
            - precomputed_fixture
            - null
        reason:
          type:
            - string
            - "null"
        assumption:
          type: string
    PlanDayEvaluation:
      type: object
      additionalProperties: false
      required:
        - assessment
        - evaluation_scope
        - heuristic_status
        - global_optimum_claimed
        - start_reference
        - stops
        - cost
        - conflicts
        - blocking_unknowns
        - assumptions
        - warnings
        - checked_dimensions
        - unchecked_dimensions
        - candidate_place_ids_considered
        - assertion_ids
        - source_ids
        - provider_source_ids
        - rule_version
      properties:
        assessment:
          enum:
            - feasible
            - infeasible
            - needs_verification
        evaluation_scope:
          const: published_constraints_and_available_routes
        heuristic_status:
          enum:
            - candidate_plan_found
            - no_candidate_plan_found
        global_optimum_claimed:
          const: false
        start_reference:
          oneOf:
            - type: object
              additionalProperties: false
              required:
                - type
                - place_id
              properties:
                type:
                  const: place
                place_id:
                  type: string
            - type: object
              additionalProperties: false
              required:
                - type
              properties:
                type:
                  const: coordinates_supplied
        stops:
          type: array
          items:
            type: object
            additionalProperties: false
            required:
              - place_id
              - arrival_at
              - departure_at
              - duration_minutes
              - route_from_previous
              - visit
            properties:
              place_id:
                type: string
              arrival_at:
                type: string
                format: date-time
              departure_at:
                type: string
                format: date-time
              duration_minutes:
                type: integer
                minimum: 1
              route_from_previous:
                $ref: "#/components/schemas/PlannedRouteLeg"
              visit:
                $ref: "#/components/schemas/VisitWindowEvaluation"
        cost:
          $ref: "#/components/schemas/PlanCost"
        conflicts:
          type: array
          items:
            type: string
        blocking_unknowns:
          type: array
          items:
            type: string
        assumptions:
          type: array
          items:
            type: string
        warnings:
          type: array
          items:
            type: string
        checked_dimensions:
          type: array
          items:
            type: string
        unchecked_dimensions:
          type: array
          items:
            type: string
        candidate_place_ids_considered:
          type: array
          items:
            type: string
        assertion_ids:
          type: array
          items:
            type: string
        source_ids:
          type: array
          items:
            type: string
        provider_source_ids:
          type: array
          items:
            type: string
        rule_version:
          const: planner-1.0
    ItineraryValidation:
      type: object
      additionalProperties: false
      required:
        - assessment
        - evaluation_scope
        - stops
        - transport_checks
        - cost
        - conflicts
        - blocking_unknowns
        - suggested_fixes
        - assumptions
        - warnings
        - checked_dimensions
        - unchecked_dimensions
        - assertion_ids
        - source_ids
        - rule_version
      properties:
        assessment:
          enum:
            - feasible
            - infeasible
            - needs_verification
        evaluation_scope:
          const: published_constraints_and_user_supplied_transport
        stops:
          type: array
          items:
            type: object
            additionalProperties: false
            required:
              - index
              - place_id
              - arrival_at
              - departure_at
              - visit
              - conflicts
              - blocking_unknowns
            properties:
              index:
                type: integer
                minimum: 0
              place_id:
                type: string
              arrival_at:
                type: string
                format: date-time
              departure_at:
                type: string
                format: date-time
              visit:
                oneOf:
                  - $ref: "#/components/schemas/VisitWindowEvaluation"
                  - type: "null"
              conflicts:
                type: array
                items:
                  type: string
              blocking_unknowns:
                type: array
                items:
                  type: string
        transport_checks:
          type: array
          items:
            type: object
            additionalProperties: false
            required:
              - from_place_id
              - to_place_id
              - source
              - min_minutes
              - max_minutes
              - assessment
              - reason
            properties:
              from_place_id:
                type: string
              to_place_id:
                type: string
              source:
                enum:
                  - user_supplied
                  - missing
                  - not_required
              min_minutes:
                type:
                  - integer
                  - "null"
              max_minutes:
                type:
                  - integer
                  - "null"
              assessment:
                enum:
                  - feasible
                  - infeasible
                  - needs_verification
              reason:
                type: string
        cost:
          $ref: "#/components/schemas/PlanCost"
        conflicts:
          type: array
          items:
            type: string
        blocking_unknowns:
          type: array
          items:
            type: string
        suggested_fixes:
          type: array
          items:
            type: string
        assumptions:
          type: array
          items:
            type: string
        warnings:
          type: array
          items:
            type: string
        checked_dimensions:
          type: array
          items:
            type: string
        unchecked_dimensions:
          type: array
          items:
            type: string
        assertion_ids:
          type: array
          items:
            type: string
        source_ids:
          type: array
          items:
            type: string
        rule_version:
          const: planner-1.0
    ServiceMetadataResult:
      type: object
      additionalProperties: false
      required:
        - identity
        - data_mode
        - fact_store
        - dataset
        - coverage
        - capabilities
        - disabled
        - access
        - limits
      properties:
        identity:
          type: string
        data_mode:
          enum:
            - demo
            - verified
        fact_store:
          enum:
            - d1_revision
            - bundled_fallback
        dataset:
          $ref: "#/components/schemas/DatasetMetadata"
        coverage:
          type: object
          additionalProperties: false
          required:
            - kind
            - places
          properties:
            kind:
              type: string
            places:
              type: integer
              minimum: 0
        capabilities:
          type: array
          items:
            type: object
            additionalProperties: false
            required:
              - name
              - method
              - path
              - credits
            properties:
              name:
                type: string
              method:
                type: string
              path:
                type: string
              credits:
                type: integer
                minimum: 0
        disabled:
          type: object
          additionalProperties: false
          required:
            - ard
            - a2a
            - inquiries
            - payments
            - pricing
            - llm
          properties:
            ard:
              type: boolean
            a2a:
              type: boolean
            inquiries:
              type: boolean
            payments:
              type: boolean
            pricing:
              type: boolean
            llm:
              type: boolean
        access:
          type: object
          additionalProperties: false
          required:
            - api_keys
            - quota_store
          properties:
            api_keys:
              type: string
            quota_store:
              type: string
        limits:
          type: object
          additionalProperties: false
          required:
            - max_stops
            - candidate_places
            - request_bytes
            - response_bytes
            - anonymous_daily_credits
            - authenticated_daily_credits
            - anonymous_tool_daily_credits
            - authenticated_tool_daily_credits
            - anonymous_concurrent_tools
            - authenticated_concurrent_tools
            - global_daily_compute_credits
          properties:
            max_stops:
              type: integer
            candidate_places:
              type: integer
            request_bytes:
              type: integer
            response_bytes:
              type: integer
            anonymous_daily_credits:
              type: integer
            authenticated_daily_credits:
              type: integer
            anonymous_tool_daily_credits:
              type: integer
            authenticated_tool_daily_credits:
              type: integer
            anonymous_concurrent_tools:
              type: integer
            authenticated_concurrent_tools:
              type: integer
            global_daily_compute_credits:
              type: integer
    ServiceStatusResult:
      type: object
      additionalProperties: false
      required:
        - service
        - data
        - fact_store
        - quota_store
        - dataset
        - providers
        - actions
        - mcp
      properties:
        service:
          type: string
        data:
          type: string
        fact_store:
          type: string
        quota_store:
          type: string
        dataset:
          $ref: "#/components/schemas/DatasetMetadata"
        providers:
          type: object
          additionalProperties: false
          required:
            - weather
            - routing
            - llm
          properties:
            weather:
              type: object
              additionalProperties: false
              required:
                - configured
                - effective
              properties:
                configured:
                  type: string
                effective:
                  type: string
            routing:
              type: object
              additionalProperties: false
              required:
                - configured
                - effective
                - state
                - valid_to
              properties:
                configured:
                  type: string
                effective:
                  type: string
                state:
                  enum:
                    - demo
                    - ok
                    - stale
                    - unavailable
                    - disabled
                valid_to:
                  type:
                    - string
                    - "null"
                  format: date-time
            llm:
              type: object
              additionalProperties: false
              required:
                - configured
                - effective
              properties:
                configured:
                  type: string
                effective:
                  type: string
        actions:
          type: string
        mcp:
          type: string
    SearchPlacesInput:
      $schema: https://json-schema.org/draft/2020-12/schema
      type: object
      properties:
        query:
          default: ""
          type: string
          maxLength: 200
        category:
          type: string
          enum:
            - museum
            - heritage
            - food
            - park
            - market
            - activity
            - night
        locale:
          default: en
          type: string
          enum:
            - en
            - zh
        filters:
          default: {}
          type: object
          properties:
            step_free_entry:
              type: boolean
            accessible_toilet:
              type: boolean
            halal_official:
              type: boolean
          additionalProperties: false
        include_unverified:
          default: false
          type: boolean
        limit:
          default: 10
          type: integer
          minimum: 1
          maximum: 50
        cursor:
          type: string
          maxLength: 500
        fields:
          maxItems: 5
          type: array
          items:
            type: string
            enum:
              - name
              - summary
              - category
              - location
              - facilities
              - schedule
      required:
        - query
        - locale
        - filters
        - include_unverified
        - limit
      additionalProperties: false
    GetPlaceFactsInput:
      $schema: https://json-schema.org/draft/2020-12/schema
      type: object
      properties:
        place_id:
          type: string
          pattern: ^[a-z0-9][a-z0-9_-]{2,79}$
        locale:
          default: en
          type: string
          enum:
            - en
            - zh
        fields:
          maxItems: 8
          type: array
          items:
            type: string
            enum:
              - identity
              - location
              - summary
              - schedule
              - offers
              - services
              - facilities
              - certificates
      required:
        - place_id
        - locale
      additionalProperties: false
    WeatherInput:
      $schema: https://json-schema.org/draft/2020-12/schema
      type: object
      properties:
        location_id:
          type: string
          const: melaka_central
        date:
          type: string
          pattern: ^\d{4}-\d{2}-\d{2}$
        locale:
          default: en
          type: string
          enum:
            - en
            - zh
      required:
        - location_id
        - date
        - locale
      additionalProperties: false
x-request-limit-bytes: 65536
x-response-limit-bytes: 131072
x-assessment-semantics:
  feasible: All requested hard constraints inside the declared evaluation scope passed; not a guarantee.
  infeasible: At least one supported hard conflict was established.
  needs_verification: No established hard conflict, but required evidence or route data is unavailable, stale, conflicting, or restricted.
