Key points
goalis the LLM-backed step mode. It cannot be combined withscript,tasks,include, orapproval.llm_enableddefaults totrue. Setting it tofalsebansoutput.items, every stepcondition, everygoal, and any script carrying blocking guardrail or policy knowledge context.modelresolves in order: task, step, pipeline, configured default.- The named model must be configured and allowed in the run scope; AAA decides whether the caller may use it.
- A goal runs inside the per-run agent. There is no always-on LLM service, and the agent calls the provider the resolved model names.
- A task can carry a goal too, so one step can mix deterministic and model-driven work across its tasks.
Examples
llm_enabled: true
model: reasoning-large
- name: release-notes
depends_on: [publish]
goal: Read the commits since the last tag and write release notes to /workspace/NOTES.md.
model: reasoning-largename: release-service
description: Build, verify, and publish the payments service.
container_image: alpine:3.20
working_directory: /workspace
timeout: 45m
display_option: list
llm_enabled: true
model: reasoning-large
variables:
- RELEASE_CHANNEL
- platform/shared:ARTIFACT_BUCKET
steps:
- name: build
script: |
make build
echo "1.0.$(date +%s)" > /nopsai/outputs/BUILD_TAG
outputs:
- name: BUILD_TAG
- name: verify
image: golang:1.24
depends_on: [build]
runtime_pool: ci
volumes:
- build-cache:/root/.cache
script: |
go vet ./...
go test ./...
- name: checks
depends_on: [verify]
tasks:
- name: licenses
ignore_failure: true
script: |
./scripts/license-check.sh
- name: sbom
script: |
./scripts/sbom.sh > /workspace/sbom.json
sha256sum /workspace/sbom.json | cut -d' ' -f1 > /nopsai/outputs/SBOM_DIGEST
outputs:
- name: SBOM_DIGEST
- name: risk-review
depends_on: [checks.sbom]
goal: Review the changed files and list the risks this release carries.
model: reasoning-large
- name: report
depends_on: [checks.licenses, checks.sbom]
variables:
SBOM_DIGEST: $steps.checks.sbom.outputs.SBOM_DIGEST
script: |
echo "checks complete"
echo "sbom $SBOM_DIGEST"
- name: package
depends_on: [checks]
secrets:
- REGISTRY_TOKEN
- platform/shared:SIGNING_KEY
variables:
BUILD_TAG: $steps.build.outputs.BUILD_TAG
script: |
./scripts/package.sh "$BUILD_TAG"
echo "channel $RELEASE_CHANNEL, bucket $ARTIFACT_BUCKET"
./scripts/mint-token.sh > /nopsai/outputs/PUBLISH_TOKEN
./scripts/sign.sh --key "$SIGNING_KEY" --token "$REGISTRY_TOKEN"
outputs:
- name: PUBLISH_TOKEN
sensitive: true
- name: announce
depends_on: [package]
condition: Only run when the release channel is stable.
ignore_failure: true
script: |
./scripts/announce.sh
- name: production-gate
depends_on: [package]
approval:
type: production-release
teams:
- platform/sre
allow_self_approval: false
timeout: 24h
- name: publish
depends_on: [production-gate]
script: |
./scripts/promote.sh
- name: release-notes
depends_on: [publish]
goal: Read the commits since the last tag and write release notes to /workspace/NOTES.md.
model: reasoning-largellm_enabled: false
steps:
- name: release-notes
goal: Write the release notes.Field reference
llm_enabledpipelinebooleanOptionaltrue
Set false to declare a script-only pipeline. Validation then rejects every LLM-backed construct.
llm_enabled: false- With
false, the pipeline cannot defineoutput.items, any stepcondition, anygoal, or a script that carries blocking guardrail/policy knowledge context.
services/nopsai/pkg/validation/pipeline.go
modelpipelinestringOptionalConfigured default model
Default provider/model profile for conditions, goals, and final outputs.
model: reasoning-large- The profile must be configured and allowed in the run scope.
steps[].model, tasks[].model, output.model, output.items[].model
AAA still decides whether the original caller may use the selected profile.
services/nopsai/pkg/validation/pipeline.go
steps[].goalstep modestringConditionalNone
Single LLM-backed goal for the step.
goal: Update the changelog with every merged pull request since the last tag.- Rejected when
llm_enabled: false. - Cannot be combined with
scriptortasks.
steps[].modelstepstringOptionalPipeline `model`
Model-client override applied to the step condition and to tasks that do not set their own.
model: fast-smalltasks[].model
tasks[].goaltaskstringConditionalNone
LLM-backed task goal. Mutually exclusive with script; exactly one of the two is required.
goal: Summarize the failing tests and propose a fix.- Rejected when
llm_enabled: false.
services/nopsai/pkg/validation/pipeline.go
tasks[].modeltaskstringOptionalStep `model`, then pipeline, then configured default
Most specific model-client override for this task.
model: reasoning-largeHow it works
A goal is a statement of intent with a definition of done, not a prompt fragment. "Read the commits since the last tag and write release notes to /workspace/NOTES.md" tells the agent what to read, what to produce, and where to put it.
The workspace is the interface. A goal step reads and writes the same /workspace the script steps use, which is why an LLM step can sit in the middle of a deterministic pipeline without special plumbing.
Model selection is a governance decision as much as a quality one. Naming a model in the pipeline makes it reviewable, and the scope check makes it enforceable.
Implementation evidence
services/nopsai/pkg/validation/pipeline.goRules the validator enforces on this directive set.
doc/llm-model-selection.mdModel resolution and provider selection.

