Verification Pipeline
Every changeset submitted by an agent passes through a verification pipeline before it can be merged. This is the quality gate that prevents broken code from reaching your repository.
How It Works
When an agent calls VERIFY, dkod:
- Copies the full repository into an isolated temp directory
- Overlays the changeset's modified files on top
- Resolves which pipeline to run (file, database, or auto-detect)
- Executes each stage and streams results back to the agent
- Updates the changeset status to approved or rejected
Pipeline Resolution Order
dkod resolves the verification pipeline in priority order:
.dkod/pipeline.yamlin your repository (highest priority)- Database pipeline configured via the API
- Auto-detected from project files (lowest priority)
Auto-Detection (Zero Configuration)
Most projects don't need any pipeline configuration. dkod automatically detects your project type from its files and runs the appropriate build and test commands. This works out of the box for all 16 supported languages:
| Project File | Language | Default Steps |
|---|---|---|
Cargo.toml | Rust | cargo check, cargo test |
package.json + bun.lock / bun.lockb | Bun | bun test |
package.json | Node.js | npm test |
pyproject.toml or requirements.txt | Python | pytest |
go.mod | Go | go build ./..., go test ./... |
pom.xml or build.gradle | Java | mvn test or gradle test |
build.gradle.kts | Kotlin | gradle test |
*.csproj or *.sln | C# | dotnet build, dotnet test |
Gemfile | Ruby | bundle exec rake test |
composer.json | PHP | composer test |
Package.swift | Swift | swift build, swift test |
build.sbt | Scala | sbt test |
*.cabal or stack.yaml | Haskell | cabal test or stack test |
CMakeLists.txt or Makefile | C / C++ | make test |
Project.toml | Julia | julia --project test/runtests.jl |
*.sh | Bash | shellcheck *.sh, bash -n *.sh |
| None | — | Auto-approve with warning |
Notes:
- Kotlin projects using the Groovy DSL (
build.gradleinstead ofbuild.gradle.kts) are detected as Java. Both rungradle testidentically, so behavior is the same.- CMake-based C/C++ projects need a custom
.dkod/pipeline.yamlwith two separate steps: one forcmake -B build(configure) and one forcmake --build build(build), beforemake test. Bothcmake -Bandcmake --buildmust be listed inallowed_commands. Shell operators like&&are blocked and cannot combine them into a singlerun:value.
Custom Pipeline Configuration (Optional)
For most projects, auto-detection is all you need. Custom pipelines are useful when you want to add extra steps (linting, AI review, manual approval gates) or restrict which commands are allowed. Create .dkod/pipeline.yaml in your repository root to override the defaults.
Schema
pipeline:
name: my-project # Pipeline name (for logging)
timeout: 10m # Overall timeout (supports s, m, h, d)
allowed_commands: # Optional: restrict which commands are permitted
- cargo check
- cargo clippy
- cargo test
stages:
- name: stage-name
parallel: false # Run steps in parallel? (default: false)
steps:
- name: step-name
type: command # Step type (default: command, see below)
run: cargo check # Shell command (for type: command)
timeout: 60s # Per-step timeout
required: true # Fail the pipeline if this step fails? (default: true)
changeset_aware: false # Scope command to affected packages? (default: false)Step Types
| Type | Description | Required Fields |
|---|---|---|
command | Run a shell command | run |
semantic | AST-based code analysis | check (list of check names) |
agent-review | AI code review via Claude | prompt |
human-approve | Manual approval gate | — |
Note: Available semantic check names include
unused-imports,dead-code,type-safety, andcomplexity. Rundk pipeline checks --listto see all available checks for your project type.
# Example: semantic step with multiple checks
- name: analysis
type: semantic
check:
- unused-imports
- dead-code
- type-safety
timeout: 60sCommand Allowlist
For security, only approved shell commands can run in type: command steps. This does not affect agent-review, semantic, or human-approve step types.
With allowed_commands: Only the listed command prefixes are permitted. Use this to lock down exactly which tools run.
Without allowed_commands: The default allowlist applies:
cargo check,cargo test,cargo clippy,cargo fmt,cargo buildnpm test,npm run lint,npm run checkbun test,bun run lint,bun run checknpx tsc,bunx tscpytest,python -m pytestgo build,go test,go vetmvn test,mvn compile,gradle test,gradle builddotnet build,dotnet testbundle exec rake test,bundle exec rspeccomposer test,php artisan testswift build,swift testsbt test,sbt compilecabal build,cabal test,stack build,stack testjulia --projectshellcheck,bash -nmake check,make test,make lint
Shell metacharacters (;, &, |, `, $, >, <, >>, etc.) are always blocked regardless of allowlist.
Changeset-Aware Commands
When changeset_aware: true, dkod scopes commands to only the affected packages:
- Rust:
cargo testbecomescargo test -p affected-crate-1 -p affected-crate-2;cargo checkbecomescargo check -p affected-crate-1 -p affected-crate-2 - Bun/Node:
bun testbecomesbun test src/components src/pages;npm testbecomesnpm test -- src/components src/pages(directories containing changed files passed as arguments) - Python:
pytestbecomespytest affected_package1 affected_package2(all affected packages as positional arguments) - Go:
go test ./...becomesgo test ./affected/pkg/... ./other/pkg/...;go build ./...becomesgo build ./affected/pkg/... ./other/pkg/... - Java (Maven):
mvn testbecomesmvn test -pl affected-module-1,affected-module-2 - Java/Kotlin (Gradle):
gradle testbecomesgradle :affected-module:test :other-module:test - C#:
dotnet testbecomesdotnet test affected/Project.csprojthendotnet test affected/Other.csproj(dkod runs a separate invocation per affected project, sincedotnet testonly accepts a single project path) - Scala:
sbt testbecomessbt affected/test other/test(whereaffectedandotherare sbt sub-project names, not filesystem paths) - Haskell (Cabal):
cabal testbecomescabal test affected-pkg other-pkg - Haskell (Stack):
stack testbecomesstack test affected-pkg other-pkg
Any Cargo subcommand that accepts -p flags is supported. When transforming commands, any flags in the original run value are preserved — for example, pytest -v becomes pytest -v affected_package1 affected_package2.
This makes verification faster by only testing what changed.
Note: If a command does not match any of the supported transformations above,
changeset_aware: trueis silently treated asfalseand the command runs unchanged against the full project.
Examples
These are optional .dkod/pipeline.yaml configurations. You only need one if you want to customize the default behavior.
Rust Project
pipeline:
name: rust-verify
timeout: 5m
stages:
- name: checks
steps:
- name: clippy
run: cargo clippy
timeout: 60s
changeset_aware: true
required: false
- name: test
steps:
- name: test
run: cargo test
timeout: 3m
changeset_aware: trueTypeScript Project (Bun)
pipeline:
name: ts-verify
timeout: 5m
allowed_commands:
- bun test
- bun run lint
- bunx tsc
stages:
- name: checks
parallel: true
steps:
- name: typecheck
run: bunx tsc --noEmit
timeout: 60s
- name: lint
run: bun run lint
timeout: 30s
required: false
- name: test
steps:
- name: test
run: bun test
timeout: 2m
changeset_aware: truePython Project
pipeline:
name: python-verify
timeout: 5m
allowed_commands:
- pytest
stages:
- name: test
steps:
- name: test
run: pytest -v
timeout: 3m
changeset_aware: trueGo Project
pipeline:
name: go-verify
timeout: 5m
stages:
- name: checks
parallel: true
steps:
- name: vet
run: go vet ./...
timeout: 60s
- name: test
run: go test ./...
timeout: 3m
changeset_aware: trueJava Project (Maven)
pipeline:
name: java-verify
timeout: 10m
stages:
- name: test
steps:
- name: test
run: mvn test
timeout: 5m
changeset_aware: trueKotlin Project (Gradle)
pipeline:
name: kotlin-verify
timeout: 10m
stages:
- name: test
steps:
- name: test
run: gradle test
timeout: 5m
changeset_aware: trueC# Project
pipeline:
name: csharp-verify
timeout: 5m
stages:
- name: checks
steps:
- name: build
run: dotnet build
timeout: 2m
- name: test
run: dotnet test
timeout: 3m
changeset_aware: trueRuby Project
pipeline:
name: ruby-verify
timeout: 5m
stages:
- name: test
steps:
- name: test
run: bundle exec rake test
timeout: 3mPHP Project
pipeline:
name: php-verify
timeout: 5m
stages:
- name: test
steps:
- name: test
run: composer test
timeout: 3mSwift Project
pipeline:
name: swift-verify
timeout: 5m
stages:
- name: checks
steps:
- name: build
run: swift build
timeout: 2m
- name: test
run: swift test
timeout: 3mScala Project
pipeline:
name: scala-verify
timeout: 10m
stages:
- name: test
steps:
- name: test
run: sbt test
timeout: 5m
changeset_aware: trueHaskell Project
pipeline:
name: haskell-verify
timeout: 10m
stages:
- name: test
steps:
- name: test
run: cabal test
timeout: 5m
changeset_aware: trueC / C++ Project
Tip: CMake-based projects should add two cmake steps before
make test. The example below assumes a Makefile already exists. For CMake projects, add bothcmake -Bandcmake --buildto yourallowed_commandsand include acmake -B buildstep (configure) followed by acmake --build buildstep (build) beforemake test.
pipeline:
name: cpp-verify
timeout: 5m
allowed_commands:
- make test
- make check
stages:
- name: test
steps:
- name: test
run: make test
timeout: 3mJulia Project
pipeline:
name: julia-verify
timeout: 5m
stages:
- name: test
steps:
- name: test
run: julia --project test/runtests.jl
timeout: 3mBash Project
pipeline:
name: bash-verify
timeout: 2m
stages:
- name: checks
parallel: true
steps:
- name: shellcheck
run: shellcheck src/*.sh
timeout: 30s
- name: syntax
run: bash -n src/*.sh
timeout: 30sMulti-Stage with AI Review
pipeline:
name: full-verify
timeout: 24h
stages:
- name: checks
parallel: false
steps:
- name: check
run: cargo check
timeout: 60s
- name: test
run: cargo test
timeout: 3m
changeset_aware: true
- name: review
steps:
- name: ai-review
type: agent-review
prompt: Review for security vulnerabilities, error handling, and correctness
timeout: 5m
required: false
- name: approval
steps:
- name: human-gate
type: human-approve
timeout: 7dHow
human-approveworks: When the pipeline reaches ahuman-approvestep, it pauses and notifies the repository owner via the dkod dashboard. The changeset remains in a pending state until a team member explicitly approves or rejects it through the dashboard UI or the API (POST /api/changesets/{id}/approveorPOST /api/changesets/{id}/reject). If no action is taken, the step times out according to the pipeline or step-leveltimeoutsetting. Note that the top-levelpipeline.timeoutacts as a hard ceiling — if it's shorter than a step'stimeout, the pipeline timeout takes precedence.
Next Steps
- Agent Protocol — understand the CONNECT → VERIFY → LAND flow
- Session Isolation — how agents work in isolated sessions
- Multi-Agent Workflows — orchestrate multiple agents
