# yaml-language-server: $schema=./node_modules/cli-contracts/schemas/cli-contract.schema.json
cli_contracts: 0.1.0

info:
  title: behavior-contracts CLI
  version: 0.1.0
  description: >-
    bc — the build-time codegen CLI over behavior-contracts' public API.
    The consumer authors behaviors in native TS only (`@behavior` / `@leaf`
    static methods with plain TS bodies) and never touches the IR. `bc generate
    --from <behaviors.ts> --behavior <ClassName>` uses the TypeScript compiler to
    read each method's SOURCE — type-extracting parameter/return types and
    lowering the body AST (`+`/`?:`/`.map`/`?.` etc.) to the component-graph IR
    (no module execution), then runs source→ir→emit end to end in-process. What
    is emitted depends on the target: the codegen targets (typed ts, native
    go/rust) terminate the IR at build time and embed none of it (IR-free), while
    the literal targets (ts/python/php) bake the IR in and punt to the runtime.
    Only the go/rust native targets import no bc runtime at all (runtime-free).
    The IR exists only inside the CLI; nothing reads it at runtime. `typescript` is a
    build-time-only optional peer dependency used solely on this `--from <.ts>`
    path; the runtime entry stays dependency-free. `--in <ir-doc.json>` (a
    pre-serialized ComponentGraphIRDoc) remains as an internal/test input
    alongside --from.
  license:
    name: MIT
  contact:
    name: foo-ogawa
    url: https://github.com/foo-ogawa/behavior-contracts

artifact_slots:
  behaviors-module:
    direction: read
    description: >-
      The consumer's TS SOURCE module exporting an `@behavior` class with
      `@behavior` / `@leaf` static methods — the TS-authoring codegen input
      (`--from`). The TS compiler type-extracts each method's parameter/return
      types and lowers its body AST to IR (no module execution). BC runs
      source→ir→native.
  ir-doc:
    direction: read
    description: >-
      Serialized ComponentGraphIRDoc (unbranded JSON IR) — the internal/test
      codegen input (`--in`). The TS-authoring surface uses --from instead.
  generated-module:
    direction: write
    description: >-
      Generated module source for the requested target (literal = IR baked in;
      codegen = IR-free; go/rust native additionally runtime-free).

command_sets:
  bc:
    summary: build-time codegen CLI over behavior-contracts' public API.
    executable: bc

    global_options:
      - name: version
        aliases: [V]
        description: Print version and exit.
        schema:
          type: boolean

      - name: help
        aliases: [h]
        description: Show help and exit.
        schema:
          type: boolean

    commands:
      # ── generate ─────────────────────────────────────────
      generate:
        summary: Emit a native module from a TS behavior module (--from) or a serialized IR doc (--in).
        description: >-
          TS-authoring flow (--from): reads the consumer's TS SOURCE module and
          its --behavior export (an `@behavior` class), then runs source→ir→native
          in-process (the AST reader lowers each method to IR → generateModule)
          for the --lang emitter. The IR is CLI-internal and never touches the
          consumer surface. Internal/test flow (--in): reads the
          ComponentGraphIRDoc, adopts it via loadCompiledIR, and runs the same
          generateModule. Exactly one of --from / --in is required (--from also
          requires --behavior). Writes the module source to --out if given, else
          to stdout. Rejects an unregistered --lang.
        usage:
          - bc generate --lang rust-typed-native --from behaviors.ts --behavior UserRepo --out gen.rs
          - bc generate --lang go-typed-native --from behaviors.ts --behavior UserRepo --out gen.go
          - bc generate --lang typescript-typed --in ir-doc.json --runtime-import ./runtime.js

        options:
          - name: lang
            required: true
            description: >-
              Target emitter (must be a registered language, e.g.
              rust-typed-native, go-typed-native, typescript-typed).
            value_name: emitter
            schema:
              type: string

          - name: from
            required: false
            description: >-
              TS-authoring input: the consumer's `.ts` SOURCE. The TypeScript
              compiler type-extracts each method's parameter/return types and
              lowers its body AST (`+`/`?:`/`.map`/`?.` etc.) to IR — the source
              is read, not executed. BC compiles the --behavior export's methods
              and runs the emitter. The module's `behavior-contracts` import must
              resolve to the same installed package as bc (so `@behavior`/`@leaf`
              markers are identified by declaration file). Requires the optional
              peer dependency `typescript`. Mutually exclusive with --in.
            value_name: behaviors.ts
            schema:
              type: string
            file:
              mode: read
              exists: true
              encoding: utf-8

          - name: behavior
            required: false
            description: >-
              (with --from) The name of the exported `@behavior` class to compile
              (one class = its public static methods, each a root Behavior).
              Required whenever --from is given.
            value_name: ClassName
            schema:
              type: string

          - name: in
            required: false
            description: >-
              Internal/test input: path to a serialized ComponentGraphIRDoc JSON
              file. The TS-authoring surface uses --from instead. Mutually
              exclusive with --from.
            value_name: ir-doc.json
            schema:
              type: string
            file:
              mode: read
              exists: true
              media_type: application/json
              encoding: utf-8

          - name: out
            required: false
            description: Write the generated covered module here instead of stdout.
            value_name: file
            schema:
              type: string
            file:
              mode: write

          - name: shared-types-out
            required: false
            description: >-
              (--lang go-typed-native / rust-typed-native) Write the BC-OWNED
              shared wire types module (WireValue/WireRow/WireList + probe kinds,
              rust also the shared BehaviorError; plus the cross-package/-crate
              batch ports structs) here. This is the module a covered module built
              with --shared-types-import imports. Independent of --out: a build may
              emit the covered module, the shared module, or both. For go the
              emitted package name is derived from --shared-types-import (so
              --shared-types-import is required with go). Rejected for other
              emitters. A standalone --shared-types-out does NOT print the covered
              module to stdout.
            value_name: file
            schema:
              type: string
            file:
              mode: write

          - name: runtime-import
            required: false
            description: Override the runtime import specifier baked into the module.
            value_name: spec
            schema:
              type: string

          - name: shared-types-import
            required: false
            description: >-
              (--lang go-typed-native / rust-typed-native) Package (go) / crate
              (rust) path for the BC-GENERATED shared wire types (WireValue/
              WireRow/WireList; rust also the shared BehaviorError). When set, BC
              emits those types into that shared package/crate and the covered
              module imports them (go: package-qualified; rust: use <path>::*), so
              a separate leaf-transport package/crate can return them without an
              import cycle (cross-package / cross-crate layout). Absent →
              in-package. Ignored by other emitters.
            value_name: spec
            schema:
              type: string

          - name: leaf-transport
            required: false
            repeatable: true
            description: >-
              (--lang rust-typed-native / go-typed-native only) Map a covered leaf
              (catalog component) to the op-agnostic transport symbol the runner
              calls directly, as <leaf>=<symbol>; repeat per leaf. A leaf not
              listed uses the in-package default name (rust leaf_<comp> / go
              Leaf_<comp>). Ignored by other emitters.
            value_name: mapping
            schema:
              type: array
              items:
                type: string

          - name: leaf-transport-import
            required: false
            description: >-
              (--lang rust-typed-native / go-typed-native only) Module the leaf
              transport symbols are imported from (rust `use <module>::{…}`, go
              package import + selector); absent → resolve in-scope / same
              package. Ignored by other emitters.
            value_name: module
            schema:
              type: string

        exits:
          '0':
            description: Module generated (written to --out, or printed to stdout).
            stdout:
              format: text
          '1':
            description: >-
              Generation failed (--from module unimportable / missing --behavior
              export / compile error; or unreadable/invalid --in; or --lang is
              not a registered emitter).
            stderr:
              format: text
          '2':
            description: >-
              Usage error — --lang omitted, neither --from nor --in given (or
              both), or --from without --behavior.
            stderr:
              format: text

        x-agent:
          riskLevel: low
          requiresConfirmation: false
          idempotent: true
          sideEffects:
            - file_write
          sideEffectNote: >-
            Writes to the filesystem only when --out is specified; otherwise
            output goes to stdout.

      # ── check ────────────────────────────────────────────
      check:
        summary: Verify a committed module matches a fresh generation (drift gate).
        description: >-
          Regenerates the module for --lang from the SAME input as generate
          (--from a TS behavior module + --behavior, or --in an IR doc) and
          byte-compares it against the committed --out file. Exits non-zero on
          drift. This is the build's codegen drift gate.
        usage:
          - bc check --lang rust-typed-native --from behaviors.ts --behavior UserRepo --out gen.rs
          - bc check --lang rust-typed-native --in ir-doc.json --out gen.rs

        options:
          - name: lang
            required: true
            description: Target emitter (must be a registered language).
            value_name: emitter
            schema:
              type: string

          - name: from
            required: false
            description: >-
              TS-authoring input (same as generate --from): the consumer's `.ts`
              SOURCE, read by the TS compiler (type-extract + AST-lower, not
              executed; needs the optional peer `typescript`). Regenerated +
              byte-compared against --out. Requires --behavior. Mutually exclusive
              with --in.
            value_name: behaviors.ts
            schema:
              type: string
            file:
              mode: read
              exists: true
              encoding: utf-8

          - name: behavior
            required: false
            description: >-
              (with --from) The name of the exported `@behavior` class to
              compile. Required whenever --from is given.
            value_name: ClassName
            schema:
              type: string

          - name: in
            required: false
            description: >-
              Internal/test input: path to a serialized ComponentGraphIRDoc JSON
              file. Mutually exclusive with --from.
            value_name: ir-doc.json
            schema:
              type: string
            file:
              mode: read
              exists: true
              media_type: application/json
              encoding: utf-8

          - name: out
            required: false
            description: >-
              The committed covered-module file to diff a fresh generation
              against. At least one of --out / --shared-types-out is required.
            value_name: file
            schema:
              type: string
            file:
              mode: read
              exists: true

          - name: shared-types-out
            required: false
            description: >-
              (--lang go-typed-native / rust-typed-native) The committed BC-OWNED
              shared wire types module to diff a fresh generation against (drift
              gate for the file emitted by `generate --shared-types-out`). May be
              checked together with --out in one invocation; at least one of
              --out / --shared-types-out is required. For go, --shared-types-import
              is required (it names the package). Rejected for other emitters.
            value_name: file
            schema:
              type: string
            file:
              mode: read
              exists: true

          - name: runtime-import
            required: false
            description: Override the runtime import specifier baked into the module.
            value_name: spec
            schema:
              type: string

          - name: shared-types-import
            required: false
            description: >-
              (--lang go-typed-native / rust-typed-native) Package (go) / crate
              (rust) path for the BC-GENERATED shared wire types (WireValue/
              WireRow/WireList; rust also the shared BehaviorError). When set, BC
              emits those types into that shared package/crate and the covered
              module imports them (go: package-qualified; rust: use <path>::*), so
              a separate leaf-transport package/crate can return them without an
              import cycle (cross-package / cross-crate layout). Absent →
              in-package. Ignored by other emitters.
            value_name: spec
            schema:
              type: string

          - name: leaf-transport
            required: false
            repeatable: true
            description: >-
              (--lang rust-typed-native / go-typed-native only) Map a covered leaf
              (catalog component) to the op-agnostic transport symbol the runner
              calls directly, as <leaf>=<symbol>; repeat per leaf. A leaf not
              listed uses the in-package default name (rust leaf_<comp> / go
              Leaf_<comp>). Ignored by other emitters.
            value_name: mapping
            schema:
              type: array
              items:
                type: string

          - name: leaf-transport-import
            required: false
            description: >-
              (--lang rust-typed-native / go-typed-native only) Module the leaf
              transport symbols are imported from (rust `use <module>::{…}`, go
              package import + selector); absent → resolve in-scope / same
              package. Ignored by other emitters.
            value_name: module
            schema:
              type: string

        exits:
          '0':
            description: Up to date — the committed --out matches a fresh generation.
            stdout:
              format: text
          '1':
            description: >-
              Drift detected (--out differs or does not exist), or generation
              failed (--from module unimportable / missing --behavior export /
              compile error; or unreadable/invalid --in; or unregistered --lang).
            stderr:
              format: text
          '2':
            description: >-
              Usage error — --lang omitted, neither --from nor --in given (or
              both), --from without --behavior, or neither --out nor
              --shared-types-out was given.
            stderr:
              format: text

        x-agent:
          riskLevel: low
          requiresConfirmation: false
          idempotent: true
          sideEffects: []
