Claude meet CUE: From Intuition to Precision with Self-Correcting AI#
AI coding assistants like Claude Code and Copilot are marvels of intuition. Born from the connectionist school of AI, they can generate code, configuration, and documentation with remarkable fluency. But as we discussed in our blog post about safer AI, this intuition comes with a challenge: a lack of precision.
How do we bridge the gap between an AI's probabilistic world and the deterministic systems it needs to control? How do we build systems that don't just hope for the bestโavoiding the "YOLO-factor"โbut guarantee correctness?
This guide demonstrates how to solve one instance of this classic configuration correctness problem. We'll show you how to pair the intuitive power of Claude Code with the logical rigor of CUE to create robust, self-correcting workflows that are provably correct. While we use Claude Code as an example, all LLMs face this same precision problem.
This guide is a hands-on tutorial. You can find all the code to follow along and run the experiments yourself in this GitHub repository.
The Setup: A Simple Go Project#
We'll start with a basic "hello world" Go application. This gives us a concrete repository for our intuitive AI partner, Claude Code, to work with.
We can confirm everything is working as expected.
Our goal is to ask Claude Code to create a GitHub Actions CI workflow for this project. This is a perfect task for an intuitive AI assistant.
Part 1: The "YOLO Factor" in Action#
A natural way to work with an AI assistant is to simply tell it what you want. A key to effective prompting is to be specific about the goal and the verification steps.
Here, we ask Claude Code not only to create the workflow but also to validate
it using a specific cue
command. This command leverages our curated CUE
schema for GitHub
Actions, adding a layer
of logical verification to Claude Code's intuitive output.
> I want to host this repo on GitHub and use GitHub Actions for CI. Please
template an appropriate workflow and validate that workflow using `cue vet -c
-d '#Workflow' cue.dev/x/githubactions@latest workflow.yml`
Claude Code correctly identifies the project type and generates a valid workflow file.
โ Bash(find . -maxdepth 2 -type f -name "*.json" -o -name "*.js" -o -name "*.ts" -o -name "*.py" -o -name "*.go" -o -name "*.rs" -o -name "Dockerfile" -o -name "*.yml"โฆ)
...
โ Read(go.mod)
ย Read 4 lines (ctrl+r to expand)
โ Bash(find . -name "*.go" | head -10)
ย ./cmd/hello/main.go
โ Read(cmd/hello/main.go)
ย Read 8 lines (ctrl+r to expand)
...
โ Perfect! This is a simple Go project with a basic "hello world" application.
Let me update the todo and create an appropriate GitHub Actions workflow for Go
projects.
โ Bash(mkdir -p .github/workflows)
โ Write(.github/workflows/ci.yml)
ย ย ย Wrote 72 lines to .github/workflows/ci.yml
โ Now let me update the todo and validate the workflow using the cue command:
โ Bash(cue vet -c -d '#Workflow' cue.dev/x/githubactions@latest .github/workflows/ci.yml)
โ Perfect! I've created a comprehensive GitHub Actions CI workflow for your Go
project at .github/workflows/ci.yml and validated it successfully using the CUE
command.
The command succeeded! We now have a workflow file, and thanks to CUE, we have a high degree of confidence that it's structurally valid.
name: CI
on:
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.20'
- name: Build
run: go build -v ./...
- name: Test
run: go test -v ./...
The Fallacy of Conversational Memory#
This is great, but it requires us to remember to ask for validation in every prompt. It would be better if our AI partner could remember this rule. Let's try telling it to do so.
> I want you to always validate GitHub Actions workflow files when they are
updated. Please remember this
Claude Code's response is reassuring, but it reveals the core challenge of working with purely intuitive systems.
โ Understood. I'll always validate GitHub Actions workflow files using CUE
after any updates. Let me validate the updated workflow now:
โ Bash(cue vet -c -d '#Workflow' cue.dev/x/githubactions@latest .github/workflows/ci.yml)
โ Validation successful - the updated workflow is valid.
"I'll always validate GitHub Actions workflow..." sounds promising, but is it true? Let's probe further.
> How and where is "I'll always validate GitHub Actions workflow files using
CUE after any updates" remembered/configured?
Claude Code's response is honest and insightful.
โ That instruction isn't actually remembered or configured anywhere in the
system. I was responding conversationally to your request, but Claude Code
doesn't have persistent memory between sessions or a way to configure custom
validation rules.
To make this behavior automatic, you could:
1. Add it to a project README or CLAUDE.md file - I can read project
documentation to understand conventions
2. Create a git pre-commit hook that runs the CUE validation
3. Add the validation step directly to your CI workflow so it runs
automatically
4. Use a Makefile or script that combines workflow updates with validation
This is a critical lesson and a perfect illustration of the "YOLO-factor."
Conversational promises are not persistent configurations. Relying on the
AI's "memory" is simply hoping for the best. While Claude Code cleverly
suggests workarounds like a CLAUDE.md
file, these are still fragile solutions
that don't scale and lack the rigor needed for production systems.
In a production environment, these conversational promises are not reliable. We need to engineer precision.
Part 2: Engineering Precision with Programmatic Guardrails#
Instead of relying on conversational context, we can enforce our rules with deterministic, programmatic controls. This is how we move from hoping for correctness to guaranteeing it.
Claude Code hooks provide the perfect mechanism. Hooks are the composable guardrails that ensure every AI action is safe and correct.
The Hook Configuration#
First, we create a settings file that registers our hook. This configuration
tells Claude Code: "After any tool is used (PostToolUse
), run our CUE-powered
validation script."
{
"hooks": {
"PostToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": ".claude/validate-github-actions.sh"
}
]
}
]
}
}
The Validation Logic: Shareable, Precise, and Declarative#
Our command points to a simple shell script that pipes Claude Code's action context to a CUE command.
Step 1: The Shell Script Wrapper#
#!/bin/bash
if ! cat - | cue cmd validate >&2; then
exit 2
fi
Edit
), the
file that was changed, the session ID, and more.
Step 2: Modeling the Hook's Data with a CUE Schema#
To work with this data safely, we first define a CUE schema that describes its structure. This gives us type-safety and allows us to robustly parse the event data.
package example
// #hookInput defines the base schema of all hook inputs. Per the CUE that
// appears at https://docs.anthropic.com/en/docs/claude-code/hooks#hook-input
#hookInput: {
session_id!: string
transcript_path!: string
cwd!: string
permission_mode?: string
hook_event_name!: string
}
// #toolBase is the base set of fields for a hook tool.
#toolBase: {
#hookInput
tool_name!: string
tool_input?: _
tool_response?: _
}
// #preToolUseInput is the base schema for PostToolUse hooks.
#preToolUseInput: {
#toolBase
hook_event_name!: "PreToolUse"
}
// #editTool is the schema for the Edit tool
#editTool: {
#toolBase
tool_name!: "Edit"
tool_input?: {
file_path!: string
old_string!: string
new_string!: string
}
tool_response?: {
filePath!: string
oldString!: string
newString!: string
originalFile!: string
structuredPatch: [...]
replaceAll?: bool
userModified?: bool
}
}
// #postToolUseInput is the base schema for PostToolUse hooks.
#postToolUseInput: {
#toolBase
hook_event_name!: "PostToolUse"
tool_response!: _
}
#preEditToolUseInput: {
#editTool
#preToolUseInput
}
#postEditToolUseInput: {
#editTool
#postToolUseInput
}
Step 3: Implementing the Validation Logic in CUE#
The core logic is defined entirely in CUE. It performs three key tasks:
- It reads and parses the JSON event data from Claude Code using the schema we defined.
- It checks if the event was an
Edit
and if the file path was inside.github/workflows
and ends with.yml
. - If both conditions are true, it executes cue vet using our curated GitHub Actions schema.
This declarative approach ensures our validation only runs exactly when and where we want it to.
The cue vet
command in our script references a schema,
cue.dev/x/githubactions@latest
. This schema is part of a CUE module; a
shareable, versioned unit of configuration logic. This principle of sharing and
reuse is central to CUE. Just as we use this public module for the base schema,
a company could create its own internal modules to define and distribute
policies for runner sizes, approved actions, or security rules, ensuring
consistency across all AI- and human-generated configurations.
package example
import (
"tool/os"
"tool/file"
"encoding/json"
"strings"
"path"
"tool/exec"
)
_os: string @tag(os, var=os)
command: validate: {
env: os.Getenv & {
CLAUDE_PROJECT_DIR: string
}
read: file.Read & {
$after: env
filename: "/dev/stdin"
contents: string
hookInput: {#hookInput, ...}
hookInput: json.Unmarshal(contents)
}
if (read.hookInput & #postEditToolUseInput) != _|_ {
let filepath = path.ToSlash(read.hookInput.tool_input.file_path, _os)
let filename = path.Base(filepath, _os)
let dir = path.Rel(env.CLAUDE_PROJECT_DIR, path.Dir(filepath), _os)
if strings.HasSuffix(filename, ".yml") && dir == ".github/workflows" {
validate: exec.Run & {
cmd: ["cue", "vet", "-c", "-d=#Workflow", "cue.dev/x/githubactions@latest", filepath]
}
}
}
}
Part 3: The Neuro-Symbolic Payoff: A Self-Correcting Loop#
Let's see this synthesis of intuition and logic in action. To make things
interesting, we'll manually introduce an error into our ci.yml
file. The line
something: else
is not valid.
name: CI
on:
pull_request:
branches: [ main ]
# sneak in an error!
something: else
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.20'
- name: Build
run: go build -v ./...
- name: Test
run: go test -v ./...
gofmt
step.
Claude Code suggests the correct edit, but it is unaware of our hidden error. We accept the change. Immediately, our hook fires, CUE validates the file, and the precise, logical feedback is sent back to Claude Code.
โ Update(.github/workflows/ci.yml)
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ Edit file โ
โ โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ โ
โ โ .github/workflows/ci.yml โ โ
โ โ โ โ
โ โ 15 uses: actions/setup-go@v4 โ โ
โ โ 16 with: โ โ
โ โ 17 go-version: '1.20' โ โ
โ โ 18 + - name: Check formatting โ โ
โ โ 19 + run: gofmt -s -d . โ โ
โ โ 20 - name: Build โ โ
โ โ 21 run: go build -v ./... โ โ
โ โ 22 - name: Test โ โ
โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ โ
โ Do you want to make this edit to ci.yml? โ
โ โฏ 1. Yes โ
โ 2. Yes, allow all edits during this session (shift+tab) โ
โ 3. No, and tell Claude what to do differently (esc) โ
โ โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
โ Update(.github/workflows/ci.yml)
ย Updated .github/workflows/ci.yml with 2 additions
15 uses: actions/setup-go@v4
16 with:
17 go-version: '1.20'
18 + - name: Check formatting
19 + run: gofmt -s -d .
20 - name: Build
21 run: go build -v ./...
22 - name: Test
> Edit operation feedback:
- [.claude/validate-github-actions.sh]: something: field not allowed:
.github/workflows/ci.yml:7:1
task failed: command ["cue" "vet" "-c" "-d=#Workflow" "cue.dev/x/githubactions@latest" "/home/myitcv/tmp/claude_go_example/.github/workflows/ci.yml"]
failed: exit status 1
This is the magic momentโthe neuro-symbolic payoff.
This rapid "shift left" feedback loop does more than just find errors. It enables faster iteration towards a correct solution and reduces inference cost. Because the feedback is immediate and precise, the AI doesn't waste time and tokens pursuing dead-end strategies or trying to debug a vague failure. It knows exactly what's wrong and can correct it in the next step.
Claude Code, the intuitive agent, sees the specific CUE error (something:
field not allowed
) and immediately takes action to fix the underlying
problem, proposing a new edit to remove the offending line.
โ Update(.github/workflows/ci.yml)
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ Edit file โ
โ โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ โ
โ โ .github/workflows/ci.yml โ โ
โ โ โ โ
โ โ 3 pull_request: โ โ
โ โ 4 branches: [ main ] โ โ
โ โ 5 โ โ
โ โ 6 - # sneak in an error! โ โ
โ โ 7 - something: else โ โ
โ โ 8 - โ โ
โ โ 6 jobs: โ โ
โ โ 7 test: โ โ
โ โ 8 runs-on: ubuntu-latest โ โ
โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ โ
โ Do you want to make this edit to ci.yml? โ
โ โฏ 1. Yes โ
โ 2. Yes, allow all edits during this session (shift+tab) โ
โ 3. No, and tell Claude what to do differently (esc) โ
โ โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
We accept the fix. The hook runs a final time, cue vet
passes, and the
process completes successfully. We are left with a final ci.yml
that is
provably correct.
Try it for yourself!#
Ready to move from theory to practice? All the code, CUE schemas, and hook
configurations from this guide are available in this GitHub
repository. Clone it today, fire
up the claude
assistant, and experience the power of building a
self-correcting AI workflow for yourself!
Conclusion: From Guesswork to Confidence#
This simple example demonstrates how CUE can introduce precision to generative AI systems. However, the real power of CUE lies in its composability.
A real-world scenario would combine multiple sets of constraints and policies together. Company-specific policies could be enforced, such as:
- Action Allow-listing: Define a list of approved GitHub Actions.
- Security Policies: Enforce that all jobs run with the least privilege.
- Resource Management: Validate that self-hosted runner labels conform to team budgets.
This composition happens directly in CUE. Instead of just validating against the base GitHub Actions schema, you would validate the AI's generated YAML with both the schema and your company's policies.
By combining the intuitive reasoning of Claude Code with the precise, shareable, and composable logic of CUE, we build sophisticated, policy-driven systems that are not only powerful but also provably safe, compliant, and correct. This is how we replace guesswork with confidence.
Intrigued? Want to know more?#
Get in touch with us to discuss the unique challenges in your architecture and explore how CUE can help you to make your AI integrations more reliable.