AGPL-3.0-or-later · forever.

showcase · tier 2 · business

Contract guard

Clause extraction on a local model, so the contract never leaves the machine. A validate and an assert gate the memo.

Legal-grade caution, sovereign by default. Ollama runs the whole review offline. · conformance-gated in nika-spec ↗ · re-proven at every push

  • 6tasksthe plan falls out of the bindings
  • 6wavesparallel by construction
  • 2verbs exercisedinvoke · infer
  • T2the tierbusiness

The whole file

6 tasks · 6 waves

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

contract-guard.nika.yamlsource
nika: v1workflow:  id: contract-guard  description: "Local-model clause extraction → schema gate → risk memo"# Local · the whole review runs offline, zero cloud. Also a NON-thinking# model on purpose: a thinking model can spend the whole `max_tokens` budget# in its think block and return before the JSON (engine#428).model: ollama/llama3.2:3binputs:  contract_path:    type: string    default: "examples/fixtures/contract.md"    description: "Path to the contract (markdown or plain text)"permits:  tools: ["nika:assert", "nika:read", "nika:validate", "nika:write"]  fs:    # `permits:` cannot interpolate (NIKA-AUTH-007), so this literal does not    # follow `inputs.contract_path` around: passing --var without editing this    # line is refused at run with NIKA-SEC-004. Deliberate — a reviewer can    # see every file this workflow may open by reading four words.    read: ["examples/fixtures/contract.md"]    write: ["out/risk-memo.md"]tasks:  contract:    invoke:      tool: "nika:read"      args: { path: "${{ inputs.contract_path }}" }  clauses:    with:      contract: ${{ tasks.contract.output }}    infer:      max_tokens: 1600      prompt: |        Extract every risk-bearing clause from this contract ·        ${{ with.contract }}        Quote each clause verbatim · classify its risk.      schema:        type: object        additionalProperties: false        required: [clauses]        properties:          clauses:            type: array            items:              type: object              additionalProperties: false              required: [quote, type, risk]              properties:                quote: { type: string }                type: { type: string, enum: [liability, termination, ip, payment, data, other] }                risk: { type: string, enum: [low, medium, high] }  check:    with:      clauses: ${{ tasks.clauses.output }}    invoke:      tool: "nika:validate"      args:        data: "${{ with.clauses }}"        format: json        schema:          type: object          required: [clauses]          properties:            clauses:              type: array              minItems: 1        # an empty extraction is a FAILED extraction  gate:    with:      extraction_ok: ${{ tasks.check.output.valid == true }}   # the whole condition crosses as ONE boundary expression    invoke:      tool: "nika:assert"      args:        condition: "${{ with.extraction_ok }}"        message: "Clause extraction failed the schema gate: refusing to write the memo"  memo:    with:      clauses: ${{ tasks.clauses.output.clauses }}    after:      gate: success              # state, no data · the assert passed or the memo is cancelled    infer:      max_tokens: 1200      prompt: |        Write a one-page risk memo from these clauses ·        ${{ with.clauses }}        Order by risk · high first · cite the quoted text.  save:    with:      memo: ${{ tasks.memo.output }}    invoke:      tool: "nika:write"      args:        path: out/risk-memo.md        content: "${{ with.memo }}"        create_dirs: trueoutputs:  clauses:    value: ${{ tasks.clauses.output.clauses }}    description: "Typed risk-bearing clauses, verbatim quotes"  memo: ${{ tasks.memo.output }}

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

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