Skip to content

Latest commit

 

History

History
388 lines (303 loc) · 13.2 KB

File metadata and controls

388 lines (303 loc) · 13.2 KB

SPEC.md - K8s Agent Execution via OpenCode Subagents

Project Name: Agentic SDLC Runner
Version: 1.0.0
Date: 2026-02-14
Status: Draft


1. Overview

Purpose

Enable local OpenCode CLI sessions to spawn and control remote AI agent pods running in Kubernetes, with full visibility from the main session.

Scope

  • Spawn K8s pods for [ASYNC] tasks via OpenCode subagents
  • Stream pod logs back to the main session
  • Autonomous execution with git-based completion detection
  • Integration with existing spec-kit workflow

Out of Scope

  • External controller (we use subagents)
  • K-Agent framework
  • Beads issue tracking
  • Web dashboard
  • Mayor pattern (Gastown)

2. Architecture

High-Level Flow

┌─────────────────────────────────────────────────────────────────────────┐
│                    MAIN OPENCODE SESSION                                │
│                                                                         │
│   Human: /implement                                                    │
│     │                                                                  │
│     ├──► Subagent 1: "Implement T001"                                │
│     │       ├── scripts/spawn-pod.sh T001 branch-async repo-url      │
│     │       ├── scripts/tail-logs.sh pod-001 → streams to main       │
│     │       └── git commit + push when done                          │
│     │                                                                  │
│     ├──► Subagent 2: "Implement T002" (parallel)                   │
│     │       ├── scripts/spawn-pod.sh T002 branch-async repo-url      │
│     │       ├── scripts/tail-logs.sh pod-002 → streams to main       │
│     │       └── git commit + push when done                          │
│     │                                                                  │
│     └──► Subagent N: "Implement T00N" (parallel)                   │
│             ├── scripts/spawn-pod.sh T00N branch-async repo-url      │
│             ├── scripts/tail-logs.sh pod-00N → streams to main       │
│             └── git commit + push when done                          │
└─────────────────────────────────────────────────────────────────────────┘
                    │
                    ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                         K8s CLUSTER                                    │
│                                                                         │
│   ┌──────────────┐  ┌──────────────┐  ┌──────────────┐             │
│   │ Pod 1        │  │ Pod 2        │  │ Pod N        │             │
│   │ opencode run │  │ opencode run │  │ opencode run │             │
│   │ + spec/plan │  │ + spec/plan  │  │ + spec/plan  │             │
│   └──────────────┘  └──────────────┘  └──────────────┘             │
│                                                                         │
│   Each pod: git clone → implement → git push → done                   │
└─────────────────────────────────────────────────────────────────────────┘

Git-Based Communication

Step Action
1 Developer creates branch: specs/feature-001/task-001-xxx-async
2 Orchestrator detects [ASYNC] task
3 Pod starts: git clone -b <branch>
4 Agent works autonomously
5 Agent: git add . && git commit && git push
6 Completion detected via log output

3. Components

3.1 Deployment

Component Purpose Location
Helm Chart K8s resources templating charts/agentic-sdlc-agent-runner/
Release (Dev) Dev environment values releases/agentic-sdlc-agent-runner-dev/
Release (Stg) Staging environment values releases/agentic-sdlc-agent-runner-stg/
Release (Prod) Production environment values releases/agentic-sdlc-agent-runner-prod/

3.2 Helper Scripts

Script Purpose Location
spawn-pod.sh Creates K8s pod for a task using Helm templates scripts/spawn-pod.sh
tail-logs.sh Streams pod logs to stdout scripts/tail-logs.sh

3.3 K8s Manifests (Helm Templates)

File Purpose
templates/namespace.yaml Create namespace for agents
templates/rbac.yaml ServiceAccount and roles
templates/configmap.yaml Shared configuration (non-sensitive)
templates/external-secret.yaml External Secrets Operator integration
templates/pod-template.yaml Agent pod template
templates/_helpers.tpl Helm helper templates

3.4 Docker

File Purpose
docker/Dockerfile.opencode Container image with OpenCode + git

4. Technical Design

4.1 Helm Deployment

Deploy the runner using Helm:

# Development
helm upgrade --install agentic-sdlc-agent-runner-dev \
  releases/agentic-sdlc-agent-runner-dev \
  -n agent-runner-dev --create-namespace

# Staging
helm upgrade --install agentic-sdlc-agent-runner-stg \
  releases/agentic-sdlc-agent-runner-stg \
  -n agent-runner-stg --create-namespace

# Production
helm upgrade --install agentic-sdlc-agent-runner-prod \
  releases/agentic-sdlc-agent-runner-prod \
  -n agent-runner-prod --create-namespace

Spawn a task pod using the helper script (after Helm deployment):

# Using the Helm-based spawn-pod.sh script
./scripts/spawn-pod.sh task-001 specs/feature/task-001-async https://github.com/user/repo

# With SSH authentication
SSH_SECRET_NAME=github-deploy-key ./scripts/spawn-pod.sh task-001 specs/feature/task-001-async git@github.com:user/repo.git

# Production environment
ENVIRONMENT=prod NAMESPACE=agent-runner-prod ./scripts/spawn-pod.sh task-001 specs/feature/task-001-async https://github.com/user/repo

The script uses helm template to generate the pod manifest from the release charts and applies it with kubectl apply.

4.2 Log Streaming

Stream logs from a K8s pod using kubectl:

#!/bin/bash
# Tails logs from a K8s pod to stdout

NAMESPACE="${NAMESPACE:-agent-runner}"
POD_NAME="$1"

if [ -z "$POD_NAME" ]; then
    echo "Usage: $0 <pod-name>"
    exit 1
fi

kubectl logs -f -n "$NAMESPACE" "$POD_NAME"

4.3 Pod Template (Helm)

See templates/pod-template.yaml for the full pod spec template. Key features:

{{- define "agentic-sdlc-agent-runner.podTemplate" -}}
restartPolicy: Never
serviceAccountName: {{ include "agentic-sdlc-agent-runner.serviceAccountName" . }}
containers:
  - name: agent
    image: "{{ .Values.pod.image.repository }}:{{ include "agentic-sdlc-agent-runner.imageTag" . }}"
    env:
      - name: OPENCODE_SERVER_PASSWORD
        valueFrom:
          secretKeyRef:
            name: {{ include "agentic-sdlc-agent-runner.externalSecretName" . }}
            key: server-password
      - name: GIT_REPO
        value: "{{ .Values.task.defaultRepo }}"
      - name: GIT_BRANCH
        value: "{{ .Values.task.defaultBranch }}"
    resources:
      {{- toYaml .Values.pod.resources | nindent 6 }}
{{- end }}

5. Integration with Spec-Kit

5.1 Flow

spec-kit                           Our Implementation
─────────                          ──────────────────
spec.md ──────► /plan ──────────► tasks.md
    │                               │
    │                               ├── [SYNC] tasks → Human does locally
    │                               │
    │                               └── [ASYNC] tasks → Spawns subagent
    │                                              │
    │                                              ├── scripts/spawn-pod.sh
    │                                              ├── scripts/tail-logs.sh
    │                                              └── completion detection

5.2 Integration Point

The integration happens at /implement command:

  • Parse tasks.md for [ASYNC] markers
  • For each [ASYNC] task, spawn an OpenCode subagent
  • Subagent creates K8s pod via Helm or kubectl

5.3 Context Delivery

Each pod receives:

  • Git branch (from tasks.md)
  • Repository URL (from git remote)
  • Context files (spec.md, plan.md) via git clone

6. K8s Resources (Helm Templates)

6.1 Namespace

See templates/namespace.yaml:

{{- if .Values.namespace.create }}
apiVersion: v1
kind: Namespace
metadata:
  name: {{ include "agentic-sdlc-agent-runner.namespace" . }}
  labels:
    {{- include "agentic-sdlc-agent-runner.labels" . | nindent 4 }}
{{- end }}

6.2 RBAC

See templates/rbac.yaml and templates/serviceaccount.yaml:

  • ServiceAccount with optional Workload Identity annotations
  • Role with pod management permissions
  • RoleBinding

6.3 External Secrets

See templates/external-secret.yaml:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: {{ include "agentic-sdlc-agent-runner.externalSecretName" . }}
spec:
  refreshInterval: {{ .Values.externalSecret.refreshInterval }}
  secretStoreRef:
    name: {{ .Values.externalSecret.secretStore.name }}
    kind: {{ .Values.externalSecret.secretStore.kind }}
  target:
    name: {{ include "agentic-sdlc-agent-runner.externalSecretName" . }}
  data:
    - secretKey: server-password
      remoteRef:
        key: {{ .Values.externalSecret.remoteRef.key }}

Security Note: Secrets are managed via External Secrets Operator, not hardcoded in ConfigMap.


7. Docker

7.1 Dockerfile.opencode

FROM ubuntu:22.04

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install -y \
    curl \
    git \
    ca-certificates \
    && rm -rf /var/lib/apt/lists/*

RUN curl -fsSL https://opencode.ai/install.sh | bash

RUN mkdir -p /workspace

WORKDIR /workspace

CMD ["/bin/bash"]

8. Directory Structure

agentic-sdlc-agent-runner/
├── SPEC.md                      # This file
├── PRD.md                       # Product requirements
├── docker/
│   └── Dockerfile.opencode       # OpenCode container image
├── charts/
│   └── agentic-sdlc-agent-runner/  # Helm chart
│       ├── Chart.yaml
│       ├── values.yaml
│       ├── README.md
│       └── templates/
│           ├── _helpers.tpl     # Template helpers
│           ├── namespace.yaml
│           ├── serviceaccount.yaml
│           ├── rbac.yaml
│           ├── configmap.yaml   # Non-sensitive config only
│           ├── external-secret.yaml
│           └── pod-template.yaml
├── releases/
│   ├── agentic-sdlc-agent-runner-dev/
│   │   ├── Chart.yaml
│   │   ├── values.yaml
│   │   └── argocd.yaml          # GitOps config
│   ├── agentic-sdlc-agent-runner-stg/
│   │   ├── Chart.yaml
│   │   ├── values.yaml
│   │   └── argocd.yaml
│   └── agentic-sdlc-agent-runner-prod/
│       ├── Chart.yaml
│       ├── values.yaml
│       └── argocd.yaml
└── scripts/
    ├── spawn-pod.sh             # Create task pods via Helm
    ├── tail-logs.sh             # Stream pod logs
    └── README.md                # Scripts documentation

9. Open Questions

  1. Git credentials: How does the pod authenticate to git? (SSH key, token, or workload identity?)
  2. Completion detection: Webhook or log parsing? (Log parsing preferred)
  3. Timeout: How long should a pod run before being killed?
  4. Cleanup: Auto-delete pods after completion?

10. Dependencies

Dependency Version Purpose
Kubernetes 1.25+ Container orchestration
OpenCode latest AI coding agent
Git any Version control
kubectl latest K8s CLI

11. Acceptance Criteria

  • Helm chart deploys successfully to dev/stg/prod environments
  • External Secrets Operator integrates with secret store
  • Pod clones git branch and runs opencode
  • kubectl logs streams logs to stdout
  • Agent can commit and push changes
  • Integration with spec-kit /implement works
  • Multiple [ASYNC] tasks run in parallel
  • Human can see all agent activity from main session
  • ArgoCD GitOps deployment works correctly