# LoomCycle Connector abstraction diagram (v0.8.15 → v0.17.x)
# Render: d2 docs/architecture-connector.d2 docs/assets/architecture-connector.png
#
# Shows the Connector interface as the single architectural contract
# that every wire transport translates into. HTTP server is the canonical
# implementation; MCP, gRPC, and (future) CLI servers CONSUME the
# interface; TS / Python adapters MIRROR the operation surface in their
# own languages over the HTTP wire.

vars: {
  d2-config: {
    layout-engine: elk
    pad: 24
  }
}

direction: down

# ---- The Connector interface (center) ----

connector: "connector.Connector\n(internal/connector/connector.go)" {
  style: {
    fill: "#fce7f3"
    stroke: "#be185d"
    stroke-width: 3
    border-radius: 8
    bold: true
    font-size: 16
  }

  methods: "39 methods (v0.17.x)" {
    grid-columns: 4
    style: {
      fill: "#fdf2f8"
      font-size: 11
    }

    # Run lifecycle
    spawn:   "SpawnRun" {style.fill: "#fbcfe8"}
    cancel:  "CancelRun" {style.fill: "#fbcfe8"}
    getrun:  "GetRun" {style.fill: "#fbcfe8"}
    listrun: "ListRuns" {style.fill: "#fbcfe8"}

    # Agent registry
    regag:   "RegisterAgent" {style.fill: "#fbcfe8"}
    unreg:   "UnregisterAgent" {style.fill: "#fbcfe8"}
    listag:  "ListAgents" {style.fill: "#fbcfe8"}
    streamr: "StreamUserRunStates\n(v0.12.x)" {style.fill: "#fbcfe8"}

    # Substrate tools
    mem:     "Memory" {style.fill: "#fbcfe8"}
    adef:    "AgentDef" {style.fill: "#fbcfe8"}
    sdef:    "SkillDef\n(v0.8.22)" {style.fill: "#fbcfe8"}
    ev:      "Evaluation" {style.fill: "#fbcfe8"}

    ctx:     "Context" {style.fill: "#fbcfe8"}
    intres:  "InterruptionResolve\n(v0.8.16)" {style.fill: "#fbcfe8"}

    # Substrate Def tools (one op-discriminated method each)
    mcpdef:  "MCPServerDef\n(v0.9.x)" {style.fill: "#fbcfe8"}
    schdef:  "ScheduleDef\n(v0.12.7)" {style.fill: "#fbcfe8"}
    whdef:   "WebhookDef\n(v0.14.0)" {style.fill: "#fbcfe8"}
    mbdef:   "MemoryBackendDef\n(v0.15.0)" {style.fill: "#fbcfe8"}
    a2asc:   "A2AServerCardDef\n(v0.13.0)" {style.fill: "#fbcfe8"}
    a2aad:   "A2AAgentDef\n(v0.13.0)" {style.fill: "#fbcfe8"}

    # Channel substrate (v0.9.x channel CRUD)
    ch:      "Channel\n(in-band tool)" {style.fill: "#fbcfe8"}
    listch:  "ListChannels\n(v0.9.x)" {style.fill: "#fbcfe8"}

    pubch:   "PublishChannel\n(v0.9.x)" {style.fill: "#fbcfe8"}
    subch:   "SubscribeChannel\n(v0.9.x)" {style.fill: "#fbcfe8"}
    peekch:  "PeekChannel\n(v0.9.x)" {style.fill: "#fbcfe8"}
    ackch:   "AckChannel\n(v0.9.x)" {style.fill: "#fbcfe8"}

    crch:    "CreateChannel\n(v0.11.5)" {style.fill: "#fbcfe8"}
    upch:    "UpdateChannel\n(v0.11.5)" {style.fill: "#fbcfe8"}
    delch:   "DeleteChannel\n(v0.11.5)" {style.fill: "#fbcfe8"}

    # Pause / Snapshot (v0.8.17-18, +purple tint)
    pause:   "PauseRuntime\n(v0.8.17)" {style.fill: "#e9d5ff"}

    resume:  "ResumeRuntime\n(v0.8.17)" {style.fill: "#e9d5ff"}
    state:   "GetRuntimeState\n(v0.8.17)" {style.fill: "#e9d5ff"}
    snapc:   "CreateSnapshot\n(v0.8.17)" {style.fill: "#e9d5ff"}
    snapl:   "ListSnapshots\n(v0.8.17)" {style.fill: "#e9d5ff"}

    snapg:   "GetSnapshot\n(v0.8.18)" {style.fill: "#e9d5ff"}
    snape:   "ExportSnapshot\n(v0.8.17)" {style.fill: "#e9d5ff"}
    snapr:   "RestoreSnapshot\n(v0.8.17)" {style.fill: "#e9d5ff"}
    snapd:   "DeleteSnapshot\n(v0.8.17)" {style.fill: "#e9d5ff"}

    # Hooks (v0.12.5 cluster, lighter pink to mark cluster surface)
    hookr:   "RegisterHook\n(v0.12.5)" {style.fill: "#fdf2f8"}
    hookl:   "ListHooks\n(v0.12.5)" {style.fill: "#fdf2f8"}
    hookd:   "DeleteHook\n(v0.12.5)" {style.fill: "#fdf2f8"}
  }
}

# ---- IMPLEMENTERS (the canonical surface) ----

implementers: "IMPLEMENTS" {
  style: {
    fill: "#dcfce7"
    stroke: "#15803d"
    stroke-width: 2
    border-radius: 8
    font-size: 13
    italic: true
  }

  http: "*lchttp.Server\ninternal/api/http/\nconnector_impl.go" {
    style: {
      fill: "#bbf7d0"
      stroke: "#15803d"
      bold: true
      font-size: 13
    }
  }

  http_note: "Canonical business logic\nvar _ connector.Connector = (*Server)(nil)\n(compile-time drift prevention)" {
    style: {
      fill: "#dcfce7"
      italic: true
      font-size: 10
    }
  }

  http -> http_note: {style.stroke: "#15803d"; style.stroke-dash: 2}
}

# ---- CONSUMERS (transport adapters) ----

consumers: "CONSUMES" {
  style: {
    fill: "#dbeafe"
    stroke: "#1d4ed8"
    stroke-width: 2
    border-radius: 8
    font-size: 13
    italic: true
  }

  direction: right

  mcp: "*lcmcp.Server\nstdio + HTTP MCP\n40 meta-tools (v0.21.0)" {
    style: {fill: "#bfdbfe"; bold: true; font-size: 12}
  }
  grpc: "*loomgrpc.Server\nproto handlers\n(v0.8.15 refactor)" {
    style: {fill: "#bfdbfe"; bold: true; font-size: 12}
  }
  cli: "*lccli.Server\nloomcycle run X\n(deferred)" {
    style: {fill: "#e0e7ff"; italic: true; font-size: 12; stroke-dash: 4}
  }
}

# ---- MIRRORS (client-side adapters in other languages) ----

mirrors: "MIRRORS (in other languages, over HTTP wire)" {
  style: {
    fill: "#fef3c7"
    stroke: "#a16207"
    stroke-width: 2
    border-radius: 8
    font-size: 13
    italic: true
  }

  direction: right

  ts: "@loomcycle/client\nadapters/ts/\n(TypeScript — v0.11.5+)" {
    style: {fill: "#fde68a"; bold: true; font-size: 12}
  }
  py: "loomcycle (PyPI)\nadapters/python/\n(Python — v0.7.0+, async)" {
    style: {fill: "#fde68a"; bold: true; font-size: 12}
  }
}

# ---- Edges ----

implementers.http -> connector: "satisfies" {
  style: {stroke: "#15803d"; stroke-width: 2; bold: true}
}

consumers.mcp -> connector: "dispatches via" {style.stroke: "#1d4ed8"; style.stroke-width: 2}
consumers.grpc -> connector: "dispatches via" {style.stroke: "#1d4ed8"; style.stroke-width: 2}
consumers.cli -> connector: "dispatches via" {style.stroke: "#1d4ed8"; style.stroke-dash: 4}

# Mirrors call into the running implementer via the HTTP wire, not the Go interface itself.
mirrors.ts -> implementers.http: "HTTP /v1/*" {
  style: {stroke: "#a16207"; stroke-width: 2}
}
mirrors.py -> implementers.http: "HTTP /v1/*" {
  style: {stroke: "#a16207"; stroke-width: 2}
}

# ---- Note: consumers hold the same business logic via the field ----

field_note: "Consumers each hold a `connector.Connector` field\ntypically pointing at the running `*lchttp.Server` instance.\nDirect Go method dispatch — NO HTTP round-trips between\nMCP/gRPC handlers and the underlying business logic." {
  style: {
    fill: "#f8fafc"
    italic: true
    font-size: 11
    stroke: "#64748b"
  }
}

consumers -> field_note: {style.stroke: "#64748b"; style.stroke-dash: 2}
