Run #115 on main timed out after 30s on the Gitea runner under
concurrent-job load (writing 10001 rows via ExcelJS addRow + writeFile
is CPU-bound and CI contention pushed it past the previous threshold).
Locally these tests complete in ~1s, so doubling the budget removes
the flake without masking real regressions.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- dispo workbook imports are pinned to DISPO_IMPORT_DIR (default ./imports):
tRPC input rejects absolute paths and .. segments, runtime reader
re-validates containment via path.relative. Closes a path-traversal
class that reached ExcelJS CVEs through admin/compromised tokens.
- image validator now checks the full 8-byte PNG magic, enforces PNG IEND
and JPEG EOI trailers, scans the decoded buffer for markup polyglot
markers (<script, <svg, <iframe, javascript:, onerror=, ...), and
explicitly rejects SVG. Provider-generated covers (DALL-E, Gemini) run
through the same validator before persistence — an untrusted upstream
cannot smuggle a stored-XSS payload past us.
- added image-validation.test.ts and tightened documentation.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The 'rejects worksheets that exceed the row limit' test took 6599ms on
the QNAP act_runner, overflowing the default 5000ms vitest timeout.
Writing and parsing MAX_DISPO_WORKBOOK_ROWS+1 rows via ExcelJS is slow
on constrained hardware. Extend timeout for all three writeWorkbook-
dependent tests (row limit, column limit) to 30s, matching the fix
already applied to excel.test.ts and workbook-export.test.ts.
- Import EstimateStatus enum instead of using "DRAFT" string literal
- Type BASE_VERSION fixture explicitly so lockedAt accepts Date | null
- Add non-null assertion on mock.calls[0] to satisfy strict types
- Reorder id/spread in version fixture to avoid duplicate property warning
Phase 3b continued: covers chargeability-relevance pure functions,
estimate CRUD (create, clone, list with filters), and version lifecycle
(submit, approve, create revision) with NOT_FOUND and status guard tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 3b Tier 2: covers skill gaps, project health, top value resources,
and peak times dashboard queries including empty data edge cases,
filtering logic, and authorization guards.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Invite flow: admin can invite users by email with role selection; accept-invite page
sets password and creates the account; 72-hour token expiry; E2E tests
- User deactivate/reactivate/delete: new tRPC procedures + UI buttons; deactivation
revokes all active sessions immediately; delete cascades vacation/broadcast records;
isActive field added via migration 20260402000000_user_isactive
- Auth: block login for inactive users with audit entry
- Favicon: SVG favicon + ICO/PNG fallbacks (16, 32, 180, 192, 512px); manifest updated
- Dashboard: GridLayout dynamic-import loading skeleton prevents blank dark area
on first login before react-grid-layout chunk is cached
- Admin users: remove max-w-5xl constraint so table uses full page width
- Dev: docker container restart workflow documented in LEARNINGS.md; Prisma generate
must run inside the container after schema changes (named node_modules volume)
Co-Authored-By: claude-flow <ruv@ruv.net>
Redesigned timeline project and resource panels with expanded detail views,
added quick filter toolbar, improved drag handling, and enhanced vacation/entitlement
router logic. Includes e2e test updates and minor API fixes.
Co-Authored-By: claude-flow <ruv@ruv.net>