Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
e748c85
Replace Docker Compose with Kind for E2E testing
oldsj Jan 3, 2026
a110100
Fix Docker build: remove .python-version from COPY
oldsj Jan 3, 2026
d8c7f34
Add comprehensive caching to CI workflow
oldsj Jan 3, 2026
5519679
Fix hardcoded URLs in Playwright tests
oldsj Jan 3, 2026
921f531
Use GitHub Actions matrix for parallel Docker builds
oldsj Jan 3, 2026
8870736
Commit pnpm-lock.yaml for reproducible builds
oldsj Jan 3, 2026
7c07208
Improve CI workflow with security and performance best practices
oldsj Jan 3, 2026
82a1655
Fix trunk-action permissions for check annotations
oldsj Jan 3, 2026
d04b0f8
fmt
oldsj Jan 3, 2026
cc47cec
Fix lint issues from trunk fmt
oldsj Jan 3, 2026
fc6b92e
Fix cookie security vulnerability with pnpm override
oldsj Jan 3, 2026
e9a4907
Fix trunk prettier plugin discovery
oldsj Jan 3, 2026
062eeab
Speed up Kind image loading with parallel loads
oldsj Jan 3, 2026
7268305
Free disk space before Kind setup, use /tmp for artifacts
oldsj Jan 3, 2026
cea84a6
Optimize CI caching and backend Dockerfile
oldsj Jan 3, 2026
2251057
Remove unnecessary cleanup steps from CI
oldsj Jan 3, 2026
3302d1a
Fix E2E test selector and use haiku for tests
oldsj Jan 3, 2026
26c4d02
Fix flaky conversation history test
oldsj Jan 3, 2026
d0e811a
Fix flaky mobile tab navigation tests
oldsj Jan 4, 2026
d7bbce5
Fix E2E test seed API URL for CI
oldsj Jan 4, 2026
24c7a4d
Fix E2E test failures
oldsj Jan 4, 2026
c1e918e
Add fast test environment with mock Claude and hot reload
oldsj Jan 4, 2026
60d9cdf
Fix E2E test reliability and simplify test configuration
oldsj Jan 5, 2026
981e25e
Document feature development workflow with Playwright agents
oldsj Jan 5, 2026
18375f8
Fix markdown and YAML lint issues
oldsj Jan 5, 2026
512b02c
Format Python and TypeScript files
oldsj Jan 5, 2026
31caca7
Fix E2E test API_URL default for CI
oldsj Jan 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 28 additions & 22 deletions .claude/agents/playwright-test-generator.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Your specialty is creating robust, reliable Playwright tests that accurately sim
application behavior.

# For each test you generate

- Obtain the test plan with all the steps and verification specification
- Run the `generator_setup_page` tool to set up page for the scenario
- For each step and verification in the scenario, do the following:
Expand All @@ -29,31 +30,36 @@ application behavior.
<example-generation>
For following plan:

```markdown file=specs/plan.md
### 1. Adding New Todos
**Seed:** `tests/seed.spec.ts`
```markdown file=specs/plan.md
### 1. Adding New Todos

**Seed:** `tests/seed.spec.ts`

#### 1.1 Add Valid Todo

**Steps:**

1. Click in the "What needs to be done?" input field

#### 1.2 Add Multiple Todos

#### 1.1 Add Valid Todo
**Steps:**
1. Click in the "What needs to be done?" input field
...
```

#### 1.2 Add Multiple Todos
...
```
Following file is generated:

Following file is generated:
```ts file=add-valid-todo.spec.ts
// spec: specs/plan.md
// seed: tests/seed.spec.ts

```ts file=add-valid-todo.spec.ts
// spec: specs/plan.md
// seed: tests/seed.spec.ts
test.describe('Adding New Todos', () => {
test('Add Valid Todo', async { page } => {
// 1. Click in the "What needs to be done?" input field
await page.click(...);

test.describe('Adding New Todos', () => {
test('Add Valid Todo', async { page } => {
// 1. Click in the "What needs to be done?" input field
await page.click(...);
...
});
});
```

...
});
});
```
</example-generation>
</example-generation>
4 changes: 3 additions & 1 deletion .claude/agents/playwright-test-healer.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ resolving Playwright test failures. Your mission is to systematically identify,
broken Playwright tests using a methodical approach.

Your workflow:

1. **Initial Execution**: Run all tests using `test_run` tool to identify failing tests
2. **Debug failed tests**: For each failing test run `test_debug`.
3. **Error Investigation**: When the test pauses on errors, use available Playwright MCP tools to:
Expand All @@ -31,6 +32,7 @@ Your workflow:
7. **Iteration**: Repeat the investigation and fixing process until the test passes cleanly

Key principles:

- Be systematic and thorough in your debugging approach
- Document your findings and reasoning for each fix
- Prefer robust, maintainable solutions over quick hacks
Expand All @@ -42,4 +44,4 @@ Key principles:
so that it is skipped during the execution. Add a comment before the failing step explaining what is happening instead
of the expected behavior.
- Do not ask user questions, you are not interactive tool, do the most reasonable thing possible to pass the test.
- Never wait for networkidle or use other discouraged or deprecated apis
- Never wait for networkidle or use other discouraged or deprecated apis
3 changes: 2 additions & 1 deletion .claude/agents/playwright-test-planner.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@ You will:
Submit your test plan using `planner_save_plan` tool.

**Quality Standards**:

- Write steps that are specific enough for any tester to follow
- Include negative testing scenarios
- Ensure scenarios are independent and can be run in any order

**Output Format**: Always save the complete test plan as a markdown file with clear headings, numbered steps, and
professional formatting suitable for sharing with development and QA teams.
professional formatting suitable for sharing with development and QA teams.
214 changes: 214 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read
actions: read

env:
KIND_CLUSTER_NAME: mainloop-test

jobs:
lint:
name: Lint
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
checks: write # Required for trunk-action to post annotations
contents: read
steps:
- uses: actions/checkout@v4
- uses: trunk-io/trunk-action@v1

build:
name: Build ${{ matrix.image }}
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
include:
- image: backend
file: backend/Dockerfile
context: .
tag: mainloop-backend:test
- image: frontend
file: frontend/Dockerfile
context: .
tag: mainloop-frontend:test
build-args: VITE_API_URL=http://localhost:8000
- image: agent
file: claude-agent/Dockerfile
context: ./claude-agent
tag: mainloop-agent-controller:test

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build image
uses: docker/build-push-action@v6
with:
context: ${{ matrix.context }}
file: ${{ matrix.file }}
build-args: ${{ matrix.build-args }}
tags: ${{ matrix.tag }}
outputs: type=docker,dest=/tmp/${{ matrix.image }}.tar
cache-from: type=gha,scope=${{ matrix.image }}
cache-to: type=gha,mode=max,scope=${{ matrix.image }}

- name: Upload image artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.image }}-image
path: /tmp/${{ matrix.image }}.tar
retention-days: 1

e2e:
name: E2E Tests
needs: [lint, build]
runs-on: ubuntu-latest
timeout-minutes: 30

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
cache-dependency-path: pnpm-lock.yaml

- name: Install dependencies
run: pnpm install

- name: Cache Playwright browsers
id: playwright-cache
uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ hashFiles('frontend/package.json') }}
restore-keys: |
playwright-${{ runner.os }}-

- name: Install Playwright browsers
if: steps.playwright-cache.outputs.cache-hit != 'true'
run: pnpm exec playwright install --with-deps chromium
working-directory: frontend

- name: Install Playwright deps (if cached)
if: steps.playwright-cache.outputs.cache-hit == 'true'
run: pnpm exec playwright install-deps chromium
working-directory: frontend

- name: Setup Kind
uses: helm/kind-action@v1.12.0
with:
cluster_name: ${{ env.KIND_CLUSTER_NAME }}
config: ./scripts/kind/cluster-config.yaml

- name: Download all image artifacts
uses: actions/download-artifact@v4
with:
pattern: '*-image'
path: /tmp/images
merge-multiple: true

- name: Load images into Docker and Kind
run: |
# Load into Docker (parallel)
docker load --input /tmp/images/backend.tar &
docker load --input /tmp/images/frontend.tar &
docker load --input /tmp/images/agent.tar &
wait

# Load into Kind (parallel)
kind load docker-image mainloop-backend:test --name ${{ env.KIND_CLUSTER_NAME }} &
kind load docker-image mainloop-frontend:test --name ${{ env.KIND_CLUSTER_NAME }} &
kind load docker-image mainloop-agent-controller:test --name ${{ env.KIND_CLUSTER_NAME }} &
wait

- name: Create secrets
env:
CLAUDE_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
kubectl create namespace mainloop
kubectl create secret generic mainloop-secrets \
--namespace mainloop \
--from-literal=claude-secret-token="${CLAUDE_TOKEN}" \
--from-literal=github-token="${GH_TOKEN}"

- name: Deploy to Kind
run: |
kubectl apply -k k8s/apps/mainloop/overlays/test --server-side
kubectl rollout status deployment/mainloop-backend -n mainloop --timeout=180s
kubectl rollout status deployment/mainloop-frontend -n mainloop --timeout=180s
kubectl rollout status statefulset/postgres -n mainloop --timeout=120s

- name: Wait for services
run: |
# Wait for endpoints to be ready (more reliable than just pod ready)
kubectl wait --for=jsonpath='{.subsets[0].addresses[0].ip}' \
endpoints/mainloop-backend -n mainloop --timeout=120s || true
kubectl wait --for=jsonpath='{.subsets[0].addresses[0].ip}' \
endpoints/mainloop-frontend -n mainloop --timeout=120s || true

# Verify HTTP endpoints are responding
curl -sf --retry 10 --retry-delay 3 --retry-all-errors \
http://localhost:8000/health || {
echo "Backend failed to start"
kubectl logs -n mainloop deployment/mainloop-backend --tail=50
exit 1
}
echo "Backend healthy"

curl -sf --retry 10 --retry-delay 3 --retry-all-errors \
http://localhost:3000 || {
echo "Frontend failed to start"
kubectl logs -n mainloop deployment/mainloop-frontend --tail=50
exit 1
}
echo "Frontend healthy"

- name: Run Playwright tests
run: |
cd frontend
PLAYWRIGHT_BASE_URL=http://localhost:3000 pnpm exec playwright test
env:
CI: true

- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: frontend/playwright-report/
retention-days: 7

- name: Upload failure screenshots
uses: actions/upload-artifact@v4
if: failure()
with:
name: test-screenshots
path: frontend/test-results/
retention-days: 7
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Dependencies
node_modules/
.pnpm-debug.log
pnpm-lock.yaml

# Python
__pycache__/
Expand Down
7 changes: 2 additions & 5 deletions .mcp.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@
"mcpServers": {
"playwright-test": {
"command": "bash",
"args": [
"-c",
"cd frontend && npx playwright run-test-mcp-server"
]
"args": ["-c", "cd frontend && npx playwright run-test-mcp-server"]
}
}
}
}
7 changes: 7 additions & 0 deletions .trunk/trunk.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ runtimes:
- python@3.10.8
# This is the section where you manage your linters. (https://docs.trunk.io/check/configuration)
lint:
definitions:
- name: prettier
runtime: node
package: prettier
extra_packages:
- prettier-plugin-svelte
- prettier-plugin-tailwindcss
ignore:
# K8s security best practices - remaining issues are acceptable for internal Tailscale-only services
# Fixed: container security contexts, health probes, RBAC over-permissions
Expand Down
Loading
Loading