AGPL-3.0-or-later · forever.

showcase · tier 2 · ops & support

Support triage

The overnight queue gets classified with schema enums, first replies drafted, urgent tickets escalated.

By 9am the board is tagged, drafted and batched. Humans handle the hard ones. · conformance-gated in nika-spec ↗ · re-proven at every push

  • 6tasksthe plan falls out of the bindings
  • 4wavesparallel by construction
  • 2verbs exercisedinvoke · infer
  • T2the tierops & support

The whole file

6 tasks · 4 waves

The plan falls out of the bindings, nothing is scheduled by hand.

support-triage.nika.yamlsource
nika: v1workflow:  id: support-triage  description: "Ticket queue → typed triage → urgent escalation → triage board"# A NON-thinking local model, deliberately: a thinking model can spend the# whole `max_tokens` budget in its think block and return before the JSON# (engine#428). Schema showcases pick a model that answers directly.model: ollama/llama3.2:3b   # local · zero key · swap for groq/llama-3.3-70b (triage wants speed)const:  queue_path: "examples/fixtures/support-queue.json"secrets:  oncall_webhook:    source: env    key: ONCALL_WEBHOOK_URL    egress:                       # sanction the one send · the secret IS the URL      - to: "nika:notify"        host_from_self: truepermits:  tools: ["nika:jq", "nika:notify", "nika:read", "nika:uuid", "nika:write"]  fs:    read: ["examples/fixtures/support-queue.json"]    # One board per batch id. `*` matches a single segment and never crosses    # `/`, so the uuid can never steer the write out of out/ — `out/**` would    # let it.    write: ["out/triage-*.json"]  net:    # `host_from_self:` above sanctions the FLOW (the secret may be the URL) —    # it does not grant the capability. The host stays unknown at check, so it    # is judged at RUN against this bound: name the escalation host here or the    # send is refused mid-run, after the tokens are already spent.    http: ["hooks.slack.com"]tasks:  batch:    invoke:      tool: "nika:uuid"      args: { version: v7 }  queue:    invoke:      tool: "nika:read"      args: { path: "${{ const.queue_path }}" }  triage:    with:      queue: ${{ tasks.queue.output }}    infer:      max_tokens: 1500                 # the whole queue in one call · a ceiling, not UNBOUNDED      prompt: |        Triage every ticket in this queue ·        ${{ with.queue }}        For each · classify category and urgency, draft a 2-sentence first reply.      schema:        type: object        additionalProperties: false    # a deterministic shape across providers        required: [tickets]        properties:          tickets:            type: array            items:              type: object              additionalProperties: false              required: [id, category, urgency, first_reply]              properties:                id: { type: string }                category: { type: string, enum: [billing, bug, how-to, account, other] }                urgency: { type: string, enum: [low, normal, high, critical] }                first_reply: { type: string }  urgent:    with:      tickets: ${{ tasks.triage.output.tickets }}    invoke:      tool: "nika:jq"      args:        input: "${{ with.tickets }}"        expression: 'map(select(.urgency == "high" or .urgency == "critical"))'  escalate:    with:      urgent: ${{ tasks.urgent.output }}      batch: ${{ tasks.batch.output }}    when: ${{ size(with.urgent) > 0 }}    invoke:      tool: "nika:notify"      args:        channel: webhook        target: "${{ secrets.oncall_webhook }}"        message: "Urgent tickets in triage batch ${{ with.batch }} · ${{ with.urgent }}"        severity: warning  board:    with:      tickets: ${{ tasks.triage.output.tickets }}      batch: ${{ tasks.batch.output }}    invoke:      tool: "nika:write"      args:        # A `.json` artifact takes ONE interpolated value as `content:` and        # lets the engine serialize it. Typing `{ "tickets": ${{ … }} }` by        # hand emits unquoted fields and the board stops being JSON.        path: "out/triage-${{ with.batch }}.json"        content: "${{ with.tickets }}"        create_dirs: trueoutputs:  tickets:    value: ${{ tasks.triage.output.tickets }}    description: "The classified queue with drafted first replies"

nika inspect · engine 0.106.1 · vendored graph, never re-derived

6 declared edges · 4 waves · the plan falls out of the bindings