From d7229e23002c7e82f9870987238e821f760456bd Mon Sep 17 00:00:00 2001 From: Niranjan Kurambhatti Date: Thu, 13 Nov 2025 18:38:32 -0600 Subject: [PATCH 1/9] chore: unify qa automation --- .cursor/commands/qa/Taskfile.yml | 19 +- .github/workflows/ci.yml.scaffold | 189 ++++++------------ .husky/pre-commit | 19 +- .husky/pre-push | 19 +- .../setup/EnsureDevice/device-check.test.ts | 150 ++++++++++---- 5 files changed, 201 insertions(+), 195 deletions(-) diff --git a/.cursor/commands/qa/Taskfile.yml b/.cursor/commands/qa/Taskfile.yml index 5aacd8d..bd5f394 100644 --- a/.cursor/commands/qa/Taskfile.yml +++ b/.cursor/commands/qa/Taskfile.yml @@ -110,16 +110,27 @@ tasks: - cd ../../../frontend && bun run test:e2e:ui silent: false - # Complete test suite + # Complete QA suite (THE definitive quality check) all: - desc: "Complete test suite (smoke + lint + typecheck + unit + e2e)" - cmds: + desc: "Complete QA suite (fix + rules + smoke + lint + typecheck + unit + e2e)" + cmds: + - echo "๐ŸŽฏ Running comprehensive QA suite..." + - echo "" + - task: fix + - echo "" + - task: rules:check + - echo "" - task: smoke + - echo "" - task: lint + - echo "" - task: typecheck + - echo "" - task: unit + - echo "" - task: e2e - - echo "๐ŸŽ‰ All tests passed!" + - echo "" + - echo "๐ŸŽ‰ All QA checks passed!" silent: false # Appium (mobile testing) diff --git a/.github/workflows/ci.yml.scaffold b/.github/workflows/ci.yml.scaffold index 5a01843..790b26a 100644 --- a/.github/workflows/ci.yml.scaffold +++ b/.github/workflows/ci.yml.scaffold @@ -1,10 +1,10 @@ # GitHub Actions CI Workflow - SCAFFOLD # # Status: NOT YET ACTIVE (rename to ci.yml to activate) -# Purpose: Run automated tests and quality checks on every push/PR +# Purpose: Run comprehensive QA suite on every push/PR # -# This workflow will use the unified Task automation system. -# All checks mirror local development (Husky hooks + Cursor commands) +# This workflow uses the unified `task qa:all` command. +# Mirrors Husky pre-push hook exactly (same commands, same checks). # # To activate: # 1. Rename this file from ci.yml.scaffold to ci.yml @@ -22,155 +22,94 @@ on: jobs: # โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” - # Job 1: Founder Rules Validation + # Comprehensive QA Suite (mirrors pre-push hook) # โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” - # Mirrors: .husky/pre-commit hook -# Command: bun run task founder:rules:check + # Command: cd .cursor && task qa:all + # Includes: fix, rules, smoke, lint, typecheck, unit, e2e - founder-rules: - name: Validate Founder Rules + qa-all: + name: QA Suite (fix + rules + smoke + lint + typecheck + unit + e2e) runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - # TODO: Install Task - # - name: Install go-task - # run: | - # sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin - # task --version + - name: Install go-task + run: | + sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin + task --version - # TODO: Install bun - # - name: Setup bun - # uses: oven-sh/setup-bun@v1 - # with: - # bun-version: latest + - name: Setup bun + uses: oven-sh/setup-bun@v1 + with: + bun-version: latest - # TODO: Install Node.js (for automation scripts) - # - name: Setup Node.js - # uses: actions/setup-node@v4 - # with: - # node-version: '20' + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' - # TODO: Run founder rules check - # - name: Check Founder Rules - # run: bun run task founder:rules:check - - # โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” - # Job 2: Backend Smoke Tests - # โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” - # Mirrors: .husky/pre-push hook (backend part) -# Command: bun run qa:smoke:backend - - backend-smoke: - name: Backend Smoke Tests - runs-on: ubuntu-latest - needs: founder-rules - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - # TODO: Setup dependencies - # - Install Task, bun, Node.js (same as above) - - # TODO: Install backend dependencies - # - name: Install Backend Dependencies - # run: cd backend && bun install - - # TODO: Start backend - # - name: Start Backend - # run: cd .cursor && task backend:dev & - # # Give it time to start - # sleep 5 - - # TODO: Run smoke tests - # - name: Run Backend Smoke Tests - # run: bun run qa:smoke:backend - - # โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” - # Job 3: Frontend Smoke Tests - # โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” - # Mirrors: .husky/pre-push hook (frontend part) -# Command: bun run qa:smoke:frontend - - frontend-smoke: - name: Frontend Smoke Tests - runs-on: ubuntu-latest - needs: founder-rules - - steps: - - name: Checkout code - uses: actions/checkout@v4 + - name: Install Encore CLI + run: | + curl -L https://encore.dev/install.sh | bash + echo "$HOME/.encore/bin" >> $GITHUB_PATH - # TODO: Setup dependencies - # - Install Task, bun, Node.js (same as above) + - name: Install Backend Dependencies + run: cd backend && bun install - # TODO: Install frontend dependencies - # - name: Install Frontend Dependencies - # run: cd frontend && bun install + - name: Install Frontend Dependencies + run: cd frontend && bun install - # TODO: Build frontend - # - name: Build Frontend - # run: cd .cursor && task frontend:build + - name: Start Backend + run: | + cd backend + encore run & + echo "Waiting for backend to be ready..." + timeout 60 bash -c 'until curl -sf http://localhost:4000/health > /dev/null; do sleep 2; done' - # TODO: Start frontend - # - name: Start Frontend - # run: cd .cursor && task frontend:dev & - # sleep 5 - - # TODO: Run smoke tests - # - name: Run Frontend Smoke Tests - # run: bun run qa:smoke:frontend - - # โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” - # Job 4: Type Checking - # โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” - # Additional quality check -# Command: bun run typecheck - - typecheck: - name: TypeScript Type Checking - runs-on: ubuntu-latest - needs: founder-rules - - steps: - - name: Checkout code - uses: actions/checkout@v4 + - name: Start Frontend + run: | + cd frontend + bun run dev & + echo "Waiting for frontend to be ready..." + timeout 60 bash -c 'until curl -sf http://localhost:5173 > /dev/null; do sleep 2; done' - # TODO: Setup and run typecheck - # - name: Run TypeScript Type Checking - # run: bun run typecheck + - name: Run Complete QA Suite + run: cd .cursor && task qa:all # โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” # Implementation Notes: # โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” # -# 1. All Task commands are the SAME as local development -# 2. CI enforces the same rules as Husky hooks (redundant safety) -# 3. Jobs run in parallel where possible (founder-rules first, others parallel) -# 4. Uses ubuntu-latest for consistency -# 5. Each job installs its own dependencies (GitHub Actions best practice) +# SIMPLICITY: Single job runs `task qa:all` - same as pre-push hook +# MIRRORS LOCAL: Exact same command developers run locally +# DRY: No duplication - all logic in .cursor/commands/qa/Taskfile.yml # -# Dependencies needed: -# - go-task (Taskfile runner) -# - bun (package manager) -# - Node.js (for automation scripts) -# - Encore CLI (for backend) +# What `task qa:all` runs: +# 1. qa:fix - Auto-fix linting/formatting +# 2. qa:rules - Validate founder rules (no console.log, no any, American spelling) +# 3. qa:smoke - Health checks (backend + frontend) +# 4. qa:lint - Linting (backend + frontend) +# 5. qa:typecheck - TypeScript validation (frontend) +# 6. qa:unit - Unit tests (backend only - encore test) +# 7. qa:e2e - E2E tests (frontend Playwright) # -# Environment variables: -# - BACKEND_PORT, FRONTEND_PORT (auto-resolved from automation/scripts/env.mjs) +# Dependencies: +# - go-task - Taskfile runner +# - bun - Package manager +# - Node.js - Automation scripts +# - Encore CLI - Backend runtime # -# Security: -# - No secrets needed for CI checks -# - Database: Use in-memory or test DB -# - No external API calls in smoke tests +# Environment: +# - Uses standard ports from .env (4000 backend, 5173 frontend) +# - In-memory database for tests +# - No secrets required # -# To test before activating: +# Testing before activation: # 1. Create feature branch # 2. Rename to ci.yml # 3. Push to trigger workflow -# 4. Verify all jobs pass +# 4. Verify qa:all passes # 5. Merge to main diff --git a/.husky/pre-commit b/.husky/pre-commit index 796f07a..b98778a 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,17 +1,16 @@ #!/bin/sh # Husky pre-commit hook -# Auto-fixes and validates code before allowing commit +# Comprehensive QA suite BEFORE commit (coding agent safety) +# +# WHY REDUNDANT: Coding agents can bypass guards and commit directly. +# Running qa:all here ensures quality checks at EVERY commit. -# Auto-fix linting and formatting -echo "๐Ÿ”ง Auto-fixing code quality issues..." -(cd .cursor && task qa:fix) || exit 1 - -# Run the full QA suite (linting, testing, etc.) -echo "๐Ÿ› ๏ธ Running comprehensive QA checks..." +echo "๐Ÿ› ๏ธ Running comprehensive QA suite before commit..." +echo " (Redundant with pre-push for coding agent safety)" +echo "" (cd .cursor && task qa:all) || exit 1 -# Validate founder rules -echo "๐Ÿ“‹ Validating founder rules..." -(cd .cursor && task qa:rules:check) || exit 1 +echo "" +echo "โœ… Pre-commit checks passed!" diff --git a/.husky/pre-push b/.husky/pre-push index 889824a..800a53b 100644 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -1,19 +1,12 @@ #!/bin/sh # Husky pre-push hook -# 1. Cleanup root documentation (vibe_manager_vibe responsibility) -# 2. Run auto-fix and comprehensive QA suite -# 3. Check for merge commits in new changes +# Comprehensive QA suite + merge commit check +# +# WHY REDUNDANT: Coding agents can bypass pre-commit and push directly. +# Running qa:all here ensures quality checks at EVERY push. -echo "๐Ÿงน Vibe Manager: Cleaning up root documentation..." -echo "" - -echo "" -echo "๐Ÿ”ง Auto-fixing code quality issues before push..." -echo "" -(cd .cursor && task qa:fix) || exit 1 - -echo "" -echo "๐Ÿ› ๏ธ Running comprehensive QA checks before push..." +echo "๐Ÿ› ๏ธ Running comprehensive QA suite before push..." +echo " (Redundant with pre-commit for coding agent safety)" echo "" (cd .cursor && task qa:all) || exit 1 diff --git a/backend/agent/nodes/setup/EnsureDevice/device-check.test.ts b/backend/agent/nodes/setup/EnsureDevice/device-check.test.ts index 3c08a14..63ca672 100644 --- a/backend/agent/nodes/setup/EnsureDevice/device-check.test.ts +++ b/backend/agent/nodes/setup/EnsureDevice/device-check.test.ts @@ -1,89 +1,153 @@ -import { describe, expect, test } from "vitest"; +import { beforeEach, describe, expect, test, vi } from "vitest"; import { checkDevicePrerequisites } from "./device-check"; +// Mock child_process to avoid real adb calls +vi.mock("node:child_process", () => ({ + exec: vi.fn(), +})); + +// Import after mocking to get the mocked version +const childProcess = await import("node:child_process"); +const { promisify } = await import("node:util"); + describe("checkDevicePrerequisites", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); test("should detect online device via adb", async () => { + // Mock successful adb output with one device + vi.mocked(childProcess.exec).mockImplementation((cmd, callback) => { + if (typeof callback === "function") { + callback(null, { + stdout: + "List of devices attached\nemulator-5554 device product:sdk_gphone64_arm64", + stderr: "", + } as never); + } + return {} as never; + }); + const result = await checkDevicePrerequisites({ appId: "com.jetbrains.kotlinconf", }); - // This test depends on having a device/emulator connected - // In CI, we'd mock the exec call - expect(result).toHaveProperty("isOnline"); - - if (result.isOnline) { - expect(result.deviceId).toBeTruthy(); - expect(result.details).toHaveProperty("totalDevices"); - } else { - // No device connected - this is expected in test environment - expect(result.error).toBeTruthy(); - expect(result.error).toContain("device"); - } + expect(result.isOnline).toBe(true); + expect(result.deviceId).toBe("emulator-5554"); + expect(result.details).toHaveProperty("totalDevices"); + expect(result.details?.totalDevices).toBe(1); }); test("should return error when no devices connected", async () => { - // This test would need mocking to simulate "no devices" - // For now, just verify error structure exists + // Mock adb output with no devices + vi.mocked(childProcess.exec).mockImplementation((cmd, callback) => { + if (typeof callback === "function") { + callback(null, { + stdout: "List of devices attached\n", + stderr: "", + } as never); + } + return {} as never; + }); + const result = await checkDevicePrerequisites({ appId: "com.example.test", }); - expect(result).toHaveProperty("isOnline"); - - if (!result.isOnline) { - expect(result.error).toBeTruthy(); - expect(result.error).toContain("device"); - } + expect(result.isOnline).toBe(false); + expect(result.error).toBeTruthy(); + expect(result.error).toContain("No connected devices found"); }); test("should include device details when online", async () => { + // Mock successful adb output with specific device + vi.mocked(childProcess.exec).mockImplementation((cmd, callback) => { + if (typeof callback === "function") { + callback(null, { + stdout: + "List of devices attached\nemulator-5554 device product:sdk_gphone64_arm64", + stderr: "", + } as never); + } + return {} as never; + }); + const result = await checkDevicePrerequisites({ appId: "com.jetbrains.kotlinconf", deviceId: "emulator-5554", }); - expect(result).toHaveProperty("details"); - - if (result.isOnline) { - expect(result.details).toHaveProperty("adbOutput"); - } + expect(result.isOnline).toBe(true); + expect(result.details).toHaveProperty("adbOutput"); + expect(result.details).toHaveProperty("totalDevices"); }); test("should filter for specific deviceId when provided", async () => { - // Test with a likely-available device ID + // Mock adb output with multiple devices + vi.mocked(childProcess.exec).mockImplementation((cmd, callback) => { + if (typeof callback === "function") { + callback(null, { + stdout: + "List of devices attached\nemulator-5554 device product:sdk_gphone64_arm64\nemulator-5556 device product:sdk_gphone64_x86", + stderr: "", + } as never); + } + return {} as never; + }); + const result = await checkDevicePrerequisites({ appId: "com.jetbrains.kotlinconf", deviceId: "emulator-5554", }); - if (result.isOnline) { - // If device found, should match requested ID - expect(result.deviceId).toBe("emulator-5554"); - } else { - // If not found, error should mention requested ID - expect(result.error).toContain("emulator-5554"); - expect(result.error).toContain("not found"); - expect(result.details).toHaveProperty("requestedDeviceId"); - expect(result.details?.requestedDeviceId).toBe("emulator-5554"); - } + expect(result.isOnline).toBe(true); + expect(result.deviceId).toBe("emulator-5554"); + expect(result.details?.totalDevices).toBe(2); }); test("should return error when requested deviceId not found in multi-device lab", async () => { - // Test with a non-existent device ID + // Mock adb output with devices, but not the requested one + vi.mocked(childProcess.exec).mockImplementation((cmd, callback) => { + if (typeof callback === "function") { + callback(null, { + stdout: + "List of devices attached\nemulator-5554 device product:sdk_gphone64_arm64\nemulator-5556 device product:sdk_gphone64_x86", + stderr: "", + } as never); + } + return {} as never; + }); + const result = await checkDevicePrerequisites({ appId: "com.jetbrains.kotlinconf", deviceId: "nonexistent-device-12345", }); - // Should always fail for non-existent device expect(result.isOnline).toBe(false); expect(result.error).toBeTruthy(); expect(result.error).toContain("nonexistent-device-12345"); expect(result.error).toContain("not found"); + expect(result.details).toHaveProperty("requestedDeviceId", "nonexistent-device-12345"); + expect(result.details).toHaveProperty("availableDevices"); + expect(Array.isArray(result.details?.availableDevices)).toBe(true); + expect(result.details?.availableDevices).toHaveLength(2); + }); + + test("should handle adb command failure gracefully", async () => { + // Mock adb command failure (binary not found) + vi.mocked(childProcess.exec).mockImplementation((cmd, callback) => { + if (typeof callback === "function") { + const error = new Error("Command failed: adb devices -l\n/bin/sh: 1: adb: not found"); + callback(error as never, null as never); + } + return {} as never; + }); - // Should list available devices in error - if (result.details?.availableDevices) { - expect(Array.isArray(result.details.availableDevices)).toBe(true); - } + const result = await checkDevicePrerequisites({ + appId: "com.example.test", + }); + + expect(result.isOnline).toBe(false); + expect(result.error).toBeTruthy(); + expect(result.error).toContain("Device check failed"); }); }); From 3acc8d3e7186460151a50a2a1b89534bc6768b4e Mon Sep 17 00:00:00 2001 From: Niranjan Kurambhatti Date: Thu, 13 Nov 2025 18:45:37 -0600 Subject: [PATCH 2/9] ci: activate qa workflow --- .github/workflows/{ci.yml.scaffold => ci.yml} | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) rename .github/workflows/{ci.yml.scaffold => ci.yml} (90%) diff --git a/.github/workflows/ci.yml.scaffold b/.github/workflows/ci.yml similarity index 90% rename from .github/workflows/ci.yml.scaffold rename to .github/workflows/ci.yml index 790b26a..496d99b 100644 --- a/.github/workflows/ci.yml.scaffold +++ b/.github/workflows/ci.yml @@ -1,16 +1,15 @@ -# GitHub Actions CI Workflow - SCAFFOLD -# -# Status: NOT YET ACTIVE (rename to ci.yml to activate) +# GitHub Actions CI Workflow +# +# Status: ACTIVE # Purpose: Run comprehensive QA suite on every push/PR -# +# # This workflow uses the unified `task qa:all` command. # Mirrors Husky pre-push hook exactly (same commands, same checks). # -# To activate: -# 1. Rename this file from ci.yml.scaffold to ci.yml -# 2. Verify all task commands work in CI environment -# 3. Test in a feature branch first -# 4. Merge to main when validated +# Before enabling in other branches: +# 1. Verify `task qa:all` passes locally +# 2. Ensure backend/frontend ports match `.env` +# 3. Confirm Encore CLI auth is configured in repo secrets name: CI @@ -113,3 +112,9 @@ jobs: # 4. Verify qa:all passes # 5. Merge to main +# Validation checklist when modifying: +# 1. Create feature branch +# 2. Push to trigger workflow +# 3. Confirm qa:all passes in GitHub Actions +# 4. Merge to main after review + From e93cb737f71b62bdf65eaa02e357e26ce397b4c3 Mon Sep 17 00:00:00 2001 From: Niranjan Kurambhatti Date: Thu, 13 Nov 2025 18:52:35 -0600 Subject: [PATCH 3/9] ci: add encore auth token for cloud secrets --- .github/workflows/ci.yml | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 496d99b..846fc5c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,11 +5,6 @@ # # This workflow uses the unified `task qa:all` command. # Mirrors Husky pre-push hook exactly (same commands, same checks). -# -# Before enabling in other branches: -# 1. Verify `task qa:all` passes locally -# 2. Ensure backend/frontend ports match `.env` -# 3. Confirm Encore CLI auth is configured in repo secrets name: CI @@ -54,6 +49,18 @@ jobs: curl -L https://encore.dev/install.sh | bash echo "$HOME/.encore/bin" >> $GITHUB_PATH + - name: Authenticate with Encore Cloud + env: + ENCORE_AUTH_TOKEN: ${{ secrets.ENCORE_AUTH_TOKEN }} + run: | + if [ -z "$ENCORE_AUTH_TOKEN" ]; then + echo "โš ๏ธ WARNING: ENCORE_AUTH_TOKEN not set in GitHub Secrets" + echo " Encore builds requiring secrets will fail" + echo " To fix: Add your Encore auth token as a GitHub Secret named 'ENCORE_AUTH_TOKEN'" + else + echo "๐Ÿ” Encore authentication configured" + fi + - name: Install Backend Dependencies run: cd backend && bun install @@ -61,6 +68,8 @@ jobs: run: cd frontend && bun install - name: Start Backend + env: + ENCORE_AUTH_TOKEN: ${{ secrets.ENCORE_AUTH_TOKEN }} run: | cd backend encore run & @@ -103,7 +112,13 @@ jobs: # Environment: # - Uses standard ports from .env (4000 backend, 5173 frontend) # - In-memory database for tests -# - No secrets required +# - ENCORE_AUTH_TOKEN: GitHub Secret required for accessing Encore Cloud secrets +# +# GitHub Secrets Setup: +# 1. Go to: Settings โ†’ Secrets and variables โ†’ Actions +# 2. Create new secret: ENCORE_AUTH_TOKEN +# 3. Get token from: encore auth token (run locally) +# 4. Paste into GitHub Secrets # # Testing before activation: # 1. Create feature branch From b15b805931a1b5892fe6929f07dfc4d978510fdf Mon Sep 17 00:00:00 2001 From: Niranjan Kurambhatti Date: Thu, 13 Nov 2025 18:53:58 -0600 Subject: [PATCH 4/9] ci: install playwright browser binaries for e2e tests --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 846fc5c..6b2f4b4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,6 +67,9 @@ jobs: - name: Install Frontend Dependencies run: cd frontend && bun install + - name: Install Playwright Browser Binaries + run: cd frontend && bunx playwright install --with-deps chromium + - name: Start Backend env: ENCORE_AUTH_TOKEN: ${{ secrets.ENCORE_AUTH_TOKEN }} From d1247d19104a381b3cc3630c19967cf7cea20f28 Mon Sep 17 00:00:00 2001 From: Niranjan Kurambhatti Date: Thu, 13 Nov 2025 18:59:10 -0600 Subject: [PATCH 5/9] ci: fix encore cloud authentication with proper login flow --- .github/workflows/ci.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6b2f4b4..fe748d9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,8 @@ jobs: qa-all: name: QA Suite (fix + rules + smoke + lint + typecheck + unit + e2e) runs-on: ubuntu-latest + env: + ENCORE_AUTH_TOKEN: ${{ secrets.ENCORE_AUTH_TOKEN }} steps: - name: Checkout code @@ -50,15 +52,16 @@ jobs: echo "$HOME/.encore/bin" >> $GITHUB_PATH - name: Authenticate with Encore Cloud - env: - ENCORE_AUTH_TOKEN: ${{ secrets.ENCORE_AUTH_TOKEN }} run: | if [ -z "$ENCORE_AUTH_TOKEN" ]; then echo "โš ๏ธ WARNING: ENCORE_AUTH_TOKEN not set in GitHub Secrets" echo " Encore builds requiring secrets will fail" echo " To fix: Add your Encore auth token as a GitHub Secret named 'ENCORE_AUTH_TOKEN'" + exit 1 else - echo "๐Ÿ” Encore authentication configured" + echo "๐Ÿ” Authenticating with Encore Cloud..." + encore auth login --token "$ENCORE_AUTH_TOKEN" + echo "โœ… Encore authentication successful" fi - name: Install Backend Dependencies @@ -71,8 +74,6 @@ jobs: run: cd frontend && bunx playwright install --with-deps chromium - name: Start Backend - env: - ENCORE_AUTH_TOKEN: ${{ secrets.ENCORE_AUTH_TOKEN }} run: | cd backend encore run & From 564590c15a4f17ca75f916afea50bc7de0283087 Mon Sep 17 00:00:00 2001 From: Niranjan Kurambhatti Date: Thu, 13 Nov 2025 19:09:38 -0600 Subject: [PATCH 6/9] ci: fix encore auth flag from --token to --auth-key --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fe748d9..0a3e2d8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,7 +60,7 @@ jobs: exit 1 else echo "๐Ÿ” Authenticating with Encore Cloud..." - encore auth login --token "$ENCORE_AUTH_TOKEN" + encore auth login --auth-key "$ENCORE_AUTH_TOKEN" echo "โœ… Encore authentication successful" fi From 3ae7e4b2bfc931691ddfcabda0edde2136f6bcfa Mon Sep 17 00:00:00 2001 From: Niranjan Kurambhatti Date: Thu, 13 Nov 2025 19:14:05 -0600 Subject: [PATCH 7/9] ci: fix encore auth - use ENCORE_AUTH_KEY instead of token - Changed from ENCORE_AUTH_TOKEN to ENCORE_AUTH_KEY (app-specific) - Updated docs to clarify auth key vs personal token difference - Auth key must be created in Encore dashboard, not via 'encore auth token' - Fixes 500 error caused by using wrong credential type --- .github/workflows/ci.yml | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0a3e2d8..2b1e2c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: name: QA Suite (fix + rules + smoke + lint + typecheck + unit + e2e) runs-on: ubuntu-latest env: - ENCORE_AUTH_TOKEN: ${{ secrets.ENCORE_AUTH_TOKEN }} + ENCORE_AUTH_KEY: ${{ secrets.ENCORE_AUTH_KEY }} steps: - name: Checkout code @@ -53,14 +53,18 @@ jobs: - name: Authenticate with Encore Cloud run: | - if [ -z "$ENCORE_AUTH_TOKEN" ]; then - echo "โš ๏ธ WARNING: ENCORE_AUTH_TOKEN not set in GitHub Secrets" + if [ -z "$ENCORE_AUTH_KEY" ]; then + echo "โš ๏ธ WARNING: ENCORE_AUTH_KEY not set in GitHub Secrets" echo " Encore builds requiring secrets will fail" - echo " To fix: Add your Encore auth token as a GitHub Secret named 'ENCORE_AUTH_TOKEN'" + echo " To fix:" + echo " 1. Go to https://app.encore.cloud/screengraph-ovzi" + echo " 2. Navigate to: App Settings โ†’ Auth Keys" + echo " 3. Create new auth key" + echo " 4. Add as GitHub Secret named 'ENCORE_AUTH_KEY'" exit 1 else echo "๐Ÿ” Authenticating with Encore Cloud..." - encore auth login --auth-key "$ENCORE_AUTH_TOKEN" + encore auth login --auth-key "$ENCORE_AUTH_KEY" echo "โœ… Encore authentication successful" fi @@ -116,13 +120,14 @@ jobs: # Environment: # - Uses standard ports from .env (4000 backend, 5173 frontend) # - In-memory database for tests -# - ENCORE_AUTH_TOKEN: GitHub Secret required for accessing Encore Cloud secrets +# - ENCORE_AUTH_KEY: GitHub Secret (app-specific auth key) for Encore Cloud authentication # # GitHub Secrets Setup: -# 1. Go to: Settings โ†’ Secrets and variables โ†’ Actions -# 2. Create new secret: ENCORE_AUTH_TOKEN -# 3. Get token from: encore auth token (run locally) -# 4. Paste into GitHub Secrets +# 1. Go to: https://app.encore.cloud/screengraph-ovzi โ†’ App Settings โ†’ Auth Keys +# 2. Create new auth key (NOT `encore auth token` - that's different!) +# 3. Go to: GitHub repo โ†’ Settings โ†’ Secrets and variables โ†’ Actions +# 4. Create new secret: ENCORE_AUTH_KEY +# 5. Paste the auth key from step 2 # # Testing before activation: # 1. Create feature branch From 32febac7965b6c648c575c9c7465df61dcedc458 Mon Sep 17 00:00:00 2001 From: Niranjan Kurambhatti Date: Thu, 13 Nov 2025 19:19:07 -0600 Subject: [PATCH 8/9] fix(frontend): add missing tailwind-variants dependency - badge.svelte imports tailwind-variants but it wasn't in package.json - Passed locally due to persistent node_modules - Failed in CI with fresh install - Fixes typecheck error in CI workflow --- frontend/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/package.json b/frontend/package.json index 1ba20b1..1d7a77e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -26,6 +26,7 @@ "envalid": "^8.1.1", "lucide-svelte": "^0.425.0", "tailwind-merge": "^3.3.1", + "tailwind-variants": "^3.1.1", "tw-animate-css": "^1.4.0" }, "devDependencies": { From 45e386a6ca91685ebe872cbeb5080f46ba6775e1 Mon Sep 17 00:00:00 2001 From: Niranjan Kurambhatti Date: Thu, 13 Nov 2025 19:23:36 -0600 Subject: [PATCH 9/9] fix(qa): remove auto-fix from qa:all to prevent unstaged changes in hooks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Running qa:fix inside qa:all during git hooks causes auto-fixes without staging changes, leading to commits without the fixes. Solution: - qa:all now validation-only (no code modification) - Created qa:all:fix for manual workflow (fix โ†’ validate) - Updated hooks and CI docs to clarify validation-only behavior Rationale: - Git hooks should validate, not modify uncommitted code - CI should validate, not modify code (anti-pattern) - Manual workflow: run qa:all:fix, review changes, stage, commit Files: - .cursor/commands/qa/Taskfile.yml - Split validation from fixing - .github/workflows/ci.yml - Updated docs - .husky/pre-commit - Clarified validation-only - .husky/pre-push - Clarified validation-only --- .cursor/commands/qa/Taskfile.yml | 26 ++++++++++++++++++++------ .github/workflows/ci.yml | 20 ++++++++++++-------- .husky/pre-commit | 11 ++++++++--- .husky/pre-push | 9 +++++++-- bun.lock | 3 +++ 5 files changed, 50 insertions(+), 19 deletions(-) diff --git a/.cursor/commands/qa/Taskfile.yml b/.cursor/commands/qa/Taskfile.yml index bd5f394..e330f32 100644 --- a/.cursor/commands/qa/Taskfile.yml +++ b/.cursor/commands/qa/Taskfile.yml @@ -110,13 +110,12 @@ tasks: - cd ../../../frontend && bun run test:e2e:ui silent: false - # Complete QA suite (THE definitive quality check) + # Validation-only QA suite (for git hooks and CI) + # Does NOT modify code - only validates all: - desc: "Complete QA suite (fix + rules + smoke + lint + typecheck + unit + e2e)" + desc: "Validation QA suite (rules + smoke + lint + typecheck + unit + e2e)" cmds: - - echo "๐ŸŽฏ Running comprehensive QA suite..." - - echo "" - - task: fix + - echo "๐ŸŽฏ Running validation QA suite (no auto-fix)..." - echo "" - task: rules:check - echo "" @@ -130,7 +129,22 @@ tasks: - echo "" - task: e2e - echo "" - - echo "๐ŸŽ‰ All QA checks passed!" + - echo "๐ŸŽ‰ All validation checks passed!" + silent: false + + # Complete QA workflow (fix THEN validate) + # Use this manually before committing + all:fix: + desc: "Complete workflow - auto-fix then validate (manual use only)" + cmds: + - echo "Step 1 - Auto-fixing issues..." + - task: fix + - echo "" + - echo "Auto-fix complete. Review changes with git diff" + - echo "Stage changes with git add . if satisfied" + - echo "" + - echo "Step 2 - Running validation suite..." + - task: all silent: false # Appium (mobile testing) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b1e2c8..0f5a80f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -102,14 +102,18 @@ jobs: # MIRRORS LOCAL: Exact same command developers run locally # DRY: No duplication - all logic in .cursor/commands/qa/Taskfile.yml # -# What `task qa:all` runs: -# 1. qa:fix - Auto-fix linting/formatting -# 2. qa:rules - Validate founder rules (no console.log, no any, American spelling) -# 3. qa:smoke - Health checks (backend + frontend) -# 4. qa:lint - Linting (backend + frontend) -# 5. qa:typecheck - TypeScript validation (frontend) -# 6. qa:unit - Unit tests (backend only - encore test) -# 7. qa:e2e - E2E tests (frontend Playwright) +# What `task qa:all` runs (VALIDATION ONLY - no code modification): +# 1. qa:rules - Validate founder rules (no console.log, no any, American spelling) +# 2. qa:smoke - Health checks (backend + frontend) +# 3. qa:lint - Linting (backend + frontend) +# 4. qa:typecheck - TypeScript validation (frontend) +# 5. qa:unit - Unit tests (backend only - encore test) +# 6. qa:e2e - E2E tests (frontend Playwright) +# +# Note: Auto-fix (qa:fix) is intentionally excluded from qa:all +# - Git hooks should validate, not modify uncommitted code +# - CI should validate, not modify code (anti-pattern) +# - Manual workflow: `task qa:all:fix` (fix โ†’ validate) before committing # # Dependencies: # - go-task - Taskfile runner diff --git a/.husky/pre-commit b/.husky/pre-commit index b98778a..369913f 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,16 +1,21 @@ #!/bin/sh # Husky pre-commit hook -# Comprehensive QA suite BEFORE commit (coding agent safety) +# Validation QA suite BEFORE commit (coding agent safety) # # WHY REDUNDANT: Coding agents can bypass guards and commit directly. # Running qa:all here ensures quality checks at EVERY commit. +# +# IMPORTANT: qa:all is VALIDATION ONLY (does not modify code) +# To auto-fix before committing, run: cd .cursor && task qa:all:fix +# This will fix issues, show you the changes, and let you stage them. -echo "๐Ÿ› ๏ธ Running comprehensive QA suite before commit..." +echo "๐Ÿ› ๏ธ Running validation QA suite before commit..." +echo " (Validation only - does not modify code)" echo " (Redundant with pre-push for coding agent safety)" echo "" (cd .cursor && task qa:all) || exit 1 echo "" -echo "โœ… Pre-commit checks passed!" +echo "โœ… Pre-commit validation passed!" diff --git a/.husky/pre-push b/.husky/pre-push index 800a53b..7490441 100644 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -1,11 +1,16 @@ #!/bin/sh # Husky pre-push hook -# Comprehensive QA suite + merge commit check +# Validation QA suite + merge commit check # # WHY REDUNDANT: Coding agents can bypass pre-commit and push directly. # Running qa:all here ensures quality checks at EVERY push. +# +# IMPORTANT: qa:all is VALIDATION ONLY (does not modify code) +# To auto-fix before pushing, run: cd .cursor && task qa:all:fix +# This will fix issues, show you the changes, and let you stage them. -echo "๐Ÿ› ๏ธ Running comprehensive QA suite before push..." +echo "๐Ÿ› ๏ธ Running validation QA suite before push..." +echo " (Validation only - does not modify code)" echo " (Redundant with pre-commit for coding agent safety)" echo "" (cd .cursor && task qa:all) || exit 1 diff --git a/bun.lock b/bun.lock index 9d57257..dcc9baf 100644 --- a/bun.lock +++ b/bun.lock @@ -48,6 +48,7 @@ "envalid": "^8.1.1", "lucide-svelte": "^0.425.0", "tailwind-merge": "^3.3.1", + "tailwind-variants": "^3.1.1", "tw-animate-css": "^1.4.0", }, "devDependencies": { @@ -1755,6 +1756,8 @@ "tailwind-merge": ["tailwind-merge@3.3.1", "", {}, "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g=="], + "tailwind-variants": ["tailwind-variants@3.1.1", "", { "peerDependencies": { "tailwind-merge": ">=3.0.0", "tailwindcss": "*" }, "optionalPeers": ["tailwind-merge"] }, "sha512-ftLXe3krnqkMHsuBTEmaVUXYovXtPyTK7ckEfDRXS8PBZx0bAUas+A0jYxuKA5b8qg++wvQ3d2MQ7l/xeZxbZQ=="], + "tailwindcss": ["tailwindcss@4.1.17", "", {}, "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q=="], "tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="],