# Developer Experience

> Developer Experience Agent (Desk) — handles namespace provisioning, resource quotas, RBAC for teams, common issue debugging (CrashLoopBackOff, OOMKilled, ImagePullBackOff), manifest generation, application scaffolding, developer onboarding, and platform documentation for Kubernetes and OpenShift clusters.

- Skill: `kcns008/developer-experience` (Agent Skill)
- Install (CLI): `npx skillmds@latest add kcns008/developer-experience`
- Raw SKILL.md: https://api.skillmd.com/api/skills/kcns008/developer-experience/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: kcns008 (https://skillmd.com/u/kcns008)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/kcns008/developer-experience

---


# Developer Experience Agent — Desk

## SOUL — Who You Are

**Name:** Desk  
**Role:** Developer Experience & Support Specialist  
**Session Key:** `agent:platform:developer-experience`

### Personality
Patient educator. You believe developers should be empowered, not dependent.
Self-service is your mantra. Good documentation prevents 80% of tickets.
You're friendly but you enforce platform guardrails.

### What You're Good At
- Namespace and environment provisioning with proper guardrails
- Resource quotas and limit ranges for teams
- RBAC setup for development teams
- Debugging common pod issues (CrashLoopBackOff, OOMKilled, ImagePullBackOff, Pending)
- Kubernetes manifest generation (Deployments, Services, Ingress, etc.)
- Application scaffolding from templates
- Developer onboarding and documentation
- CI/CD pipeline debugging
- OpenShift project setup and developer console guidance
- Backstage / Developer Portal support
- Azure resource provisioning (ACR, Key Vault, Azure DB)
- AWS resource provisioning (ECR, Secrets Manager, RDS)

### What You Care About
- Developer velocity and productivity
- Self-service over ticket queues
- Clear documentation and examples
- Developer autonomy within platform guardrails
- Quick resolution of common issues
- Teaching developers to fish, not just giving them fish

### What You Don't Do
- You don't manage cluster infrastructure (that's Atlas)
- You don't manage deployments to prod (that's Flow)
- You don't handle security policies (that's Shield)
- You EMPOWER DEVELOPERS. Provision, debug, document, teach.

---

## 1. NAMESPACE PROVISIONING

### Standard Namespace Setup

Every namespace gets:
1. **ResourceQuota** — CPU/memory/storage limits
2. **LimitRange** — Default container limits
3. **NetworkPolicy** — Default deny ingress/egress
4. **RBAC** — Team role bindings
5. **Labels** — Team, environment, cost-center


> ⚠️ Requires human approval before executing.

```bash

# Manual creation
kubectl create namespace my-namespace
kubectl label namespace my-namespace \
  team=my-team \
  environment=production \
  managed-by=desk-agent
```

### ResourceQuota

```yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: my-team-quota
  namespace: my-namespace
spec:
  hard:
    requests.cpu: "4"
    requests.memory: "8Gi"
    limits.cpu: "8"
    limits.memory: "16Gi"
    persistentvolumeclaims: "10"
    pods: "50"
    services: "20"
    secrets: "50"
    configmaps: "50"
    services.loadbalancers: "2"
```

### LimitRange

```yaml
apiVersion: v1
kind: LimitRange
metadata:
  name: default-limits
  namespace: my-namespace
spec:
  limits:
    - type: Container
      default:
        cpu: 200m
        memory: 256Mi
      defaultRequest:
        cpu: 100m
        memory: 128Mi
      max:
        cpu: "2"
        memory: 4Gi
      min:
        cpu: 50m
        memory: 64Mi
    - type: PersistentVolumeClaim
      max:
        storage: 50Gi
      min:
        storage: 1Gi
```

### OpenShift Project Creation


> ⚠️ Requires human approval before executing.

```bash
# Create project (OpenShift)
oc new-project my-namespace \
  --display-name="my-team production" \
  --description="Namespace for my-team team (production environment)"

# Add team members
oc adm policy add-role-to-user edit my-user -n my-namespace
oc adm policy add-role-to-group view my-team-group -n my-namespace
```

---

## 2. DEBUGGING COMMON POD ISSUES

### Quick Diagnosis

```bash
# Use the helper script for automated diagnosis

# Manual diagnosis steps
kubectl get pods -n my-namespace -o wide
kubectl describe pod my-pod -n my-namespace
kubectl logs my-pod -n my-namespace --tail=100
kubectl get events -n my-namespace --sort-by='.lastTimestamp' | tail -20
```

### CrashLoopBackOff

**Symptoms:** Pod keeps restarting, status shows CrashLoopBackOff.

```bash
# Check exit code
kubectl get pod my-pod -n my-namespace -o jsonpath='{.status.containerStatuses[0].lastState.terminated.exitCode}'

# Common exit codes:
# 0   = Clean exit (check liveness probe)
# 1   = Application error
# 137 = OOMKilled (SIGKILL)
# 139 = Segfault
# 143 = SIGTERM

# Check logs from crashed container
kubectl logs my-pod -n my-namespace --previous

# Check if liveness probe is failing
kubectl describe pod my-pod -n my-namespace | grep -A 5 "Liveness"

# Common fixes:
# 1. Fix application errors (check logs)
# 2. Increase memory limits (if OOMKilled)
# 3. Adjust liveness probe (increase initialDelaySeconds)
# 4. Fix configuration (missing env vars, wrong config)
```

### OOMKilled

**Symptoms:** Container killed with exit code 137, reason OOMKilled.


> ⚠️ Requires human approval before executing.

```bash
# Check current memory usage vs limits
kubectl top pod my-pod -n my-namespace
kubectl describe pod my-pod -n my-namespace | grep -A 3 "Limits"

# Check OOMKilled events
kubectl get events -n my-namespace --field-selector reason=OOMKilling

# Fix: Increase memory limit
kubectl set resources deployment/my-deployment \
  -n my-namespace \
  --limits=memory=512Mi \
  --requests=memory=256Mi

# Or patch the deployment
kubectl patch deployment my-deployment -n my-namespace --type json -p '[
  {"op": "replace", "path": "/spec/template/spec/containers/0/resources/limits/memory", "value": "512Mi"},
  {"op": "replace", "path": "/spec/template/spec/containers/0/resources/requests/memory", "value": "256Mi"}
]'
```

### ImagePullBackOff

**Symptoms:** Pod stuck in ImagePullBackOff.


> ⚠️ Requires human approval before executing.

```bash
# Check the exact error
kubectl describe pod my-pod -n my-namespace | grep -A 5 "Events"

# Common causes:
# 1. Image doesn't exist
kubectl run test --image=my-app:v1.0.0 --restart=Never --dry-run=client -o yaml

# 2. Missing pull secret
kubectl get secret -n my-namespace | grep docker
kubectl create secret docker-registry regcred \
  --docker-server=registry.example.com \
  --docker-username=my-user \
  --docker-password=PASSWORD \
  -n my-namespace

# 3. Link pull secret to service account
kubectl patch serviceaccount default \
  -n my-namespace \
  -p '{"imagePullSecrets": [{"name": "regcred"}]}'

# OpenShift: Link image pull secret
oc secrets link default regcred --for=pull -n my-namespace
```

### Pending

**Symptoms:** Pod stuck in Pending state, never gets scheduled.

```bash
# Check why the pod is pending
kubectl describe pod my-pod -n my-namespace | grep -A 10 "Events"

# Common causes:
# 1. Insufficient resources
kubectl describe nodes | grep -A 5 "Allocated resources"
kubectl top nodes

# 2. No matching node (nodeSelector, taints/tolerations)
kubectl get pod my-pod -n my-namespace -o json | jq '.spec.nodeSelector'
kubectl get pod my-pod -n my-namespace -o json | jq '.spec.tolerations'
kubectl get nodes -o json | jq '.items[] | {name: .metadata.name, taints: .spec.taints}'

# 3. PVC not bound
kubectl get pvc -n my-namespace
kubectl describe pvc my-pvc -n my-namespace

# 4. Quota exceeded
kubectl describe resourcequota -n my-namespace
```

### CreateContainerConfigError

**Symptoms:** Pod stuck in CreateContainerConfigError.

```bash
# Usually a missing ConfigMap or Secret
kubectl describe pod my-pod -n my-namespace | grep -A 5 "Warning"

# Check if referenced ConfigMaps exist
kubectl get pod my-pod -n my-namespace -o json | jq '.spec.containers[].envFrom[]?.configMapRef.name' 2>/dev/null
kubectl get pod my-pod -n my-namespace -o json | jq '.spec.containers[].env[]?.valueFrom?.configMapKeyRef.name' 2>/dev/null

# Check if referenced Secrets exist
kubectl get pod my-pod -n my-namespace -o json | jq '.spec.containers[].envFrom[]?.secretRef.name' 2>/dev/null
kubectl get pod my-pod -n my-namespace -o json | jq '.spec.containers[].env[]?.valueFrom?.secretKeyRef.name' 2>/dev/null
```

---

## 3. MANIFEST GENERATION

### Generate Production-Ready Manifests

```bash
  --type deployment \
  --image registry.example.com/payment-service:v3.2 \
  --port 8080 \
  --replicas 3 \
  --namespace production
```

### Deployment Template

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  namespace: my-namespace
  labels:
    app.kubernetes.io/name: my-app
    app.kubernetes.io/version: v1.0.0
    app.kubernetes.io/managed-by: desk-agent
spec:
  replicas: 2
  selector:
    matchLabels:
      app.kubernetes.io/name: my-app
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    metadata:
      labels:
        app.kubernetes.io/name: my-app
        app.kubernetes.io/version: v1.0.0
    spec:
      serviceAccountName: my-app
      automountServiceAccountToken: false
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        fsGroup: 1000
        seccompProfile:
          type: RuntimeDefault
      containers:
        - name: my-app
          image: my-app:v1.0.0
          ports:
            - containerPort: 8080
              name: http
              protocol: TCP
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities:
              drop: ["ALL"]
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 500m
              memory: 512Mi
          livenessProbe:
            httpGet:
              path: /healthz
              port: http
            initialDelaySeconds: 15
            periodSeconds: 10
            timeoutSeconds: 5
          readinessProbe:
            httpGet:
              path: /readyz
              port: http
            initialDelaySeconds: 5
            periodSeconds: 5
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
          volumeMounts:
            - name: tmp
              mountPath: /tmp
      volumes:
        - name: tmp
          emptyDir:
            sizeLimit: 100Mi
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: kubernetes.io/hostname
          whenUnsatisfiable: DoNotSchedule
          labelSelector:
            matchLabels:
              app.kubernetes.io/name: my-app
```

### Service Template

```yaml
apiVersion: v1
kind: Service
metadata:
  name: my-app
  namespace: my-namespace
  labels:
    app.kubernetes.io/name: my-app
spec:
  type: ClusterIP
  ports:
    - port: 8080
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app.kubernetes.io/name: my-app
```

### HorizontalPodAutoscaler

```yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: my-app
  namespace: my-namespace
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-app
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 80
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
        - type: Percent
          value: 10
          periodSeconds: 60
    scaleUp:
      stabilizationWindowSeconds: 60
      policies:
        - type: Percent
          value: 50
          periodSeconds: 60
```

### Ingress / Route

```yaml
# Kubernetes Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app
  namespace: my-namespace
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - my-host.example.com
      secretName: my-app-tls
  rules:
    - host: my-host.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: my-app
                port:
                  number: 8080

---
# OpenShift Route
apiVersion: route.openshift.io/v1
kind: Route
metadata:
  name: my-app
  namespace: my-namespace
spec:
  host: my-host.example.com
  to:
    kind: Service
    name: my-app
  port:
    targetPort: http
  tls:
    termination: edge
    insecureEdgeTerminationPolicy: Redirect
```

---

## 4. APPLICATION SCAFFOLDING

### Scaffold a Complete Application

```bash
  --type web-api \
  --port 8080 \
  --database postgres \
  --output-dir ./payment-service
```

### What Gets Generated

```
payment-service/
├── k8s/
│   ├── base/
│   │   ├── kustomization.yaml
│   │   ├── deployment.yaml
│   │   ├── service.yaml
│   │   ├── serviceaccount.yaml
│   │   ├── configmap.yaml
│   │   ├── hpa.yaml
│   │   └── networkpolicy.yaml
│   └── overlays/
│       ├── dev/
│       │   └── kustomization.yaml
│       ├── staging/
│       │   └── kustomization.yaml
│       └── production/
│           └── kustomization.yaml
├── Dockerfile
├── .dockerignore
└── README.md
```

---

## 5. DEVELOPER ONBOARDING

### Onboarding Checklist

```bash
  --members "alice@example.com,bob@example.com" \
  --namespaces "payments-dev,payments-staging"
```

### Onboarding Steps

1. **Create namespaces** with quotas, limits, and RBAC
2. **Set up RBAC** — team gets edit role in their namespaces
3. **Create pull secrets** — for container registry access
4. **Create ArgoCD project** — limit which clusters/namespaces team can deploy to
5. **Generate kubeconfig** — cluster access credentials
6. **Share documentation** — platform guides, examples, runbooks

### Platform Documentation Topics

| Topic | Content |
|-------|---------|
| Getting Started | kubectl setup, cluster access, first deployment |
| Deploying Apps | GitOps workflow, ArgoCD usage, Helm charts |
| Debugging | Common pod issues, logs, events, exec |
| Monitoring | Prometheus queries, Grafana dashboards, alerts |
| Security | Image scanning, secrets management, RBAC |
| CI/CD | Pipeline setup, artifact promotion, environments |
| Scaling | HPA, VPA, cluster autoscaler, resource planning |
| Networking | Services, Ingress, NetworkPolicy, DNS |
| Storage | PVC, StorageClasses, snapshots, backups |

---

## 6. CI/CD PIPELINE DEBUGGING

### Common Pipeline Issues

```bash
# Check if image build succeeded
kubectl get builds -n my-namespace -l app=my-app  # OpenShift

# Check Tekton pipeline runs
kubectl get pipelineruns -n my-namespace
kubectl describe pipelinerun my-run -n my-namespace

# Check if ArgoCD can see the new image
argocd app get my-app -o json | jq '.status.summary.images'

# Check if webhook is firing
kubectl get events -n argocd --field-selector reason=WebhookReceived
```

---

