the skeleton register
corpus-qa
Your intent sounds like: answer from a corpus, or say that the answer is unknown One of 14 skeletons the pack ships, green as-is under nika check. Fill the # SLOT: lines, repair from the fix lines, re-check. Machines read the catalog; the binary lists them with nika new '?'.
- 4SLOT pointsthe only lines you edit
- 3patterns lockedarrive correct, stay correct
- 0tools grantedthe minimum for the job
- 1838ae7csha-pinnedre-hashed on every test run
the plan
derived from the file, never drawn by handconflicting_idsinvoke
duplicates_consistentinvoke
index_onceinvoke
index_twiceinvokeretrieveinvoke
index_is_idempotentinvokeanswerinfer
compiledinvokefinalizeinvoke
the file, whole
conformance-gated upstream on every spec pushcorpus-qa.nika.yamlsource
# SPDX-License-Identifier: Apache-2.0# yaml-language-server: $schema=https://nika.sh/spec/v1/workflow.schema.json## TEMPLATE · corpus-qa · canonicalize corpus → retrieve → answer or abstain.## Identical duplicates are idempotent, while one id carrying conflicting text# is refused before indexing. An empty retrieval is a valid answer with empty# citations, never a fabricated model response.## Needs · nothing. Runs offline in an empty directory with mock/echo.# Run · nika run <file> --model mock/echonika: corpus-qamodel: mock/echo # SLOT: replace with a local or hosted seatconst: query: "What is the holiday carry-over policy?" # SLOT: the question documents: # SLOT: replace with retrieved source chunks - { id: handbook, text: "Support hours are 09:00 to 17:00 UTC." } - { id: security, text: "Security incidents are reported within one hour." } - { id: handbook, text: "Support hours are 09:00 to 17:00 UTC." }permits: tools: ["nika:jq", "nika:assert"]tasks: conflicting_ids: invoke: tool: nika:jq args: input: ${{ const.documents }} expression: 'sort_by(.id) | group_by(.id) | map(select((map(.text) | unique | length) > 1) | .[0].id)' duplicates_consistent: with: conflicting_ids: ${{ tasks.conflicting_ids.output }} invoke: tool: nika:assert args: condition: ${{ size(with.conflicting_ids) == 0 }} message: "One corpus id names conflicting source text" index_once: after: duplicates_consistent: success invoke: tool: nika:jq args: input: ${{ const.documents }} expression: "sort_by(.id) | unique_by(.id)" index_twice: with: index: ${{ tasks.index_once.output }} invoke: tool: nika:jq args: input: ${{ with.index }} expression: "sort_by(.id) | unique_by(.id)" index_is_idempotent: with: first: ${{ tasks.index_once.output }} second: ${{ tasks.index_twice.output }} invoke: tool: nika:jq args: input: { first: "${{ with.first }}", second: "${{ with.second }}" } expression: ".first == .second" compiled: with: idempotent: ${{ tasks.index_is_idempotent.output }} invoke: tool: nika:assert args: condition: ${{ with.idempotent }} message: "Corpus index changed when canonicalized twice" retrieve: with: index: ${{ tasks.index_once.output }} invoke: tool: nika:jq args: input: { documents: "${{ with.index }}", query: "${{ const.query }}" } expression: '.documents as $docs | .query as $query | ($docs | map(select((.text | ascii_downcase) | contains($query | ascii_downcase))))' answer: with: hits: ${{ tasks.retrieve.output }} when: ${{ size(with.hits) > 0 }} infer: max_tokens: 500 # SLOT: answer spend ceiling prompt: | Answer from these passages only and cite their ids. Question: ${{ const.query }} Passages: ${{ with.hits }} schema: type: object additionalProperties: false required: [answer, citations] properties: answer: { type: string, minLength: 1 } citations: { type: array, minItems: 1, items: { type: string } } finalize: with: hits: ${{ tasks.retrieve.output }} answer: ${{ tasks.answer.output }} invoke: tool: nika:jq args: input: { hits: "${{ with.hits }}", answer: "${{ with.answer }}" } expression: 'if (.hits | length) == 0 then {known: false, answer: "I do not know from this corpus.", citations: []} else {known: true, answer: .answer.answer, citations: .answer.citations} end'outputs: result: ${{ tasks.finalize.output }}sha256 1838ae7cc787baed…. The copy above re-hashes to its pin on every test run (a copy is re-provable, never trusted). Source: corpus-qa.nika.yaml in the spec pack · open it in the playground →
the patterns it locks
`retrieval` | `knowledge` | conflicting-id refusalidempotent sort/unique indexempty citations on unknown
cross-references
Scaffold it locally: nika new corpus-qa my-flow.nika.yaml. Try the shape in the playground, or walk the showcase for the same patterns on real work. Read the spec →