• Overview
  • Functions
  • Definitions
  • Automation Issue Lifecycle
  • Automation Issue Lifecycle - Corner Cases
  • Hints for KI-Writers
  • HIRO 6 - Transition Guide for Users having used HIRO 5 before
  • KI Best Practices
  • KI Debugging
  • Transition from KI Syntax 5

Knowledge Item Syntax 6 is a complete re-engineering of the KI language from scratch. It makes use of many concepts of the old syntax but has some significant changes. The following instructions are meant to guide you to understand the differences. The new Knowledge Item Syntax:

  • No longer in XML format

  • Works pretty much the same

  • Easier to write and read

Changes at a glance

  • Elements are called Functions now

  • Blocks are now Sections

  • Scope uses :: instead of : (Example: LOCAL::Var instead of LOCAL:Var)

  • No more explicit var operations, all done with implicit syntax

  • Implied "exists" match unless specified otherwise

  • Specialized "count" operator


Zombie Cleanup KI (Old Syntax)
<KI xmlns="https://graphit.co/schemas/v2/KiSchema" >
    <Title>Zombie Cleanup</Title>
    <Description>only use in case of zombie apocalypse</Description>
        <Var Mode="string" Name="MachineClass" Value="Linux" />
        <Var Mode="exists" Name="KillZombieParents" />
        <Execute Host="${NODE:NodeName}" RootMode="true" ScriptMode="true">
            <![CDATA[for pid in $(ps -A -ostat,ppid | awk '/[zZ]/{print $2}'); do kill $pid; done]]>
        <VarDelete Name="KillZombieParents" />
New KI Syntax
  title: "Zombie Cleanup"
  description: "only use in case of zombie apocalypse"
  MachineClass == "Linux"
  output: LOCAL::Result = execute("for pid in $(ps -A -ostat,ppid | awk '/[zZ]/{print $2}'); do kill $pid; done", host: "${NodeName}", scriptmode: "true")

Full list of incompatible changes to old syntax

  1. LOCAL scope is removed from ordering of scopes. That means, if you add something to LOCAL scoping, then it will be the first scope, which will be checked by lookup.

  2. LOCAL scope exists in prepare mode, but is not global as it was (e.g. initializing a LOCAL::COUNTER to 0 in PREPARE and then increasing it in the DO section will only ever increase from 0 to 1)

  3. NodeID, KIID, IID are autoimported and explicit matching in conditions does not work.

  4. It is no longer allowed to use non-existing variables in prepare/do blocks. The same for conditional variables in or statements, which may not exist, can’t be used later.

  5. created_on currently only supports self and any and no more specific origins

  6. KI Syntax does not support value ranges natively

  7. The "eject" command was changed to set the issue in WAITING state, EJECTED state has been removed completely

  8. Sections on and when can’t be empty

  9. Matching in on and when the same variable multiple times works differently now. All matches were evaluated, but only last was saved. Now it has accumulative effect (Foobar > 0 Foobar < 5 before produced anything smaller 5, if there exists something bigger than 5, now it will results, that Foobar is between 0 and 5).

How to use the new syntax


Aliases are declared by using the as expression. This becomes increasingly important once you want to use the same attribute multiple times with different match sets. Each alias will create a new "value space" while accessing the same variable or alias multiple times will narrow down the previously matched value list.

Variable as VAR1

Example for multi-aliasing the same variable

Variable["Key1"] as VAR1
Variable["Key2"] as VAR2

This will make VAR1 and VAR2 each point to different values of Variable with the respective keys. Without using "as" this would create a never matching condition as matching on "Key1" would create a list that can never contain a "Key2".


You can either bind to a specific node or to a relative node in a relationship of:

  • @connected

  • @out

  • @in

Use the with …​ end statement to create a more specific context, this will create a separate scope for this match

with <ogit type>@<relation:connected/out/in>:<connection type>
# How does the with@related:attribute expression work - https://github.com/arago/civ_hiro/blob/master/kis/a.GameControl/PrintScore.ki#L8

  # this node has a connection ogit/playedby to his parent with a ogit/_type ogit/Freeciv/Game
  with ogit/Freeciv/Game@in:ogit/playedby
    # What should additionally be in this node

Reading Data

Adding data to the execution context of your Issue is as simple as matching a variable or value in a Knowledge Item. Only data that is matched will be added to the context of the Issue and will be available for processing. HIRO does not guess related data, you must teach it to read/match this data if you need it for your use case.

Data Manipulation

You can add, replace and remove data values from variables.

Simple assignment

This should be the default way to reassign or add completely new variables. If used on a variable not previously matched, it will by default replace all values valid for the current scope for that variable with the given value(s). Using @self or @any scoping can be used here as well to replace only local or all values instead. Using the assignment operator on a matched variable will actually remove the previously selected value(s) and add the new value(s) instead.

  • =

A = 1
A = ["a", "b", "c"]
A = B
NODE::A = "f"


Add() can be used to force creation of a new value in an existing variable without overwriting a previously matched value in the process.

  • add()

    • single values - "d"

    • lists - ["a","b","c"]

    • Can assign value from variable in context or new string value

add(A = "d")
add(A = ["a","b","c"])
add(NODE::A = "f")


With delete() either all values for the given variable from the current scope and context will be deleted or only specific values matching additional conditions.

  • delete()

    • single values - "d"

    • lists - ["a","b","c"]

delete(A, "b")

The second example will only remove values with a specific key from the variable while the third example will only remove specific values from the variable.


The set() command can be used to overwrite existing variable values which may include values that are not in the current context

  • set()

    • single values - "d"

    • lists - ["a","b","c"]

set(A = "d")
set(A = ["a","b","c"])
set(A["key"] = "e")
set(NODE::A = "f")


Like in the old syntax the do section will execute the described steps once the conditions of a Knowledge Item are met. While you can use if/else statements to add logic to your Knowledge Items, please be aware that this should be severely limited to checking temporary results of other commands. If possible make if/else decisions in separate KIs on or when blocks. Never ever have if as first command in your do block, this is considered an anti pattern and is an almost certain sign that this KI should be split into two.

  • if

  • else

  • then

  • end

if statements must be closed with and end
if Status == 0 and Output != "" then
  SpotRequestInfo[EC2SpotRequestID] = Output
  if ERROR =~ "does not exist" then
    log("spot request not existing anymore, removing")


Creating JSON Data in KI

  LOCAL::JSON = '{
  "op": "call_service",
  "service": "/app_ctrl/stop_app",
  "args": {}

Using JavaScript in KI

  result: NODE::KeysList = javascript("
    for (var i=1; i<= 26;i++){

Predefined Action Handlers

There are some predefined ActionHandlers that can be used without additional configuration:

  • HTTP

  • local

  • SSH

  • websocket

code: LOCAL::CODE,
body: LOCAL::RESULT = action("HTTP", url: "http://localhost/somepath", method: "POST", body: "{\"expr\": \"1 + 1\"}")
exit: LOCAL::STORESTATUS = execute("echo testing execute", host: "_AAE_", timeout: 60)

result: LOCAL::STORESTATUS = action("ExecuteLocalCommand", command: "echo testing execute", timeout: 60)
exit: LOCAL::STORESTATUS = execute("echo testing execute", host: "remote.host.com", timeout: 60)

result: LOCAL::STORESTATUS = action("ExecuteCommand", command: "echo testing execute", host: "remote.host.com", timeout: 60)
data: LOCAL::RESULT = action("websocket", url:"ws://${ogit/host}:9090", data: '${LOCAL::JSON}')