A library of Goose AI agent recipes for Kubernetes platform operations, built around a Confluent for Kubernetes (CFK) / ArgoCD / Kyverno stack running on OpenShift or k3d.
| Requirement | Notes |
|---|---|
| Goose installed | brew install block-goose-cli or see goose docs |
| Ollama running | ollama serve |
| Model pulled | ollama pull qwen3-coder:30b |
| kubectl configured | Correct context pointing at your cluster |
| Goose default provider | Set to Ollama via goose configure |
Not all recipes need the same model. Using a lighter model for summarisation tasks significantly reduces heat, fan noise, and runtime on Apple Silicon.
| Model | Best for | Approx size |
|---|---|---|
qwen3-coder:30b |
Recipes that generate kubectl patches, YAML fixes, or complex code | 18 GB |
gemma4:latest |
Health checks, summarisation, RAG status reporting | 9.6 GB |
qwen2.5-coder:7b |
Quick single-section checks, low-heat background runs | 4.7 GB |
llama3.2:3b |
Minimal resource use, simple queries only | 2.0 GB |
Recommended split:
# Code-heavy recipes (patch generation, YAML output)
--model qwen3-coder:30b
# Summarisation / health check recipes
--model gemma4:latest
# Background / scheduled / low-priority runs
--model qwen2.5-coder:7b# Run any recipe — always use --no-session (see Session DB section below)
goose run --no-session --model qwen3-coder:30b --recipe ~/goose-recipes/<recipe>.yamlEvery recipe has sensible defaults (usually confluent for namespace, argocd for ArgoCD namespace). Override any of them with --params key=value. You can pass multiple --params flags in one command.
# Check a different namespace
goose run --no-session --model qwen3-coder:30b \
--recipe ~/goose-recipes/k8s-pod-review.yaml \
--params namespace=kafka
# Override multiple params at once
goose run --no-session --model qwen3-coder:30b \
--recipe ~/goose-recipes/daily-cluster-health.yaml \
--params namespace=kafka \
--params argocd_namespace=openshift-gitops
# mtls-cert-expiry accepts space-separated namespaces in a single param
goose run --no-session --model qwen3-coder:30b \
--recipe ~/goose-recipes/mtls-cert-expiry.yaml \
--params "namespaces=confluent kafka monitoring"
# To create an output file report
goose run --no-session --model qwen3-coder:30b \
--recipe k8s-pod-review.yaml \
--params namespace=my-app \
> ./pod-review-my-app-$(date +%Y%m%d).md 2>&1To see what parameters a recipe accepts, check the parameters: block at the bottom of its YAML file — each entry shows the key name, default value, and a description.
# Quick way to see a recipe's parameters
grep -A4 "parameters:" ~/goose-recipes/<recipe>.yamlGoose stores all session history in a SQLite DB. Find it with:
goose info
# Sessions DB (sqlite): ~/.local/share/goose/sessions/sessions.dbAlways use --no-session for recipe runs. Without it, Goose loads prior session context before your recipe starts, which degrades model performance across the day — especially when running multiple recipes in the same shell session. --no-session creates a clean isolated run each time.
Note: --no-session still writes a session record to the DB but does not load prior context. The DB grows slowly over time — use the aliases below to keep it manageable.
Token tracking (accumulated_total_tokens) is not populated when using local Ollama models, so Goose cannot enforce context limits automatically. --no-session and goose-clear are your manual safeguards.
# Check DB size
alias goose-size="ls -lh ~/.local/share/goose/sessions/sessions.db"
# Clear everything — recommended before important recipe runs
alias goose-clear="rm ~/.local/share/goose/sessions/sessions.db && echo 'sessions cleared'"
# Trim sessions older than 1 day — safe daily maintenance
alias goose-trim="sqlite3 ~/.local/share/goose/sessions/sessions.db \"DELETE FROM messages WHERE session_id IN (SELECT id FROM sessions WHERE created_at < date('now', '-1 day')); DELETE FROM sessions WHERE created_at < date('now', '-1 day'); VACUUM;\" && echo 'old sessions trimmed'"goose-clear # before starting work
goose run --no-session ... # every recipe run
goose-trim # end of day / weekly cleanupsqlite3 ~/.local/share/goose/sessions/sessions.db "
SELECT id, created_at,
(SELECT COUNT(*) FROM messages WHERE session_id = s.id) as msgs
FROM sessions s
WHERE date(created_at) = date('now')
ORDER BY created_at DESC;"A recipe that should complete in ~10 tool calls showing 30+ messages indicates looping — see Troubleshooting below.
| Recipe | Description |
|---|---|
k8s-pod-review.yaml |
Pod best practice check (resources, probes, security context) |
argocd-sync-status.yaml |
ArgoCD app sync and health status |
kyverno-policy-adherence.yaml |
Kyverno violations per namespace with fixes |
daily-cluster-health.yaml |
Master RAG health check across all areas |
Scans TLS secrets across CFK namespaces, extracts certificate expiry dates and flags anything expiring within 30 days (AMBER) or 7 days (RED).
Parameters
| Parameter | Default | Description |
|---|---|---|
namespaces |
confluent kafka |
Space-separated list of namespaces to scan |
Usage
# Scan default CFK namespaces
goose run --no-session --model qwen3-coder:30b --recipe ~/goose-recipes/mtls-cert-expiry.yaml
# Scan specific namespaces
goose run --no-session --model qwen3-coder:30b --recipe ~/goose-recipes/mtls-cert-expiry.yaml \
--params "namespaces=confluent kafka monitoring"What it checks
- All
kubernetes.io/tlssecrets in each namespace - Confluent-specific secrets containing
ca.crt - CFK component TLS secret references (Kafka, Schema Registry, REST Proxy, Control Center)
- Certificate subject and issuer
Output
RED = expires ≤ 7 days → immediate renewal required
AMBER = expires ≤ 30 days → schedule renewal this week
GREEN = expires > 30 days → no action needed
Checks all CFK custom resources for Ready/NotReady conditions, degraded replicas, and component-level issues. Cross-references CR status with actual pod state.
Parameters
| Parameter | Default | Description |
|---|---|---|
namespace |
confluent |
Namespace where CFK components are deployed |
operator_namespace |
confluent |
Namespace where Confluent Operator is deployed |
Usage
# Default
goose run --no-session --model qwen3-coder:30b \
--recipe ~/goose-recipes/confluent-component-health.yaml
# Custom namespaces
goose run --no-session --model qwen3-coder:30b \
--recipe ~/goose-recipes/confluent-component-health.yaml \
--params namespace=kafka \
--params operator_namespace=confluent-operatorComponents checked
- Kafka (KRaft mode) — phase, readyReplicas, conditions
- Schema Registry
- Kafka REST Proxy
- Control Center
- Kafka Connect (if present)
- ksqlDB (if present)
- KafkaTopic CRs
Output
Tabular component health summary with replica counts, phase, and RAG status. Per-issue detail with describe and log commands.
Cluster-wide audit of all Deployments and StatefulSets with replicas > 1 that have no matching PodDisruptionBudget. Produces a prioritised fix list with ready-to-apply YAML.
Parameters
| Parameter | Default | Description |
|---|---|---|
scope |
cluster |
Either cluster for all namespaces or a specific namespace name |
Usage
# Cluster-wide
goose run --no-session --model qwen3-coder:30b --recipe ~/goose-recipes/pdb-coverage.yaml
# Single namespace
goose run --no-session --model qwen3-coder:30b --recipe ~/goose-recipes/pdb-coverage.yaml \
--params scope=confluentPriority classification
| Priority | Condition |
|---|---|
| HIGH | StatefulSet with replicas > 1, no PDB (Kafka brokers, databases) |
| MEDIUM | Deployment with replicas > 1, no PDB |
| LOW | PDB exists but misconfigured (e.g. minAvailable = replicas) |
Output
Summary table + per-workload fix with ready-to-apply kubectl apply YAML for each missing PDB.
Coverage gap analysis — identifies which namespaces and resource kinds have no Kyverno policies applied, and which Audit-mode policies should be promoted to Enforce.
Parameters
| Parameter | Default | Description |
|---|---|---|
namespace |
confluent |
Primary namespace for resource kind gap analysis |
Usage
goose run --no-session --model qwen3-coder:30b \
--recipe ~/goose-recipes/kyverno-policy-coverage.yaml
goose run --no-session --model qwen3-coder:30b \
--recipe ~/goose-recipes/kyverno-policy-coverage.yaml \
--params namespace=kafkaWhat it analyses
- Which namespaces have no policies at all
- Per-namespace coverage of validate / mutate / verifyImages rule types
- Resource kind gaps: Pod, Deployment, StatefulSet, ServiceAccount, ClusterRoleBinding, Ingress, PVC
- Policies in Audit mode that cover security-critical rules (candidates for Enforce promotion)
- Active PolicyExceptions that may mask coverage
Output
Namespace coverage table, resource kind gap matrix, enforce promotion candidates with patch commands.
Finds completed/failed Jobs, Pods in terminal states, and unused ConfigMaps/Secrets. Outputs a commented-out cleanup script for human review — does NOT auto-delete anything.
Parameters
| Parameter | Default | Description |
|---|---|---|
namespace |
confluent |
Namespace to scan |
max_job_age_hours |
24 |
Completed jobs older than this are flagged |
max_unused_age_days |
7 |
Unused ConfigMaps/Secrets older than this are flagged |
Usage
goose run --no-session --model qwen3-coder:30b \
--recipe ~/goose-recipes/stale-resource-cleanup.yaml
goose run --no-session --model qwen3-coder:30b \
--recipe ~/goose-recipes/stale-resource-cleanup.yaml \
--params namespace=kafka \
--params max_job_age_hours=48 \
--params max_unused_age_days=14Safety: TLS secrets are always excluded from the cleanup candidate list. All delete commands in the output script are commented out and must be explicitly reviewed before running.
Dry-run the generated script
# After saving the output script to cleanup.sh:
sed 's/kubectl delete/kubectl delete --dry-run=client/g' cleanup.sh | zshChecks all namespaces for ResourceQuota and LimitRange presence and shows current usage vs limits. Uses cluster-wide kubectl calls (one per resource type) to avoid per-namespace looping.
Parameters
| Parameter | Default | Description |
|---|---|---|
scope |
cluster |
Either cluster for all namespaces or a specific namespace name |
Usage
# Cluster-wide audit
goose run --no-session --model qwen3-coder:30b \
--recipe ~/goose-recipes/namespace-resource-quota.yaml
# Single namespace
goose run --no-session --model qwen3-coder:30b \
--recipe ~/goose-recipes/namespace-resource-quota.yaml \
--params scope=confluentThresholds
| Level | Condition |
|---|---|
| 🔴 RED | Quota usage > 95% |
| 🟡 AMBER | Quota usage > 80%, or namespace missing ResourceQuota/LimitRange |
| 🟢 GREEN | Usage well below limits, quota and limitrange present |
Output
Quota usage table with used/hard values and RAG status per namespace. Missing quota and limitrange namespaces listed with ready-to-apply YAML (first 5 shown; remainder summarised with count and scoped run hint).
Dependencies
Requires jq-filters/quota-usage.jq in addition to quota-filter.jq and limitrange-filter.jq.
Produces a per-node headroom report comparing allocatable vs requested vs actual resource consumption. Identifies the largest workload schedulable on each node and shows top namespaces by resource consumption.
Parameters
| Parameter | Default | Description |
|---|---|---|
warn_threshold |
80 |
Percentage of allocatable at which to warn (AMBER) |
critical_threshold |
95 |
Percentage of allocatable at which to alert (RED) |
Usage
goose run --no-session --model qwen3-coder:30b \
--recipe ~/goose-recipes/node-capacity-planning.yaml
# Custom thresholds
goose run --no-session --model qwen3-coder:30b \
--recipe ~/goose-recipes/node-capacity-planning.yaml \
--params warn_threshold=70 \
--params critical_threshold=90Metrics required
kubectl top nodes requires metrics-server to be running. If absent the recipe falls back to requested-only analysis.
Output
Per-node table (allocatable / requested / actual / %), scheduling headroom per node, top 5 namespaces by resource consumption, specific recommendations.
Checks all PVCs for Pending/Lost state, PVs with Released/Failed status, StorageClass availability, and identifies unused PVCs not bound to any running pod.
Parameters
| Parameter | Default | Description |
|---|---|---|
scope |
cluster |
Either cluster for all namespaces or a specific namespace |
Usage
goose run --no-session --model qwen3-coder:30b --recipe ~/goose-recipes/pvc-health.yaml
goose run --no-session --model qwen3-coder:30b --recipe ~/goose-recipes/pvc-health.yaml \
--params scope=confluentStatus classifications
| Status | Severity |
|---|---|
| PVC Pending or Lost | RED |
| PV Failed | RED |
| No default StorageClass | RED |
| PV Released (data may exist) | AMBER |
| PVC unmounted by any pod | AMBER |
| FailedMount / FailedAttachVolume events | Reported |
Cluster-wide scan for containers using latest or untagged images. Uses jq pre-filtering on all kubectl calls to avoid stream stall errors on large clusters.
Parameters
| Parameter | Default | Description |
|---|---|---|
scope |
confluent |
Either a specific namespace or cluster for all namespaces. Always start with a specific namespace before running cluster-wide. |
Usage
# Single namespace first (recommended starting point)
goose run --no-session --model qwen3-coder:30b \
--recipe ~/goose-recipes/image-tag-audit.yaml \
--params scope=confluent
# Cluster-wide (only once single namespace run confirms it works)
goose run --no-session --model qwen3-coder:30b \
--recipe ~/goose-recipes/image-tag-audit.yaml \
--params scope=clusterImage classifications
| Class | Condition |
|---|---|
| CRITICAL | Tag is latest or no tag specified |
| HIGH | Named tag present but no SHA digest pinning |
| OK | Image referenced by SHA digest |
Output
Per-namespace summary table, full list of offending images, digest pinning instructions, Kyverno policy YAML to enforce compliance.
For every OutOfSync ArgoCD application, produces a human-readable analysis of what has drifted and classifies it as likely intentional vs accidental.
Parameters
| Parameter | Default | Description |
|---|---|---|
argocd_namespace |
argocd |
Namespace where ArgoCD is deployed |
Usage
goose run --no-session --model qwen3-coder:30b \
--recipe ~/goose-recipes/argocd-drift-report.yaml
goose run --no-session --model qwen3-coder:30b \
--recipe ~/goose-recipes/argocd-drift-report.yaml \
--params argocd_namespace=openshift-gitopsDrift classifications
| Classification | Example |
|---|---|
| LIKELY INTENTIONAL | HPA changed replicas, operator-injected annotations |
| LIKELY ACCIDENTAL | Image tag changed outside Git, manual resource limit patch |
| REQUIRES PRUNING | Resource exists in cluster but not in Git |
Output
Per-app drift table with resource-level detail, cause analysis, ignoreDifferences YAML recommendations for intentional drift, sync commands for accidental drift.
Compliance audit of all PolicyException resources — who created them, when, what rules they bypass, and whether they are still needed.
Parameters
| Parameter | Default | Description |
|---|---|---|
max_exception_age_days |
90 |
Exceptions older than this are flagged as potentially stale |
Usage
goose run --no-session --model qwen3-coder:30b \
--recipe ~/goose-recipes/kyverno-exception-audit.yaml
goose run --no-session --model qwen3-coder:30b \
--recipe ~/goose-recipes/kyverno-exception-audit.yaml \
--params max_exception_age_days=60Exception flags
| Flag | Meaning |
|---|---|
| CRITICAL | Bypasses an Enforce-mode security rule |
| BROAD | Targets a namespace or all resources rather than a specific resource |
| STALE | Older than max_exception_age_days |
| ORPHANED | Targeted resource no longer exists — safe to delete |
Output
Full exception inventory table, per-exception detail with bypassed rule description, kubectl delete commands for orphaned exceptions, annotation commands for adding review dates to critical exceptions.
~/goose-recipes/
├── daily-cluster-health.yaml # Master RAG health check (runs all areas)
│
├── # Confluent / Kafka
├── confluent-component-health.yaml # CFK CR status + pod cross-reference
├── mtls-cert-expiry.yaml # TLS cert expiry across CFK namespaces
│
├── # ArgoCD / GitOps
├── argocd-sync-status.yaml # App sync + health status
├── argocd-drift-report.yaml # OutOfSync drift analysis
│
├── # Kyverno / Policy
├── kyverno-policy-adherence.yaml # Violations per namespace with fixes
├── kyverno-policy-coverage.yaml # Coverage gap analysis
├── kyverno-exception-audit.yaml # PolicyException compliance audit
│
├── # Workload Health
├── k8s-pod-review.yaml # Pod best practice check
├── pdb-coverage.yaml # PodDisruptionBudget coverage
├── image-tag-audit.yaml # latest/untagged image scan
│
├── # Cluster Operations
├── namespace-resource-quota.yaml # ResourceQuota + LimitRange audit
├── node-capacity-planning.yaml # Node headroom + capacity report
├── pvc-health.yaml # PVC/PV/StorageClass health
└── stale-resource-cleanup.yaml # Stale jobs, pods, configmaps, secrets
The daily health check accumulates large amounts of JSON across sections. By the time the model reaches Section 3 (ArgoCD), the context window is often exhausted and Goose exits silently. The fix is twofold: use jq to pre-filter kubectl output, and use a lighter model.
gemma4:latest handles the health check's summarisation workload well and runs significantly cooler than qwen3-coder:30b:
goose run --no-session --model gemma4:latest \
--recipe ~/goose-recipes/daily-cluster-health.yamlReplace raw kubectl ... -o json calls with jq-filtered variants that extract only the fields the model needs. This keeps each section's output to a few KB rather than hundreds of KB.
Key patterns used in the optimised daily-cluster-health.yaml:
# Instead of raw pod JSON (potentially MBs):
kubectl get pods -n confluent -o json
# Use jq to extract only what matters:
kubectl get pods -n confluent -o json | jq '[.items[] | {
name: .metadata.name,
phase: .status.phase,
ready: [.status.containerStatuses[]? | .ready],
restarts: [.status.containerStatuses[]? | .restartCount] | max,
oomKilled: [.status.containerStatuses[]? | .lastState.terminated.reason? == "OOMKilled"] | any,
images: [.spec.containers[].image]
}]'
# Events — last 10 warnings only:
kubectl get events -n confluent \
--field-selector type=Warning \
--sort-by='.lastTimestamp' \
-o json | jq '[.items[-10:] | .[] | {
reason: .reason, message: .message,
object: .involvedObject.name, count: .count
}]'
# ArgoCD apps — key fields only:
kubectl get applications.argoproj.io -n argocd -o json | jq '[.items[] | {
name: .metadata.name,
sync: .status.sync.status,
health: .status.health.status,
lastResult: .status.operationState.phase,
autoSync: (.spec.syncPolicy.automated != null),
selfHeal: (.spec.syncPolicy.automated.selfHeal == true)
}]'Create a dedicated model variant with a larger context for health check runs:
cat > /tmp/Modelfile-health << 'EOF'
FROM gemma4:latest
PARAMETER num_ctx 32768
PARAMETER num_predict 4096
EOF
ollama create gemma4-health -f /tmp/Modelfile-healthThen use --model gemma4-health for the daily check.
Also set a longer keep-alive to prevent Ollama dropping the connection mid-run:
# Add to ~/.zshrc
export OLLAMA_KEEP_ALIVE=30mInstead of one large recipe, run each section as a separate targeted recipe call with a sleep between them. This prevents context exhaustion and keeps the Mac cool:
cat > ~/goose-recipes/run-daily-health.zsh << 'EOF'
#!/bin/zsh
REPORT_DIR="$HOME/cluster-health-reports"
mkdir -p "$REPORT_DIR"
DATE=$(date +%Y%m%d_%H%M)
LOG="$REPORT_DIR/health-$DATE.txt"
MODEL="gemma4:latest"
NS="confluent"
ARGO_NS="argocd"
echo "=== DAILY CLUSTER HEALTH: $(date) ===" > "$LOG"
run_check() {
local label=$1
local recipe=$2
shift 2
echo "\n--- $label ---" >> "$LOG"
goose run --no-session --model $MODEL --recipe "$HOME/goose-recipes/$recipe" \
"$@" >> "$LOG" 2>&1
sleep 30 # cool-down between sections
}
run_check "NODES + WORKLOADS" k8s-pod-review.yaml --params namespace=$NS
run_check "ARGOCD SYNC" argocd-sync-status.yaml --params argocd_namespace=$ARGO_NS
run_check "KYVERNO ADHERENCE" kyverno-policy-adherence.yaml --params namespace=$NS
run_check "CERT EXPIRY" mtls-cert-expiry.yaml --params "namespaces=$NS kafka"
run_check "PDB COVERAGE" pdb-coverage.yaml --params scope=$NS
echo "\n=== COMPLETE: $(date) ===" >> "$LOG"
echo "Report: $LOG"
EOF
chmod +x ~/goose-recipes/run-daily-health.zshlaunchd allows setting ProcessType: Background which tells macOS to deprioritise the process, keeping your foreground work responsive:
cat > ~/Library/LaunchAgents/com.goose.dailyhealth.plist << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.goose.dailyhealth</string>
<key>ProgramArguments</key>
<array>
<string>/bin/zsh</string>
<string>/Users/simonjday/goose-recipes/run-daily-health.zsh</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>7</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
<key>ProcessType</key>
<string>Background</string>
<key>LowPriorityIO</key>
<true/>
</dict>
</plist>
EOF
launchctl load ~/Library/LaunchAgents/com.goose.dailyhealth.plistReports are saved to ~/cluster-health-reports/health-<date>.txt and ready to review when you sit down.
A common failure pattern across recipes is:
Stream decode error: Ollama stream stalled: no data received for 30s.
This may indicate the model is overwhelmed by the request payload.
This happens when a recipe runs kubectl ... -o json without filtering, dumping potentially megabytes of raw JSON into the model context. The fix is always the same: use jq to pre-filter output before it reaches the model.
Rule of thumb: never pass raw -o json output to the model, and never pass multi-line jq as an inline shell argument.
Two patterns that cause failures:
# BAD 1 — raw JSON dumps megabytes into model context
kubectl get pods --all-namespaces -o json
# BAD 2 — multi-line jq inline causes shell quoting errors
kubectl get pods -n confluent -o json | jq '[
.items[] | {
namespace: .metadata.namespace ← shell breaks here
}
]'
# GOOD 1 — jq filter file avoids all quoting issues
cat > /tmp/pods.jq << 'JQ'
[.items[] | {
namespace: .metadata.namespace,
pod: .metadata.name,
phase: .status.phase
}]
JQ
kubectl get pods -n confluent -o json | jq -f /tmp/pods.jq
rm /tmp/pods.jq
# GOOD 2 — single-line jq is safe inline (no quoting issues)
kubectl get pods -n confluent -o json | jq -r '.items[] | .metadata.name'All recipes in this library use the jq -f ./jq-filters/<filter>.jq pattern for any multi-line filter. Single-line jq expressions are used inline where the filter is short enough.
Additional mitigations if stalls persist:
# 1. Use a namespace-scoped run before cluster-wide
--params scope=confluent # instead of scope=cluster
# 2. Use gemma4 instead of qwen3-coder for read/summarise recipes
--model gemma4:latest
# 3. Increase Ollama timeout
export OLLAMA_KEEP_ALIVE=30m # in ~/.zshrc
# 4. Create a larger context model variant
cat > /tmp/Modelfile-ops << 'EOF'
FROM qwen3-coder:30b
PARAMETER num_ctx 32768
EOF
ollama create qwen3-coder-ops -f /tmp/Modelfile-ops
# Then use: --model qwen3-coder-ops| Error | Fix |
|---|---|
model not found |
Use bare model name: --model qwen3-coder:30b not ollama/qwen3-coder:30b |
sqlx-sqlite panic |
Run with --no-session or clear ~/.local/share/goose/sessions/sessions.db |
RecipeExtensionConfigInternal |
Extensions must use structured format: type: builtin / name: developer |
unexpected argument |
Don't use -- for prompts — use --recipe or pipe via stdin |
metrics not available |
Install metrics-server; capacity planning falls back to requested-only mode |
| Goose exits mid-recipe | Context window exhausted — use jq-filtered prompts and/or gemma4-health model variant |
| Mac overheating / fans | Switch to gemma4:latest, use split script with sleep 30 between sections, schedule via launchd at 07:00 |
| Ollama drops connection | Set export OLLAMA_KEEP_ALIVE=30m in ~/.zshrc and restart ollama serve |
Stream stalled: no data for 30s |
Recipe passing raw -o json to model — add | jq -f ./jq-filters/<filter>.jq to all kubectl calls |
Stream decode error mid-output |
Response too large — recipe output capped at 5 examples; switch to scoped run with --params scope=<namespace> or use gemma4:latest |
| Recipe produces wrong findings / ignores tool output | Session context saturation — run goose-clear then rerun with --no-session |
| Good performance yesterday, bad today | Missing --no-session across the day — run goose-clear and always use --no-session |
| Recipe loops (30+ tool calls for a simple check) | Recipe has a step asking for inline arithmetic or per-namespace shell loops — see TROUBLESHOOTING.md |
| Session DB path not found | Path is case-sensitive: ~/.local/share/goose/sessions/sessions.db — verify with goose info |
If switching recipes to use the kubernetes-local MCP server instead of kubectl, always use fully-qualified CRD names:
policyreport.wgpolicyk8s.io (not: policyreport)
clusterpolicyreport.wgpolicyk8s.io (not: clusterpolicyreport)
clusterpolicies.kyverno.io (not: clusterpolicy)
applications.argoproj.io (not: application)
kafka.platform.confluent.io (not: kafka)
Plain short names return no results via MCP tool calls.