<?xml version="1.0" encoding="UTF-8"?>
<!--
  AssistantSchema.v1.0

  Describes an AI assistant declaratively: system prompt + a flat list of tools
  the LLM can invoke. The generator turns one of these XMLs into:
    • a backend chat endpoint (SSE-streaming) that handles tool dispatch
    • per-frontend typed clients + base chat ViewModels + default chat views
    • tool-definition JSON ready for the LLM API (Anthropic / OpenAI / Bedrock)
    • synthetic response DTOs for composite returns

  Models / services the tools reference are not declared in the XML — the
  generator is invoked with `-modelPath` and `-servicePath` flags pointing at
  workspace directories it scans recursively. Type names in tool bindings
  (`Implementation service="X"`, `Parameter type="Model" model="Y"`) are
  resolved against that scanned index at generation time.

  See drafts/CustomerSupportAssistant.xml for an example of the XML shape this
  drives.
-->
<xs:schema
  xmlns="https://tnventures.fi/modelgenerator/xmlschemas/assistant/v1"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  targetNamespace="https://tnventures.fi/modelgenerator/xmlschemas/assistant/v1"
  elementFormDefault="qualified">

  <!-- ============================================================================
       Root element
       ============================================================================ -->

  <xs:element name="Assistant">
    <xs:complexType>
      <xs:annotation>
        <xs:documentation>An AI assistant: a system prompt, a tool palette derived from existing Models / Services, and per-tool implementation bindings. One Assistant produces one backend chat endpoint plus matching frontend clients / chat ViewModels in every configured frontend project.</xs:documentation>
      </xs:annotation>
      <xs:sequence>
        <xs:element name="SystemPrompt" type="xs:string" minOccurs="0">
          <xs:annotation>
            <xs:documentation>Top-level system prompt the LLM sees. Concatenated with each tool's `Description` to form the full instruction. Multi-line; whitespace inside the element is preserved verbatim, so indent the text the way you want the model to see it.</xs:documentation>
          </xs:annotation>
        </xs:element>

        <xs:element name="Tools">
          <xs:annotation>
            <xs:documentation>The list of tools the LLM can invoke during a chat. At least one tool is required — an assistant with no tools degrades to a plain chat completion and is better expressed as a hand-coded call. Use &lt;ModelToolset model="X"/&gt; to auto-generate search_&lt;entity&gt; tools backed by the safe WHERE-expression parser.</xs:documentation>
          </xs:annotation>
          <xs:complexType>
            <xs:sequence>
              <xs:choice minOccurs="1" maxOccurs="unbounded">
                <xs:element name="Tool"          type="ToolType"/>
                <xs:element name="ModelToolset"  type="ModelToolsetType"/>
              </xs:choice>
            </xs:sequence>
          </xs:complexType>
        </xs:element>

        <xs:element name="ReferencedServices" minOccurs="0">
          <xs:annotation>
            <xs:documentation>Optional. Service-name bindings injected into the assistant constructor alongside any auto-injected ServiceMethod tools. Each `&lt;Service name="X"/&gt;` adds an `self._x_service` field (Python) / `_xService` field (C#) plus the matching catalog entry in the AI implementation prompt. Use this when a `kind="Custom"` tool needs to call a service whose body lives in the AI-generated method, without first declaring a per-method ServiceMethod tool. Services already reached via `&lt;Implementation kind="ServiceMethod" service="X"/&gt;` don't need to be repeated here.</xs:documentation>
          </xs:annotation>
          <xs:complexType>
            <xs:sequence>
              <xs:element name="Service" minOccurs="1" maxOccurs="unbounded">
                <xs:complexType>
                  <xs:attribute name="name" type="xs:string" use="required">
                    <xs:annotation>
                      <xs:documentation>Name of the Service XML reachable via the generator's `-servicePath` flag (matched on the target Service XML's `name` attribute). Same lookup as the `service` attribute on `&lt;Implementation kind="ServiceMethod"/&gt;`.</xs:documentation>
                    </xs:annotation>
                  </xs:attribute>
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>

        <xs:element name="Cli" type="CliFrontendType" minOccurs="0">
          <xs:annotation>
            <xs:documentation>Optional. Per-assistant overrides for the generated CLI frontend (`&lt;Name&gt;CliProgram.cs`). Unset attributes fall back to defaults in `ModelGenerator.json` under the `Cli` key, then to the generator's hard-coded defaults. Omit the element entirely to use all defaults.</xs:documentation>
          </xs:annotation>
        </xs:element>
      </xs:sequence>
      <xs:attribute name="name" type="xs:string" use="required">
        <xs:annotation>
          <xs:documentation>Required. PascalCase identifier used as the generated assistant class name (e.g. `CustomerSupportAssistant`).</xs:documentation>
        </xs:annotation>
      </xs:attribute>
      <xs:attribute name="namespace" type="xs:string" use="required">
        <xs:annotation>
          <xs:documentation>Required. Target namespace / package for the generated assistant class and its sidecar DTOs.</xs:documentation>
        </xs:annotation>
      </xs:attribute>
      <xs:attribute name="documentation" type="xs:string">
        <xs:annotation>
          <xs:documentation>Optional. Human-readable description of the assistant's purpose. Surfaces in generated docs and the assistant's class-level doc comment; NOT sent to the LLM (use SystemPrompt for that).</xs:documentation>
        </xs:annotation>
      </xs:attribute>
    </xs:complexType>
  </xs:element>

  <!-- ============================================================================
       Tool
       ============================================================================ -->

  <xs:complexType name="ToolType">
    <xs:annotation>
      <xs:documentation>One LLM-callable tool. Carries the description the LLM sees, the typed input parameter list, the typed return shape, and a binding to the C# / Python that runs when the LLM invokes it (a Service method, a domain function, or a hand-written Custom body).</xs:documentation>
    </xs:annotation>
    <xs:sequence>
      <xs:element name="Description" type="xs:string">
        <xs:annotation>
          <xs:documentation>Required. The text the LLM sees when deciding whether to call this tool — include trigger phrases, when-to-use guidance, side-effect warnings. The single highest-leverage attribute for tool-call accuracy: invest more text here than anywhere else in the XML.</xs:documentation>
        </xs:annotation>
      </xs:element>
      <xs:element name="Parameters" minOccurs="0">
        <xs:annotation>
          <xs:documentation>Optional. Typed inputs the LLM must supply when invoking the tool. Omit for zero-arg tools.</xs:documentation>
        </xs:annotation>
        <xs:complexType>
          <xs:sequence>
            <xs:element name="Parameter" type="ToolParameterType" maxOccurs="unbounded"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
      <xs:element name="ReturnParameters" minOccurs="0">
        <xs:annotation>
          <xs:documentation>Optional. Typed output the tool produces. Single `ReturnParameter` of `model` / `object` type returns the inner type directly (no wrapper); single primitive or two+ ReturnParameters trigger a synthetic DTO named `&lt;ToolName&gt;Result` with one field per parameter. For service-bound tools the bound method's actual return type wins automatically — set this only to project / reshape.</xs:documentation>
        </xs:annotation>
        <xs:complexType>
          <xs:sequence>
            <xs:element name="ReturnParameter" type="ToolReturnParameterType" maxOccurs="unbounded"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
      <xs:element name="Implementation" type="ToolImplementationType">
        <xs:annotation>
          <xs:documentation>Required. How the tool body is provided. Three flavours: bind 1:1 to a Service method, bind to a domain-service function, or declare it Custom (generator emits an abstract stub the AI implementation phase fills in).</xs:documentation>
        </xs:annotation>
      </xs:element>
    </xs:sequence>
    <xs:attribute name="name" type="xs:string" use="required">
      <xs:annotation>
        <xs:documentation>Required. snake_case identifier used as the literal tool name the LLM invokes (`get_customer`, `update_customer_email`). Must be unique within the Assistant.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute name="requireConfirmation" type="xs:boolean" default="false">
      <xs:annotation>
        <xs:documentation>Optional, default false. When true, generator stitches a "confirm with the user before invoking" hint into the tool's description and tags the tool-def JSON with a metadata flag the chat UI layer can read to render a confirm dialog before the call hits the dispatcher.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute name="userRightCommand" type="xs:string">
      <xs:annotation>
        <xs:documentation>Optional. Command-level key the dispatcher checks before invoking the tool. Same semantics as Service method's `userRightCommand`. Tools without this attribute inherit the bound service method's `userRightCommand` (when binding is ServiceMethod / DomainMethod). Set explicitly only to override.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
  </xs:complexType>

  <!-- ============================================================================
       Tool parameters
       ============================================================================ -->

  <xs:complexType name="ToolParameterType">
    <xs:annotation>
      <xs:documentation>One typed input on a tool. Same type vocabulary as ServiceSchema's ParameterType (String, Int, Decimal, Money, Bool, Id, DateTime, Date, Json, Xml, File, FilePath, DirPath, Enum, Model, Object). The `documentation` attribute becomes the field's description in the LLM's input_schema — write it for the model, not the dev.</xs:documentation>
    </xs:annotation>
    <xs:attribute name="name" type="xs:string" use="required">
      <xs:annotation>
        <xs:documentation>Required. snake_case parameter name (matches the field name in the LLM's input_schema). Mapped to camelCase / snake_case automatically per target language.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute name="type" use="required">
      <xs:annotation>
        <xs:documentation>Required. Same type vocabulary as Service parameters: String, Int, Decimal, Money, Bool, Id, DateTime, Date, Json, Xml, Enum (requires `enum`), Model (requires `model`; for collections add `collection="List"`), Object (requires `object`).</xs:documentation>
      </xs:annotation>
      <xs:simpleType>
        <xs:restriction base="xs:string">
          <xs:enumeration value="String"/>
          <xs:enumeration value="Int"/>
          <xs:enumeration value="Decimal"/>
          <xs:enumeration value="Money"/>
          <xs:enumeration value="Bool"/>
          <xs:enumeration value="Id"/>
          <xs:enumeration value="DateTime"/>
          <xs:enumeration value="Date"/>
          <xs:enumeration value="Json"/>
          <xs:enumeration value="Xml"/>
          <xs:enumeration value="File"/>
          <xs:enumeration value="FilePath"/>
          <xs:enumeration value="DirPath"/>
          <xs:enumeration value="Enum"/>
          <xs:enumeration value="Model"/>
          <xs:enumeration value="Object"/>
        </xs:restriction>
      </xs:simpleType>
    </xs:attribute>
    <xs:attribute name="model" type="xs:string">
      <xs:annotation>
        <xs:documentation>Required when `type="Model"`. Name of a model XML reachable via the generator's `-modelPath` flag (matched on the target Model XML's `name` attribute).</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute name="enum" type="xs:string">
      <xs:annotation>
        <xs:documentation>Required when `type="Enum"`. Name of an enumeration declared on a referenced model.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute name="object" type="xs:string">
      <xs:annotation>
        <xs:documentation>Required when `type="Object"`. Name of an externally-defined class / record the parameter binds to.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute name="collection">
      <xs:annotation>
        <xs:documentation>Optional. `List` if the parameter is a collection of `type`. Default is no collection (single value).</xs:documentation>
      </xs:annotation>
      <xs:simpleType>
        <xs:restriction base="xs:string">
          <xs:enumeration value="List"/>
          <xs:enumeration value="None"/>
        </xs:restriction>
      </xs:simpleType>
    </xs:attribute>
    <xs:attribute name="mandatory" type="xs:boolean" default="true">
      <xs:annotation>
        <xs:documentation>Optional, default true. When false the parameter is nullable in the LLM's input_schema and the generated dispatcher tolerates a missing value.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute name="default" type="xs:string">
      <xs:annotation>
        <xs:documentation>Optional. Default value used when `mandatory="false"` and the LLM omits the parameter. String form is type-coerced at generation time.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute name="documentation" type="xs:string">
      <xs:annotation>
        <xs:documentation>Optional but strongly recommended. Becomes the parameter's `description` field in the LLM's input_schema — the model uses this to decide what to put there. Write it for the LLM, not the developer.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
  </xs:complexType>

  <xs:complexType name="ToolReturnParameterType">
    <xs:annotation>
      <xs:documentation>One typed output field on a tool. Same type vocabulary as ToolParameterType. When ReturnParameters has exactly one entry of `model` / `object` type, the generator returns that inner type directly (single-value short-circuit). Otherwise a synthetic DTO `&lt;ToolName&gt;Result` is emitted with one field per ReturnParameter. For anonymous nested object shapes (e.g. `errors[]` of `{level, message}`), set `type="Object"` and declare the inline shape via a child `&lt;Properties&gt;` element — the generator synthesizes a sibling DTO and wires it in.</xs:documentation>
    </xs:annotation>
    <xs:sequence>
      <xs:element name="Properties" minOccurs="0">
        <xs:annotation>
          <xs:documentation>Optional. When `type="Object"`, declares an inline anonymous shape: list of `&lt;Property&gt;` entries that become fields on a synthesized nested DTO (named `&lt;ToolPascalName&gt;_&lt;ParamPascal&gt;Item`). Use this instead of `object="X"` to avoid having to define a separate top-level Object type for one-off response shapes.</xs:documentation>
        </xs:annotation>
        <xs:complexType>
          <xs:sequence>
            <xs:element name="Property" type="ToolReturnParameterType" maxOccurs="unbounded"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
    <xs:attribute name="name" type="xs:string" use="required">
      <xs:annotation>
        <xs:documentation>Required. Field name on the synthetic response DTO (and the JSON key in the wire payload). camelCase / snake_case handled per target language.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute name="type" use="required">
      <xs:annotation>
        <xs:documentation>Required. Same vocabulary as ToolParameter's `type`.</xs:documentation>
      </xs:annotation>
      <xs:simpleType>
        <xs:restriction base="xs:string">
          <xs:enumeration value="String"/>
          <xs:enumeration value="Int"/>
          <xs:enumeration value="Decimal"/>
          <xs:enumeration value="Money"/>
          <xs:enumeration value="Bool"/>
          <xs:enumeration value="Id"/>
          <xs:enumeration value="DateTime"/>
          <xs:enumeration value="Date"/>
          <xs:enumeration value="Json"/>
          <xs:enumeration value="Xml"/>
          <xs:enumeration value="Enum"/>
          <xs:enumeration value="Model"/>
          <xs:enumeration value="Object"/>
        </xs:restriction>
      </xs:simpleType>
    </xs:attribute>
    <xs:attribute name="model"  type="xs:string"/>
    <xs:attribute name="enum"   type="xs:string"/>
    <xs:attribute name="object" type="xs:string"/>
    <xs:attribute name="collection">
      <xs:simpleType>
        <xs:restriction base="xs:string">
          <xs:enumeration value="List"/>
          <xs:enumeration value="None"/>
        </xs:restriction>
      </xs:simpleType>
    </xs:attribute>
    <xs:attribute name="mandatory" type="xs:boolean" default="true">
      <xs:annotation>
        <xs:documentation>Optional, default true. When false the field is nullable in the response DTO.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute name="documentation" type="xs:string"/>
  </xs:complexType>

  <!-- ============================================================================
       Model toolset — auto-generated search tools per Model
       ============================================================================ -->

  <xs:complexType name="ModelToolsetType">
    <xs:annotation>
      <xs:documentation>Compact declaration that expands at generation time into one or more typed tools for a given Model (resolved from the generator's `-modelPath` index). The generated `search_&lt;entity&gt;` tool accepts a LLM-authored WHERE expression using bracketed model identifiers (`[Customer.Status] = @p0`); the runtime parser maps identifiers to columns, resolves FK paths into EXISTS subqueries (forward + reverse + multi-hop), and rejects DML/DDL/UNION keywords. Safer than raw-SQL access: the LLM can only reference properties declared in the model XML.</xs:documentation>
    </xs:annotation>
    <xs:attribute name="model" type="xs:string" use="required">
      <xs:annotation>
        <xs:documentation>Required. Name of the Model XML to expose. Matched against the workspace's `-modelPath` index.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute name="includeSearch" type="xs:boolean" default="true">
      <xs:annotation>
        <xs:documentation>Optional, default true. Emit a `search_&lt;entity&gt;` tool that takes a WHERE expression + paginated args.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute name="includeGetById" type="xs:boolean" default="true">
      <xs:annotation>
        <xs:documentation>Optional, default true. Emit a `get_&lt;entity&gt;_by_id` tool that takes a single Id parameter and returns the entity (or null).</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute name="includeAggregate" type="xs:boolean" default="false">
      <xs:annotation>
        <xs:documentation>Optional, default false. Emit an `aggregate_&lt;entity&gt;` tool that takes a WHERE expression, a GROUP BY list, and a structured aggregates list (function from COUNT/SUM/AVG/MIN/MAX). The default body builds the SQL through the safe parser; the host overrides the `ExecuteRawAggregateAsync` virtual hook to wire actual database execution.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute name="maxPageSize" type="xs:integer" default="100">
      <xs:annotation>
        <xs:documentation>Optional, default 100. Server-side cap on the `page_size` parameter of the search tool — prevents accidental table dumps even when the LLM asks for more.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute name="repositoryType" default="Operational">
      <xs:annotation>
        <xs:documentation>Optional, default Operational. Picks which generated repository the toolset's emitted helpers bind to. Composes the final repository class name as `{Model}{repositoryName ?? repositoryType}Repository` — same convention as Model XML's &lt;Repository&gt; element. Use Archive to query cold-storage rows, Datawarehouse to hit BI tables. AuditTrail/ArchivePartition are intentionally not exposed: AuditTrail's wide-row shape doesn't fit search-by-property, partitioned-archive routing belongs in a Tool-level binding.</xs:documentation>
      </xs:annotation>
      <xs:simpleType>
        <xs:restriction base="xs:string">
          <xs:enumeration value="Operational"/>
          <xs:enumeration value="Archive"/>
          <xs:enumeration value="Datawarehouse"/>
        </xs:restriction>
      </xs:simpleType>
    </xs:attribute>
    <xs:attribute name="repositoryName" type="xs:string">
      <xs:annotation>
        <xs:documentation>Optional. Overrides `repositoryType` for the repository class name's middle segment. Mirrors `&lt;Repository name="X"/&gt;` in Model XML — e.g. `repositoryName="TNVSql"` binds to `{Model}TNVSqlRepository`. Use when the model declares multiple repositories of the same type (the `name` attribute on the &lt;Repository&gt; element disambiguates).</xs:documentation>
      </xs:annotation>
    </xs:attribute>
  </xs:complexType>

  <!-- ============================================================================
       CLI frontend configuration
       ============================================================================ -->

  <xs:complexType name="CliFrontendType">
    <xs:annotation>
      <xs:documentation>Settings that shape the generated `&lt;Name&gt;CliProgram.cs`. Every attribute is optional; the parser layers XML attribute → `ModelGenerator.json:Cli:&lt;PascalName&gt;` → built-in default. The resolved values are baked into the generated program as compile-time constants.</xs:documentation>
    </xs:annotation>
    <xs:attribute name="mode">
      <xs:annotation>
        <xs:documentation>UX tier — `auto` (detect TTY, resolves to scrollregion on real terminal, plain on pipes / CI / NO_COLOR), `scrollregion` (bottom input panel + scroll-region scrollback, richest UX — requires ModelGeneratorCsCliRuntime), `inline` (Spectre streaming inline, no fixed bottom panel), `plain` (vanilla System.Console). Default: `auto`.</xs:documentation>
      </xs:annotation>
      <xs:simpleType>
        <xs:restriction base="xs:string">
          <xs:enumeration value="auto"/>
          <xs:enumeration value="scrollregion"/>
          <xs:enumeration value="inline"/>
          <xs:enumeration value="plain"/>
        </xs:restriction>
      </xs:simpleType>
    </xs:attribute>
    <xs:attribute name="theme">
      <xs:annotation>
        <xs:documentation>Color scheme for Spectre output — `dark`, `light`, or `none` (no color). Default: `dark`.</xs:documentation>
      </xs:annotation>
      <xs:simpleType>
        <xs:restriction base="xs:string">
          <xs:enumeration value="dark"/>
          <xs:enumeration value="light"/>
          <xs:enumeration value="none"/>
        </xs:restriction>
      </xs:simpleType>
    </xs:attribute>
    <xs:attribute name="auth">
      <xs:annotation>
        <xs:documentation>Authentication to the backend SSE endpoint. `Bearer` reads a token from `{NAME}_ASSISTANT_TOKEN` env var (current default). `WindowsAuth` sends Windows credentials via `UseDefaultCredentials=true` — pairs with backend Negotiate auth. `FormCredentials` prompts for username + password at startup, exchanges them at `/auth/form` for a bearer token, caches it for the session. Mirrors the same-named strategy on Application.xml's `&lt;Auth strategy/&gt;`. Default: `Bearer`.</xs:documentation>
      </xs:annotation>
      <xs:simpleType>
        <xs:restriction base="xs:string">
          <xs:enumeration value="Bearer"/>
          <xs:enumeration value="WindowsAuth"/>
          <xs:enumeration value="FormCredentials"/>
        </xs:restriction>
      </xs:simpleType>
    </xs:attribute>
    <xs:attribute name="prompt"              type="xs:string"/>
    <xs:attribute name="welcomeBanner"       type="xs:string"/>
    <xs:attribute name="maxHistoryMessages"  type="xs:int"/>
    <xs:attribute name="enableSlashCommands" type="xs:boolean"/>
    <xs:attribute name="showToolCalls"       type="xs:boolean"/>
    <xs:attribute name="chatLogPath"         type="xs:string"/>
  </xs:complexType>

  <!-- ============================================================================
       Tool implementation
       ============================================================================ -->

  <xs:complexType name="ToolImplementationType">
    <xs:annotation>
      <xs:documentation>Where the tool's body comes from. Three kinds:
- ServiceMethod: bind 1:1 to a Service method declared in a ReferencedService. Generator wires up arg unpacking + the call + return serialization automatically.
- DomainMethod: bind to a domain-service function (signature-compatible with a Service method, but lives in a domain class library rather than an exposed service).
- Custom: generator emits an abstract method on the assistant class with a `// AI GENERATION REQUIRED` marker; the AI implementation phase fills in the body using ReferencedService clients. Use for composites (call multiple services and join the result) or business logic that doesn't have a single backing method.</xs:documentation>
    </xs:annotation>
    <xs:attribute name="kind" use="required">
      <xs:annotation>
        <xs:documentation>Required. ServiceMethod, DomainMethod, or Custom.</xs:documentation>
      </xs:annotation>
      <xs:simpleType>
        <xs:restriction base="xs:string">
          <xs:enumeration value="ServiceMethod"/>
          <xs:enumeration value="DomainMethod"/>
          <xs:enumeration value="Custom"/>
        </xs:restriction>
      </xs:simpleType>
    </xs:attribute>
    <xs:attribute name="service" type="xs:string">
      <xs:annotation>
        <xs:documentation>Required when `kind="ServiceMethod"`. Name of a service XML reachable via the generator's `-servicePath` flag (matched on the target Service XML's `name` attribute).</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute name="domainService" type="xs:string">
      <xs:annotation>
        <xs:documentation>Required when `kind="DomainMethod"`. Name of the domain-service class.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute name="method" type="xs:string">
      <xs:annotation>
        <xs:documentation>Required when `kind="ServiceMethod"` or `kind="DomainMethod"`. Method name on the bound class. Must be signature-compatible with the tool's Parameters / ReturnParameters (or, when ReturnParameters is omitted, the method's return type wins).</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute name="omitBase" type="xs:boolean" default="false">
      <xs:annotation>
        <xs:documentation>Optional, ServiceMethod only. Default false. Generator-emitted services follow a Foo (abstract domain) / FooBase (concrete wire) split; the dispatcher calls FooBase by default. Set `omitBase="true"` to bind to hand-written services that don't follow that split — the dispatcher then calls `method` verbatim. Becomes redundant after #140 flips the convention (wire = `Foo`, domain abstract = `DoFoo`).</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    <xs:attribute name="async" type="xs:boolean" default="true">
      <xs:annotation>
        <xs:documentation>Optional, ServiceMethod / DomainMethod only. Default true. When false, the dispatcher emits a plain (non-awaited) call to the bound method. Use this when the bound method is synchronous — avoids wrapping the service in Task.FromResult shims solely to satisfy the dispatcher's await.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
  </xs:complexType>

</xs:schema>
