feat: Claude Code Monitor — lanes, pipelines and a merged workspace

Internal SmartGift build of a Claude Code monitoring dashboard.

Lanes: a durable unit of parallel agent work, one per working directory,
tracked across session restarts. Managed lanes are git worktrees the
dashboard provisions and can reset or remove behind a three-check destroy
guard and a counted preflight; adopted lanes are directories you already
own and are never destroyable.

Pipelines: a lane moves through pipeline stages. A stage the agent declares
with evidence renders green; a stage inferred from the tool-event stream
renders dashed amber and never counts as done. Detection is forward-only
within a 30-minute window, and never writes the declared stage.

Workspace: one page at /run with a lane grid, the selected lane's pipeline,
and a full Claude console behind a disclosure.
This commit is contained in:
2026-07-29 17:07:45 +07:00
commit 57dc91585d
783 changed files with 221743 additions and 0 deletions
+335
View File
@@ -0,0 +1,335 @@
# ─────────────────────────────────────────────────────────────────────────────
# GitHub Actions Deploy Pipeline Claude Code Agent Monitor
#
# Triggers on version tags and manual dispatch. Deploys to staging
# automatically and to production after manual approval.
# ─────────────────────────────────────────────────────────────────────────────
name: Deploy
on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
environment:
description: "Target environment"
required: true
type: choice
options:
- staging
- production
image_tag:
description: "Image tag to deploy (default: latest from main)"
required: false
type: string
permissions:
contents: read
packages: read
id-token: write # For OIDC cloud auth
concurrency:
group: deploy-${{ github.event.inputs.environment || 'staging' }}
cancel-in-progress: false
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}/agent-monitor
MCP_IMAGE_NAME: ${{ github.repository }}/agent-monitor-mcp
HELM_CHART_PATH: deployments/helm/agent-monitor
jobs:
# ── Resolve image tag ──────────────────────────────────────────────────
prepare:
name: Prepare Deployment
runs-on: ubuntu-latest
outputs:
image-tag: ${{ steps.resolve.outputs.tag }}
version: ${{ steps.resolve.outputs.version }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Resolve image tag
id: resolve
run: |
if [[ -n "${{ github.event.inputs.image_tag }}" ]]; then
TAG="${{ github.event.inputs.image_tag }}"
elif [[ "${{ github.ref_type }}" == "tag" ]]; then
TAG="${{ github.ref_name }}"
else
TAG="sha-$(git rev-parse --short HEAD)"
fi
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
echo "version=${TAG#v}" >> "$GITHUB_OUTPUT"
echo "Resolved image tag: ${TAG}"
# ── Build (if triggered by tag) ────────────────────────────────────────
build:
name: Build Images
runs-on: ubuntu-latest
needs: prepare
if: github.ref_type == 'tag'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build & push app image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.prepare.outputs.image-tag }}
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64,linux/arm64
- name: Build & push MCP image
uses: docker/build-push-action@v5
with:
context: .
file: ./mcp/Dockerfile
push: true
tags: |
${{ env.REGISTRY }}/${{ env.MCP_IMAGE_NAME }}:${{ needs.prepare.outputs.image-tag }}
${{ env.REGISTRY }}/${{ env.MCP_IMAGE_NAME }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64,linux/arm64
# ── Deploy to Staging ──────────────────────────────────────────────────
deploy-staging:
name: Deploy to Staging
runs-on: ubuntu-latest
needs: [prepare, build]
if: |
always() &&
needs.prepare.result == 'success' &&
(needs.build.result == 'success' || needs.build.result == 'skipped') &&
(github.event.inputs.environment == 'staging' || github.event.inputs.environment == '' || github.ref_type == 'tag')
environment:
name: staging
url: https://staging.agent-monitor.example.com
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Configure AWS credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN_STAGING }}
aws-region: ${{ vars.AWS_REGION || 'us-west-2' }}
- name: Setup kubectl
uses: azure/setup-kubectl@v3
with:
version: "v1.29.0"
- name: Setup Helm
uses: azure/setup-helm@v4
with:
version: "v3.14.0"
- name: Update kubeconfig
run: |
aws eks update-kubeconfig \
--region ${{ vars.AWS_REGION || 'us-west-2' }} \
--name ${{ vars.EKS_CLUSTER_STAGING || 'agent-monitor-staging' }}
- name: Deploy to staging via Helm
run: |
helm upgrade --install agent-monitor ${{ env.HELM_CHART_PATH }} \
--namespace agent-monitor-staging \
--create-namespace \
--set image.repository=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} \
--set image.tag=${{ needs.prepare.outputs.image-tag }} \
--set mcp.image.repository=${{ env.REGISTRY }}/${{ env.MCP_IMAGE_NAME }} \
--set mcp.image.tag=${{ needs.prepare.outputs.image-tag }} \
--set environment=staging \
--set ingress.host=staging.agent-monitor.example.com \
--values ${{ env.HELM_CHART_PATH }}/values-staging.yaml \
--wait \
--atomic \
--timeout 600s
- name: Health check
run: |
echo "Waiting for pods to be ready..."
kubectl wait --for=condition=ready pod \
-l app.kubernetes.io/name=agent-monitor \
-n agent-monitor-staging \
--timeout=300s
# Port forward and check health
kubectl port-forward svc/agent-monitor 14820:4820 -n agent-monitor-staging &
PF_PID=$!
sleep 5
for i in $(seq 1 10); do
if curl -sf http://localhost:14820/api/health | grep -q '"status":"ok"'; then
echo "✔ Health check passed"
kill $PF_PID 2>/dev/null || true
exit 0
fi
echo "Attempt $i/10..."
sleep 5
done
kill $PF_PID 2>/dev/null || true
echo "✖ Health check failed"
exit 1
- name: Notify Slack (staging)
if: always()
uses: slackapi/slack-github-action@v1.26.0
with:
payload: |
{
"text": "${{ job.status == 'success' && '✅' || '❌' }} Staging deployment ${{ job.status }}: `${{ needs.prepare.outputs.image-tag }}`",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "${{ job.status == 'success' && ':white_check_mark:' || ':x:' }} *Staging Deployment ${{ job.status }}*\n*Image:* `${{ needs.prepare.outputs.image-tag }}`\n*Commit:* `${{ github.sha }}`\n*Actor:* ${{ github.actor }}"
}
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
# ── Deploy to Production ───────────────────────────────────────────────
deploy-production:
name: Deploy to Production
runs-on: ubuntu-latest
needs: [prepare, deploy-staging]
if: |
always() &&
needs.prepare.result == 'success' &&
needs.deploy-staging.result == 'success' &&
(github.event.inputs.environment == 'production' || github.ref_type == 'tag')
environment:
name: production
url: https://agent-monitor.example.com
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Configure AWS credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN_PRODUCTION }}
aws-region: ${{ vars.AWS_REGION || 'us-west-2' }}
- name: Setup kubectl
uses: azure/setup-kubectl@v3
with:
version: "v1.29.0"
- name: Setup Helm
uses: azure/setup-helm@v4
with:
version: "v3.14.0"
- name: Update kubeconfig
run: |
aws eks update-kubeconfig \
--region ${{ vars.AWS_REGION || 'us-west-2' }} \
--name ${{ vars.EKS_CLUSTER_PRODUCTION || 'agent-monitor-production' }}
- name: Create database backup
run: |
chmod +x deployments/scripts/db-backup.sh
# Find a running pod to backup from
POD=$(kubectl get pods -n agent-monitor-production \
-l app.kubernetes.io/name=agent-monitor \
--field-selector=status.phase=Running \
-o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "")
if [[ -n "$POD" ]]; then
echo "Backing up database from pod: $POD"
kubectl exec "$POD" -n agent-monitor-production -- \
sh -c "cp /app/data/dashboard.db /tmp/pre-deploy-backup.db 2>/dev/null || true"
echo "Pre-deploy backup created"
else
echo "⚠ No running pods found skipping backup"
fi
- name: Deploy to production via Helm
run: |
helm upgrade --install agent-monitor ${{ env.HELM_CHART_PATH }} \
--namespace agent-monitor-production \
--create-namespace \
--set image.repository=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} \
--set image.tag=${{ needs.prepare.outputs.image-tag }} \
--set mcp.image.repository=${{ env.REGISTRY }}/${{ env.MCP_IMAGE_NAME }} \
--set mcp.image.tag=${{ needs.prepare.outputs.image-tag }} \
--set environment=production \
--set ingress.host=agent-monitor.example.com \
--values ${{ env.HELM_CHART_PATH }}/values-production.yaml \
--wait \
--atomic \
--timeout 600s
- name: Health check
run: |
echo "Waiting for pods to be ready..."
kubectl wait --for=condition=ready pod \
-l app.kubernetes.io/name=agent-monitor \
-n agent-monitor-production \
--timeout=300s
kubectl port-forward svc/agent-monitor 14820:4820 -n agent-monitor-production &
PF_PID=$!
sleep 5
for i in $(seq 1 15); do
if curl -sf http://localhost:14820/api/health | grep -q '"status":"ok"'; then
echo "✔ Production health check passed"
kill $PF_PID 2>/dev/null || true
exit 0
fi
echo "Attempt $i/15..."
sleep 5
done
kill $PF_PID 2>/dev/null || true
echo "✖ Production health check failed!"
exit 1
- name: Notify Slack (production)
if: always()
uses: slackapi/slack-github-action@v1.26.0
with:
payload: |
{
"text": "${{ job.status == 'success' && '🚀' || '🚨' }} Production deployment ${{ job.status }}: `${{ needs.prepare.outputs.image-tag }}`",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "${{ job.status == 'success' && ':rocket:' || ':rotating_light:' }} *Production Deployment ${{ job.status }}*\n*Image:* `${{ needs.prepare.outputs.image-tag }}`\n*Version:* `${{ needs.prepare.outputs.version }}`\n*Commit:* `${{ github.sha }}`\n*Actor:* ${{ github.actor }}\n*Workflow:* <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Run>"
}
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK