- Add instrumentation.js for OTel SDK bootstrap via --require flag
- Add tracing.ts utility functions (getCurrentTraceId, recordError, withSpan)
- Install @opentelemetry packages for auto-instrumentation
- Update Dockerfile to copy instrumentation.js and use --require
- Add trace IDs to error responses in API routes
Traces are exported to Tempo via OTLP/gRPC when running in production
(KUBERNETES_SERVICE_HOST env var present).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
LiteFS needs the actual pod hostname for cluster communication,
but HOSTNAME=0.0.0.0 was being set in both the Dockerfile and
ConfigMap, overriding the pod's hostname.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add LiteFS binary and config to Docker image for SQLite replication
- Convert k8s Deployment to StatefulSet for stable pod identities
- Pod-0 is primary (handles writes), others are replicas
- LiteFS proxy forwards write requests to primary automatically
- Add headless service for pod-to-pod communication
- Increase Node.js heap size to 4GB for Next.js build
- Exclude large Python venvs from Docker context
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add ffmpeg to Docker runtime dependencies for encoding vision frames to MP4
- Fix VisionRecorder to mark videos as 'failed' (not 'ready') when ffmpeg unavailable
- Add lazy encoding: when video requested but MP4 missing, encode frames on-demand
- Returns 202 with retryAfterMs so client knows to poll for completion
- Add link to vision markers page from /create hub
- Various vision recording improvements from plan mode work
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Research confirmed TensorFlow has wheels for Linux x86_64 (including
Synology NAS). The previous failures were due to:
1. Training scripts not being copied to Docker image
2. Venv path not being writable in container
Changes:
- Update isPlatformSupported() to include Linux ARM64 support
- Move venv to data/vision-training/.venv (mounted volume, writable)
- Add python3-venv to Docker image dependencies
- Copy training scripts to Docker image
- Create vision-training directories in Docker build
Sources:
- https://www.tensorflow.org/install/pip
- https://pypi.org/project/tensorflow/ (shows manylinux_2_17_aarch64 wheels)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The Docker build was failing because packages/llm-client/package.json
was not being copied during the dependency installation stages. This
caused zod and other llm-client dependencies to not be installed,
resulting in "Cannot find module 'zod'" errors during the build.
Added the COPY command for llm-client/package.json in both:
- base stage (line 30) - for build dependencies
- deps stage (line 81) - for production dependencies
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
canvas is an optional dependency of jsdom (used by vitest) and requires
native libraries (cairo, pango, pixman) to build on Alpine. Add these
dependencies to the base stage.
Also fix legacy ENV format warnings.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes production error where dynamic imports of @svg-maps/world and @svg-maps/usa
packages fail with "Unexpected token 'export'" error.
**Root cause:**
- The @svg-maps packages use ES module syntax (`export default {...}`) in index.js
- Node.js 18 cannot load these files via dynamic import() without "type": "module"
- Node.js 20 has improved ES module support and handles this correctly
**Changes:**
- Dockerfile: FROM node:18-alpine → FROM node:20-alpine
- deploy.yml: node-version "18" → "20"
**Testing:**
- ✅ Local dev server with Node 20: works (dynamic imports succeed)
- ✅ Production container with Node 18: fails (dynamic imports fail)
- ✅ Tested: `node -e "import('@svg-maps/world').then(...)"` succeeds on Node 20
This fix ensures the Socket.IO server can successfully process START_GAME moves
by loading map data via dynamic imports in the Validator.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed two critical bugs preventing ten-frames from rendering:
1. **Mastery mode not handled** (typstGenerator.ts:61)
- Code only checked for 'smart' | 'manual' modes
- Mastery mode fell into manual path, tried to use boolean flags that don't exist
- Resulted in all display options being `undefined`
- Fix: Check for both 'smart' OR 'mastery' modes (both use displayRules)
2. **Typst array membership syntax** (already fixed in previous commit)
- Used `(i in array)` which doesn't work in Typst
- Changed to `array.contains(i)`
Added comprehensive unit tests (tenFrames.test.ts):
- Problem analysis tests (regrouping detection)
- Display rule evaluation tests
- Full Typst template generation tests
- Mastery mode specific tests
- All 14 tests now passing
Added debug logging to trace display rules resolution:
- displayRules.ts: Shows rule evaluation per problem
- typstGenerator.ts: Shows enriched problems and Typst data
- Helps diagnose future issues
The issue was that mastery mode (which uses displayRules like smart mode)
was being treated as manual mode (which uses boolean flags), resulting in
undefined display options.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixes production error "Cannot read properties of undefined (reading 'carryBoxes')"
that occurred when users tried to adjust difficulty settings.
Root cause: displayRules was undefined for new users or users with old V1 config
in database. Difficulty adjustment buttons accessed displayRules.carryBoxes without
checking if displayRules existed first.
Changes:
- AdditionWorksheetClient: Initialize displayRules with defaults when missing
- ConfigPanel: Use null-coalescing operators instead of non-null assertions
- ConfigPanel: Add error logging when required fields are missing
- NEW: WorksheetErrorBoundary component to catch all errors in worksheet page
- page.tsx: Wrap client component with error boundary
This ensures users see helpful error messages instead of blank pages,
and never need to open the browser console to understand what went wrong.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed 3D abacus creator from main branch due to Docker build failures
with OpenSCAD AppImage extraction in GitHub Actions. The feature will be
reworked in the feature/3d-abacus-creator branch.
Removed:
- src/app/create/abacus/ - 3D abacus creator page
- src/app/api/abacus/ - API routes for 3D generation
- src/components/3d-print/ - 3D-related components
- Dockerfile: OpenSCAD and BOSL2 builder stages
- Dockerfile: OpenGL/Qt runtime dependencies
- Dockerfile: tmp/3d-jobs directory creation
- Create hub: 3D abacus card
This restores successful Docker builds and unblocks deployments.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The OpenSCAD AppImage extraction was failing with exit code 8 in GitHub
Actions because AppImages require FUSE support or special handling to extract
in Docker build environments.
Changes:
- Install libfuse2 package in openscad-builder stage
- Set APPIMAGE_EXTRACT_AND_RUN=1 environment variable
- This allows AppImage extraction without FUSE kernel module mounting
This should fix the Docker build failures that were preventing deployments.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The old OpenSCAD 2021.01 from Debian repos has CGAL bugs that cause
intersection operations to fail with "assertion violation" errors.
This was breaking the 3D abacus creator which uses intersection()
to create the mirrored book-fold design.
Changes:
- Add openscad-builder stage to download and extract OpenSCAD 2024.11.18 AppImage
- Remove old openscad from apt packages
- Add Qt5 and graphics runtime dependencies for new OpenSCAD
- Copy new OpenSCAD binary and libraries to runner stage
- Set LD_LIBRARY_PATH for OpenSCAD libraries
Testing on prod showed "CGAL ERROR: assertion violation" in intersection
operations, producing incomplete 1.8K STL files instead of multi-MB files.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add --format pdf flag to calendar PDF generation route
- Upgrade Typst from v0.11.1 to v0.13.0 in Dockerfile
- Typst v0.11.1 doesn't support --format flag with stdin/stdout
- Fixes "could not infer output format" error in production
The calendar generation scripts were in scripts/ directory which wasn't
included in tsconfig.server.json, so they weren't being compiled during
build. This required tsx in production just to import .tsx files at runtime.
Changes:
- Moved generateCalendarComposite.tsx and generateCalendarAbacus.tsx to src/utils/calendar/
- Removed CLI interface code from these files (they're now pure utility modules)
- Updated imports in API routes to use @/utils/calendar/...
- Moved tsx back to devDependencies where it belongs
- Removed scripts/ copy from Dockerfile (no longer needed)
Now these files are compiled to JavaScript during the build process and
don't require tsx at runtime. This fixes the architecture issue properly.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changes:
- Refactored scripts/generateCalendarAbacus.tsx to export generateAbacusSVG function
- Refactored scripts/generateCalendarComposite.tsx to export generateCalendarComposite function
- Updated API route to import and call functions directly instead of spawning subprocesses
- Removed tsx from production Dockerfile (not needed - Next.js transpiles at build time)
- Kept scripts and abacus-react package copies in Dockerfile (needed for imports)
This eliminates the need for tsx at runtime and improves performance by avoiding subprocess overhead.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Copy scripts directory to production container (needed for generateCalendarComposite.tsx)
- Copy abacus-react package (scripts import from @soroban/abacus-react/static)
- Install tsx globally to fix npm EACCES /nonexistent error in production
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
CRITICAL REGRESSION: Production container crash-looping with:
"Error: libc.musl-x86_64.so.1: cannot open shared object file"
Root cause identified:
Commit dafdfdd2 (3D printing support) changed runner stage from:
FROM node:18-alpine → FROM node:18-slim
to add OpenSCAD (not available in Alpine repos).
However, the deps stage was NOT updated and remained Alpine-based,
causing a binary incompatibility:
- deps stage (Alpine/musl) compiles better-sqlite3 for musl libc
- runner stage (Debian/glibc) cannot run musl-compiled binaries
Solution:
Changed deps stage from node:18-alpine to node:18-slim to match
runner, ensuring better-sqlite3 is compiled for the correct target.
Both stages must now use Debian because OpenSCAD is required for
3D printing functionality and is not available in Alpine.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The BOSL2 repository has no release tags, causing Docker build to fail
when trying to clone --branch v2.0.0. Removed the branch flag to clone
the default branch instead.
Error:
fatal: Remote branch v2.0.0 not found in upstream origin
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Reverted selective copying of package subdirectories because:
- packages/core has no "server" directory (it's "src")
- packages/templates has .typ files in root, not subdirectory
Still maintains key optimization: production-only node_modules via deps stage.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed Docker build failure caused by copying non-existent /typst subdirectory.
The .typ template files are in the root of packages/templates/, not in a subdirectory.
Changed from:
COPY /app/packages/templates/typst (does not exist)
To:
COPY /app/packages/templates/*.typ (actual files)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The styled-system directory containing generated Panda CSS was being
created during build but not copied to the production image, causing
all Panda CSS classes to be undefined at runtime.
This fix copies the generated styled-system directory from the builder
stage to the production image, ensuring styles.css is available.
Fixes missing CSS definitions for classes like fs_xl, font_bold, text_blue.400
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The Python flashcard generator requires qpdf for PDF processing.
Without it, the script exits with code 1 even though it prints
a warning saying it will skip linearization.
Added qpdf to Alpine packages to fix PDF generation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Typst was failing with "input file not found" error because the templates
directory containing flashcards-input.typ was missing from the Docker image.
Added COPY command to include packages/templates in the production image.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit fixes two critical production issues:
1. **Flashcard generator dependencies** - Added all required dependencies to Dockerfile:
- Typst for PDF generation
- Python pip and setuptools
- Python packages (pyyaml, Pillow, imagehash)
- packages/core directory with generate.py script
2. **Deployment info modal** - Fixed git commit hash display on production:
- Modified generate-build-info.js to accept env vars as fallback when .git is unavailable
- Updated Dockerfile to accept GIT_* build arguments
- Updated GitHub Actions workflow to pass git information during Docker build
The deployment info modal (Ctrl+Shift+I) will now show the correct commit hash,
branch, and build time on production, matching the behavior on dev.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Python 3.11+ prevents global pip installs by default. Since this is a
controlled Docker environment, use --break-system-packages to allow installing
the flashcard generation dependencies.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The pip3 command was not available, causing the Docker build to fail when
trying to install Python dependencies from requirements.txt. Added py3-pip
to the Alpine package installation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The Python scripts in packages/core require pyyaml, pillow, imagehash, and
other dependencies to generate flashcards. Install these from requirements.txt
during Docker build.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The flashcard generator was failing in production because the packages/core
directory (which contains the Python scripts for PDF generation) wasn't being
copied to the production Docker image. The SorobanGenerator class needs these
scripts at runtime to generate flashcards.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The flashcard generator requires both Python and Typst to work.
Added typst to the runtime dependencies in the Dockerfile.
This fixes the issue where flashcards work in dev but fail in production.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Problem:**
- Dockerfile was trying to copy socket-server.js from root (no longer exists)
- Also copying entire src/ directory instead of compiled dist/
**Solution:**
- Changed to copy dist/ directory which contains all compiled server code
- Removed separate socket-server.js copy (now in dist/)
- Removed src/ copy (runtime only needs compiled code)
**Files changed:**
- Line 50-51: Replaced socket-server.js and src/ copies with single dist/ copy
**Tested:**
- Docker build completes successfully ✓
- Container starts and Socket.IO initializes ✓
- Server responds on port 3000 ✓
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Remove standalone output references, copy .next directly
- Add compiled server files (server.js, socket-server.js, src/)
- Include drizzle migrations folder for database setup
- Create data directory for SQLite database
- Keep Python/g++/make in runtime for better-sqlite3
- Set correct working directory to /app/apps/web
- Add NODE_ENV=production environment variable
This enables proper production deployment with database migrations
running on container startup using pure Node.js.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Next.js expects static files at /.next/static and public at /public
when running from /app, not at /apps/web/.next/static.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The Next.js standalone build creates symlinks in node_modules that point
to ../../../node_modules/.pnpm. When the working directory is /app, these
resolve to /node_modules/.pnpm. Fixed by copying node_modules to /node_modules
instead of /app/node_modules.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The Next.js standalone build outputs server.js to /app/server.js, not
/app/apps/web/server.js. This was causing the container to crash on
startup with MODULE_NOT_FOUND errors, resulting in 404s for abaci.one.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
.npmrc no longer exists after reverting to default pnpm mode.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The pnpm lockfile was generated with hoisted mode, so Docker must
also use hoisted mode to match the module resolution paths.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Docker builds should use default pnpm isolated mode, not hoisted mode
which causes tsup module resolution failures.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Upgrade Dockerfile from pnpm 8.0.0 to 9.15.4 for lockfile compatibility
- Add "types": [] to abacus-react tsconfig to prevent implicit @types includes
- Fixes Docker build lockfile incompatibility
- Fixes TypeScript error looking for @types/minimatch
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add python3, py3-setuptools, make, g++ to Alpine base image
- Required for better-sqlite3 native module compilation in Docker build
- Fixes 'ModuleNotFoundError: No module named distutils' error
- Restore @soroban/core and @soroban/client dependencies in apps/web
- Correct workspace configuration to include all existing packages
- Disable problematic example-server.ts to fix TypeScript build
- Update Dockerfile to copy all required package.json files
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Fix pnpm-workspace.yaml to use packages/* pattern
- Remove references to non-existent @soroban/core and @soroban/client packages
- Update Dockerfile to only copy existing package.json files
- Clean up dependencies after workspace changes
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>