The QR SVG endpoint had zero test coverage for success paths — only
the 404 (tunnel not running) case was tested. This adds tests for
auth/no-auth SVG generation, the 500 error when token rotation isn't
started, SVG caching consistency, and cache invalidation on regeneration.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace hardcoded per-IP rate limit (10) and cookie maxAge (86400) in
system-routes.ts with QR_AUTH_FAILURE_MAX and AUTH_SESSION_TTL_MS/1000
so both auth paths stay in sync if constants change.
Add 16 new tests: grace period boundary precision, base62 charset
validation, current+previous token during grace, stopTokenRotation
state cleanup, rate limit reset, consumed token eviction, full
end-to-end QR flow, per-IP 429, cookie attributes, concurrent race,
regenerate invalidation, URL encoding, path traversal, /q without
param, and session record method:qr.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add path traversal protection to GET /api/cases/:name and fix-plan
- Use safePathSchema for LinkCaseSchema.path
- Fix QR auth test timer leak (afterAll → afterEach) and env var try/finally
- Remove dead terminal size check after Zod validation in resize route
- Remove no-op sampleCount guard in adaptive timing
- Replace hardcoded values with constants in notification-manager and subagent-windows
- Add Zod validation to POST /api/auth/revoke
- Use _apiPut instead of raw fetch in subagent-windows
- Add SwipeHandler.cleanup() for consistency with other mobile handlers
- Move NiceConfig/ProcessStats from types/plan.ts to types/common.ts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove tests for createSuccessResponse and ErrorMessages which were
removed/made private during the type system refactoring (15 failures)
- Fix RalphConfigSchema to accept 'full' string for reset field,
matching the route handler's fullReset() code path
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix Prettier formatting in ralph-tracker.ts and respawn-controller.ts
(whitespace drift from Phase 2/4 refactoring)
- Add missing `await` to writeHooksConfig() calls in hooks-config.test.ts
(async function was called without await, causing ENOENT race condition)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New test files for utilities that previously had no dedicated coverage,
plus migration tests validating the ralph-tracker and respawn-controller
timer refactorings work correctly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Overlay renderer (xterm-zerolag-input):
- Add charTop/charHeight to CellDimensions and RenderParams for precise
vertical text positioning matching xterm's canvas renderer
- Convert device.char dimensions to CSS pixels via devicePixelRatio
- Extend line div background 1px past cell boundary to cover compositing
seam between overlay layer (z-index:7) and canvas layer below
- Remove -webkit-font-smoothing/text-rendering overrides that made overlay
text thinner than canvas text
- Add per-span height/lineHeight for natural CSS vertical centering
- Add setPrompt() method for runtime prompt strategy switching (fixes tab
switching crash with "setPrompt is not a function")
app.js duplicate class members:
- Remove dead formatTokens duplicate (line ~5590 shadowed precise version)
- Remove fire-and-forget resetCircuitBreaker duplicate (shadowed notification version)
- Rename mux-panel killAllSessions to killAllMuxSessions (was shadowing
Codeman session killer, breaking Ctrl+K)
Other:
- Update index.html onclick to use killAllMuxSessions
- Add getTeamTasks mock to test route context
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Document the ephemeral single-use QR token system — how it works,
security design informed by USENIX Security 2025 research (6 flaws
addressed), timing-safe lookup, dual-layer rate limiting, QR version
optimization, desktop experience, threat coverage, and comparison
with Discord/WhatsApp/Signal QR auth models.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 2: respawn-controller.ts and ralph-tracker.ts were never migrated
to CleanupManager/Debouncer despite being marked complete. Corrected
status to reflect actual codebase state (6 of 8 files migrated).
Phase 7: All 12 route test files now exist, updated from "9 remaining".
Scorecard adjusted: Resource Cleanup 9→8/10, Test Coverage 7→7.5/10.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
navigator.mediaDevices is undefined in insecure contexts, causing
"undefined is not an object" error. Now shows actionable message.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update scorecard with before/after scores, mark phases 3-7 as
complete with verified line counts and file inventories. Fix 3
CLAUDE.md inaccuracies: static cache 1h→1y, route count ~160→~113,
remove stale src/tui reference.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Consolidate duplicated MockSession/MockStateStore into test/mocks/,
migrate respawn tests to shared mocks, and add 58 route tests for
session, system, and respawn endpoints using Fastify app.inject().
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Route modules (13→12), type domain files (14→13), SSE events (~80→~100),
total route handlers (~110→~160), and all per-group API route counts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds ephemeral single-use QR tokens for passwordless tunnel login.
Scanning the QR auto-authenticates; bare tunnel URL requires Basic Auth.
Backend:
- TunnelManager: 60s token rotation, 90s grace, rejection-sampled 6-char
base62 short codes, Map-based O(1) lookup, SVG caching, global rate limit
- Auth middleware: /q/ bypass, separate qrAuthFailures counter, enhanced
AuthSessionRecord with device context (ip, ua, createdAt, method)
- Routes: GET /q/:code (consume + cookie + redirect), POST /api/tunnel/qr/
regenerate, POST /api/auth/revoke, updated GET /api/tunnel/qr with cache
- SSE: tunnel:qrRotated, tunnel:qrRegenerated, tunnel:qrAuthUsed events
- Audit: qr_auth lifecycle log entries
Frontend:
- Auto-refresh QR via inline SVG in SSE (fallback fetch if absent)
- 60s countdown indicator on QR badge
- Regenerate QR button
- QRLjacking detection toast with [Revoke All] action button (10s duration)
- showToast enhanced with optional duration and action button support
Fixes:
- /api/logout now invalidates server-side session token (was only clearing
browser cookie, leaving token valid for replay)
Tests: 20 new tests in test/qr-auth.test.ts covering token lifecycle,
bias check, rate limiting, SVG caching, and full server integration.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Split 1,443-line types.ts into 14 focused domain files under src/types/:
common.ts, session.ts, task.ts, app-state.ts, respawn.ts, ralph.ts,
api.ts, lifecycle.ts, run-summary.ts, tools.ts, teams.ts, push.ts,
plan.ts, and index.ts barrel.
Moved PlanItem interface from plan-orchestrator.ts into types/plan.ts
to break circular dependency. Original types.ts replaced with barrel
re-export — zero changes to 36 import sites.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Split 6,710-line server.ts into focused route modules using port
interfaces for dependency injection. 107/109 routes extracted into
12 domain files with auth middleware, 5 port interfaces, and shared
helpers. Server.ts retains orchestration (SSE, lifecycle, state).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
npm package stays as aicodeman (name taken), but GitHub releases
now show as codeman@x.y.z via a post-publish retag step.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
setup-node registry-url creates .npmrc with auth token reference,
and NODE_AUTH_TOKEN maps the NPM_TOKEN secret for changesets publish.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@manypkg/get-packages only includes workspace members in its packages list,
not the workspace root. Adding "." to workspaces makes the root package
discoverable, fixing "not in the workspace" errors from changeset version.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Root cause: toggleTunnelFromWelcome() sent the entire settings object
back to PUT /api/settings, but the Zod schema uses .strict() which
rejects unknown fields (lastUsedCase, localEchoEnabled, etc.). The PUT
silently failed, so the tunnel never started.
Fix: send only {tunnelEnabled: true/false} instead of the full blob.
Also added polling fallback for tunnel status and server-side re-broadcast
when tunnel is already running.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Re-broadcast tunnel:started SSE event when tunnel is already active
and user toggles the setting, so the client receives the URL.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Both are local state files. plan.json was already gitignored,
skills-lock.json now added to .gitignore.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>