# Bilko Deploy — Standard Operating Procedure

<title id="bkmrk-bilko-deploy-%E2%80%94-stand">Bilko Deploy — Standard Operating Procedure</title></head><body># Bilko Deploy — Standard Operating Procedure

**Last updated:** 2026-04-22  
**Owner:** FlowForge (Kelsey Hightower)  
**Status:** ACTIVE

## Cloud Run Architecture

**GCP Project:** tribal-sign-487920-k0  
**Region:** europe-north1  
**Services:**

- `bilko-web` — Next.js 15 frontend (main branch → bilko-demo.alai.no)
- `bilko-api` — Express API (main branch → bilko-api-762788903040.europe-north1.run.app)
- `bilko-intesa-demo` — Intesa pitch demo (feat/intesa-bih-demo → manual deploy only)

## Deploy Map

<table id="bkmrk-branch-service-url-c"><thead><tr><th>Branch</th><th>Service</th><th>URL</th><th>CI Workflow</th><th>Last Verified</th></tr></thead><tbody><tr><td>main</td><td>bilko-web</td><td>https://bilko-demo.alai.no</td><td>gcp-deploy.yml (BROKEN)</td><td>2026-04-22</td></tr><tr><td>main</td><td>bilko-api</td><td>https://bilko-api-762788903040.europe-north1.run.app</td><td>gcp-deploy.yml (BROKEN)</td><td>2026-04-18</td></tr><tr><td>feat/intesa-bih-demo</td><td>bilko-intesa-demo</td><td>https://bilko-intesa-demo-762788903040.europe-north1.run.app</td><td>Manual gcloud only</td><td>2026-04-17</td></tr></tbody></table>

## Pre-Flight Checks (ZAKON PI2 Check 2)

**OBAVEZNO** — Run these 4 commands and paste output into MC task BEFORE touching code:

```
# 1. Target URL alive?
curl -sI https://bilko-demo.alai.no | head -3

# 2. Branch state?
git log main --oneline -5

# 3. CI health?
gh run list --repo alai-holding/bilko --branch main --limit 3

# 4. Cloud Run service status?
gcloud run services describe bilko-web \
  --region europe-north1 \
  --project tribal-sign-487920-k0 \
  --format='value(status.latestReadyRevisionName,status.url,status.traffic)'

```

**If any returns unexpected:** STOP, escalate to John. Do not proceed.

## CI Pipeline Status

**Status:** BROKEN (2026-04-15 onwards)  
**Root Causes:**

1. GitHub Actions minutes quota exhausted (monthly limit reached)
2. `--no-traffic` flag on line 206 of gcp-deploy.yml prevents traffic promotion for existing services

**Workaround:** Use manual deploy path (see below) until CI fixed.

## Manual Deploy Path (Emergency + CI Broken)

When CI is broken or for emergency fixes, follow this path:

### Step 1: Build Docker Image

```
cd /Users/makinja/ALAI/products/Bilko

docker build \
  --platform linux/amd64 \
  -f apps/web/Dockerfile \
  --build-arg NEXT_PUBLIC_API_URL=https://bilko-api-762788903040.europe-north1.run.app/api/v1 \
  -t europe-north1-docker.pkg.dev/tribal-sign-487920-k0/bilko/web:fix-<purpose>-<DDmon> \
  .

```

**Image tag convention:**

- ✅ `fix-bugs-22apr`, `fix-logo-23apr`
- ❌ `latest` (not traceable)

**Context reduction (.dockerignore):** As of 2026-04-22, .dockerignore reduces build context from 4.1GB → 50MB by excluding `node_modules`, `.next`, `apps/e2e`, `docs`, etc.

### Step 2: Push to Artifact Registry

```
gcloud auth configure-docker europe-north1-docker.pkg.dev

docker push europe-north1-docker.pkg.dev/tribal-sign-487920-k0/bilko/web:fix-<purpose>-<DDmon>

```

### Step 3: Deploy to Cloud Run

**CRITICAL:** Do NOT use `--no-traffic` flag for existing services. It blocks traffic promotion.

```
gcloud run deploy bilko-web \
  --image europe-north1-docker.pkg.dev/tribal-sign-487920-k0/bilko/web:fix-<purpose>-<DDmon> \
  --region europe-north1 \
  --platform managed \
  --allow-unauthenticated \
  --max-instances 10 \
  --min-instances 0 \
  --memory 512Mi \
  --cpu 1 \
  --concurrency 100 \
  --timeout 60s \
  --port 3000 \
  --set-env-vars NEXT_PUBLIC_API_URL=https://bilko-api-762788903040.europe-north1.run.app/api/v1,NEXT_TELEMETRY_DISABLED=1 \
  --project=tribal-sign-487920-k0

```

### Step 4: Verify Deployment

```
# Check revisions
gcloud run revisions list \
  --service bilko-web \
  --region europe-north1 \
  --project=tribal-sign-487920-k0 \
  --limit=5

# Verify traffic routing (should show 100% on latest revision)
gcloud run services describe bilko-web \
  --region europe-north1 \
  --project=tribal-sign-487920-k0 \
  --format='value(status.traffic)'

```

## Post-Deploy Evidence Gate (ZAKON PI2 Check 5)

MC task CANNOT move to `done` without ALL three:

1. **curl checks:** Paste output showing HTTP 200 for expected routes ```
    curl -sI https://bilko-demo.alai.no | head -3
    curl -sI https://bilko-demo.alai.no/invoices/new | head -3
    curl -sI https://bilko-demo.alai.no/settings | head -3
    curl -sI https://bilko-demo.alai.no/intesa-bridge | head -3  # Should be 404
    
    ```
2. **Playwright screenshots:** Stored in `docs/evidence/<task-id>/*.png`
    - Home page
    - Feature verified (e.g., invoice template save button)
    - Any isolation checks (e.g., 404 for client routes on main)
3. **verification.json:** Machine-readable evidence file ```
    {
      "task_id": 8730,
      "timestamp": "2026-04-22T21:41:10Z",
      "revision": "bilko-web-00019-7tl",
      "traffic_100_percent": true,
      "curl_checks": { "home": 200, "intesa-bridge": 404, ... },
      "playwright_pass": true,
      "screenshots": ["home.png", "invoices-new.png", ...]
    }
    
    ```

## Deploy Flow Diagram

```mermaid
flowchart LR
    A[Code Change] --> B{CI Healthy?}
    B -->|Yes| C[CI: Build + Push]
    B -->|No| D[Manual Build]
    C --> E[Artifact Registry]
    D --> E
    E --> F[Cloud Run Deploy]
    F --> G{Traffic Routing}
    G -->|100%| H[Live]
    G -->|0%| I[Blocked - Check --no-traffic flag]
    H --> J[Evidence Gate]
    J --> K{All 3 checks pass?}
    K -->|Yes| L[MC task done]
    K -->|No| M[Block - Add evidence]

```

## Known Issues + Workarounds

### Issue 1: CI broken since 2026-04-15

**Symptom:** All main branch pushes fail at deploy step  
**Root cause:** GitHub Actions quota + `--no-traffic` flag  
**Workaround:** Use manual deploy path above

### Issue 2: Intesa content leaked to public URL (fixed 2026-04-22)

**Symptom:** `/intesa-bridge` route returned 200 on bilko-demo.alai.no  
**Root cause:** Intesa feature branch merged to main  
**Fix:** Deleted intesa routes from main (commit 66d2220) + added branch-purity.yml CI check

### Issue 3: Manual paste-copy anti-pattern

**Symptom:** CEO had to manually paste docker build output and gcloud commands  
**Root cause:** FlowForge task dispatched after image built locally  
**Fix:** Always dispatch FlowForge BEFORE build step, let agent own full flow

## Branch Purity Rules

Client-specific routes MUST NOT appear on main. Reserved prefixes:

- `intesa-*` → feat/intesa-bih-demo → bilko-intesa-demo Cloud Run
- `corpint-*` → TBD client branch → TBD Cloud Run service

**CI Enforcement:** `.github/workflows/branch-purity.yml` runs on every PR to main:

```
find apps/web/app -type d \( -name "intesa-*" -o -name "corpint-*" \) | grep . && exit 1 || exit 0

```

**Registry:** `~/system/rules/client-prefix-registry.md`

## Domain Mapping

- **bilko-demo.alai.no** → Cloud Run service `bilko-web` (configured via GCP Console)
- **DNS:** Cloudflare proxy enabled
- **Mapping verified:** 2026-04-22

## Related Documentation

- **DEPLOY-MAP.md:** `/Users/makinja/ALAI/products/Bilko/DEPLOY-MAP.md`
- **Incident Postmortem:** BookStack → ALAI / Incidents / incident-2026-04-22-bilko-deploy-fix
- **ZAKON PI2:** `~/system/rules/zakon-pi2-deploy-verification.md`
- **CI Workflow:** `.github/workflows/gcp-deploy.yml`
- **Dockerfile:** `apps/web/Dockerfile`

## Escalation

**Owner:** FlowForge  
**Escalate to:** John → pi-orchestrator  
**MC category:** `devops` + `priority: H`

---

<small>Created by ALAI Skillforge, 2026-04-22</small>