Skip to content

v4.0.0#610

Open
jthrilly wants to merge 2819 commits into
mainfrom
next
Open

v4.0.0#610
jthrilly wants to merge 2819 commits into
mainfrom
next

Conversation

@jthrilly

@jthrilly jthrilly commented Feb 9, 2026

Copy link
Copy Markdown
Member

Significant release!

  • New form system
  • Schema 8
    • Geospatial interface
    • Anonymisation interface
    • Multi-select dyad census
    • Family tree census
  • New drag and drop system
  • New dashboard UI
  • Multi-user capability
  • Preview mode
  • React 19 and Next 16

Waiting on:

  • New form system #453
  • Implementation of same-sex relationships in family tree census
  • Upgrade to posthog based analytics
  • React 19 and Next 16 upgrade
  • Bugfixes for dashboard
  • Bugfixes for ALL interview features
  • e2e test coverage of SILOS
  • Cross browser playwright and storybook coverage
  • Two factor auth

Copilot AI review requested due to automatic review settings February 9, 2026 11:09
@vercel

vercel Bot commented Feb 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
fresco-sandbox Ready Ready Preview, Comment Mar 2, 2026 8:19am
fresco-silos Ready Ready Preview, Comment Mar 2, 2026 8:19am
fresco-storybook Ready Ready Preview, Comment Mar 2, 2026 8:19am

Request Review

@netlify

netlify Bot commented Feb 9, 2026

Copy link
Copy Markdown

Deploy Preview for fresco-sandbox ready!

Name Link
🔨 Latest commit c6f5899
🔍 Latest deploy log https://app.netlify.com/projects/fresco-sandbox/deploys/698a35907e33ebf10843bf3f
😎 Deploy Preview https://deploy-preview-610--fresco-sandbox.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Major v4.0.0 release update introducing a new form system, schema v8 support, preview mode, multi-user capability, and a revamped drag-and-drop/dashboard UI, alongside significant refactors and infrastructure/config updates.

Changes:

  • Added preview mode infrastructure (API routes, DB fields, auth toggle, preview interview flow).
  • Introduced new form field system (TanStack Form contexts/hooks + many new field components).
  • Reworked drag-and-drop (new lib/dnd store/hooks/utils, plus refactors in interviewer components).

Reviewed changes

Copilot reviewed 293 out of 804 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
lib/interviewer/components/Panels.js Formatting simplification for panel wrapper
lib/interviewer/components/Panel.tsx New TSX panel component with collapse/minimize/highlight logic
lib/interviewer/components/Panel.js Removed legacy Panel implementation
lib/interviewer/components/NodeBin.tsx New NodeBin using new ~/lib/dnd drop target hook
lib/interviewer/components/NodeBin.js Removed legacy NodeBin drag/drop HOC implementation
lib/interviewer/components/Node.tsx New memoized Node wrapper + MotionNode
lib/interviewer/components/Node.js Removed legacy Node implementation
lib/interviewer/components/Navigation.tsx Navigation refactor (props-driven disable state + passphrase prompt)
lib/interviewer/components/MultiNodeBucket.js Removed legacy bucket component
lib/interviewer/components/Edge.js Updated edge color selector wiring
lib/interviewer/components/DialogManager.js Redux action import refactor
lib/interviewer/components/CollapsablePrompts.js Added support for rendering children in prompts container
lib/interviewer/components/CategoricalItem.js Removed legacy categorical item implementation
lib/interviewer/components/Canvas/NodeLayout.js Updated updateNode API usage
lib/interviewer/components/Canvas/NarrativeEdgeLayout.js Minor formatting cleanup
lib/interviewer/components/Canvas/LayoutNode.js Layout node rendering tweaks (size + import ordering)
lib/interviewer/components/Canvas/EdgeLayout.js Selector refactor for codebook lookup
lib/interviewer/components/Canvas/ConvexHulls.js Selector import consolidation
lib/interviewer/components/Canvas/Canvas.js Formatting cleanup
lib/interviewer/components/BackgroundImage.js Formatting cleanup
lib/interviewer/behaviours/scrollable.js Removed legacy scrollable HOC
lib/interviewer/behaviours/DragAndDrop/useDropMonitor.js Removed legacy drop monitor hook
lib/interviewer/behaviours/DragAndDrop/store.js Store middleware updated for unserializable drag/drop values
lib/interviewer/behaviours/DragAndDrop/reducer.js Action type constants changed from Symbols to strings
lib/interviewer/behaviours/DragAndDrop/index.js Export surface adjusted
lib/interviewer/behaviours/DragAndDrop/MonitorDropTarget.js Removed legacy monitor HOC
lib/interviewer/behaviours/DragAndDrop/MonitorDragSource.js Removed legacy monitor HOC
lib/interviewer/behaviours/DragAndDrop/Monitor.js Removed legacy monitor base
lib/interviewer/behaviours/DragAndDrop/DropTarget.js Import cleanup
lib/interviewer/behaviours/DragAndDrop/DropObstacle.js Formatting + minor simplifications
lib/interviewer/behaviours/DragAndDrop/DragSource.js Defaults added for handler props
lib/interviewer/behaviours/DragAndDrop/DragPreview.js Removed eslint disable header (left blank line)
lib/interviewer/behaviours/AssetMetaProvider.js Selector import/path refactor
lib/form/utils/scrollToFirstError.ts Added helper to scroll to first form error
lib/form/utils/formContexts.ts Added TanStack Form contexts
lib/form/types.ts Added core form typing layer
lib/form/hooks/useTanStackForm.ts Added TanStack Form hook wrapper
lib/form/components/fields/utils/options.js Added option normalization helpers
lib/form/components/fields/VisualAnalogScale.js Added VAS wrapper field
lib/form/components/fields/ToggleButton.tsx Added ToggleButton field
lib/form/components/fields/Toggle.tsx Added Toggle field
lib/form/components/fields/TextArea.tsx Added TextArea field
lib/form/components/fields/Slider/index.js Added Slider barrel export
lib/form/components/fields/Slider/Track.js Added slider Track component
lib/form/components/fields/Slider/Tick.js Added slider Tick component
lib/form/components/fields/Slider/Field.js Added Slider field wrapper
lib/form/components/fields/Search.tsx Added Search field wrapper
lib/form/components/fields/RelativeDatePicker.js Added relative date picker wrapper
lib/form/components/fields/Radio.tsx Added Radio field
lib/form/components/fields/MarkdownLabel.js Added markdown label wrapper
lib/form/components/fields/Markdown.js Added markdown renderer with sanitize/raw support
lib/form/components/fields/LikertScale.js Added Likert scale wrapper
lib/form/components/fields/FieldSkeleton.tsx Added skeleton for loading states
lib/form/components/fields/DatePicker/index.js Added DatePicker barrel export
lib/form/components/fields/DatePicker/helpers.js Added DatePicker helper utilities
lib/form/components/fields/DatePicker/Panels.js Added DatePicker animated panels container
lib/form/components/fields/DatePicker/Panel.js Added DatePicker animated panel
lib/form/components/fields/DatePicker/Field.js Added DatePicker field wrapper
lib/form/components/fields/DatePicker/DatePicker/helpers.js Added DatePicker internal helpers
lib/form/components/fields/DatePicker/DatePicker/Years.js Added years range supplier
lib/form/components/fields/DatePicker/DatePicker/Months.js Added months range supplier
lib/form/components/fields/DatePicker/DatePicker/Days.js Added days range supplier
lib/form/components/fields/DatePicker/DatePicker/DatePickerContext.js Added DatePicker context
lib/form/components/fields/DatePicker/DatePicker/Date.js Added DatePicker date selector helper
lib/form/components/fields/Checkbox.js Added checkbox field component (redux-form style props)
lib/form/components/fields/Boolean.js Added boolean field component
lib/dnd/utils.ts Added DnD hit detection + raf throttle utilities
lib/dnd/types.ts Added core DnD types
lib/dnd/index.ts Added DnD public exports
lib/dnd/tests/setup.ts Added test environment setup for DnD
lib/dnd/DndStoreProvider.tsx Added Zustand-based DnD store provider + drag preview portal
lib/dialogs/useDialog.stories.tsx Added story for useDialog
lib/dialogs/Dialog.stories.tsx Added dialog stories
lib/dialogs/ControlledDialog.tsx Added controlled dialog variant
lib/dialogs/ControlledDialog.stories.tsx Added controlled dialog stories
lib/db/schema.prisma Added preview + pending protocol fields, ApiToken model, asset value
lib/db/migrations/20251204222357_add_protocol_is_pending/migration.sql Migration: add Protocol.isPending
lib/db/migrations/20251118073416_add_preview_mode_and_api_tokens/migration.sql Migration: preview mode + ApiToken + index
lib/db/migrations/20250426201139_add_protocol_experiments/migration.sql Migration: add Protocol.experiments
lib/db/migrations/20250122184616_add_asset_value/migration.sql Migration: add Asset.value + join table pk changes
lib/cache.ts Cache tags refactor + multi-tag revalidation + deployment keying
knip.json Knip config updates + Playwright wiring
jsconfig.json Added JS compilerOptions config file
hooks/usePortal.ts Made usePortal client-safe for SSR + delayed mount
hooks/useCanvas.ts Formatting cleanup
hooks/use-data-table.tsx Updated data table types import path
fresco.config.ts Added schema v8 + preview min architect version
env.js Added PREVIEW_MODE env var
docker-compose.prod.yml Updated prod compose env + ports + volumes
docker-compose.dev.yml YAML formatting cleanup
components/ui/tooltip.tsx Tailwind class ordering tweaks
components/ui/toaster.tsx Tailwind class ordering tweaks
components/ui/toast.tsx Fixed Heading import path
components/ui/table.tsx Table container styling/layout changes
components/ui/switch.tsx Tailwind class ordering tweaks
components/ui/separator.tsx Quote/style normalization
components/ui/select.tsx Tailwind class ordering tweaks
components/ui/progress.tsx Tailwind class ordering tweaks
components/ui/popover.tsx Quote/style normalization
components/ui/dialog.tsx Fixed typography import paths
components/ui/checkbox.tsx Tailwind class ordering tweaks
components/ui/card.tsx Fixed Heading import path + class changes
components/ui/badge.tsx Fixed export formatting
components/ui/Label.tsx Fixed Heading import path
components/ui/Input.tsx Tailwind class ordering tweaks
components/ui/CloseButton.tsx Made onClick optional + class ordering
components/ui/Button.tsx Updated tableHeader variant styles
components/ui/AlertDialog.tsx Fixed typography import paths
components/ui/Alert.tsx Fixed typography import paths
components/typography/Heading.tsx Minor expression parenthesization cleanup
components/layout/SettingsSection.tsx Updated Heading import path
components/layout/ResponsiveContainer.tsx Added new content base size
components/interview/ActionButton.tsx Added interview action button component
components/interview/ActionButton.stories.tsx Added ActionButton story
components/data-table/data-table.tsx Updated types import path + layout tweaks
components/data-table/data-table-toolbar.tsx Updated types import path + layout tweaks
components/data-table/data-table-skeleton.tsx Layout tweaks
components/data-table/data-table-pagination.tsx Updated pageSizes import path
components/data-table/data-table-floating-bar.tsx Minor typing + class ordering
components/data-table/data-table-faceted-filter.tsx Updated Option import path + class ordering
components/data-table/advanced/data-table-advanced-toolbar.tsx Updated types import path + wrapper removal
components/data-table/advanced/data-table-advanced-filter.tsx Updated types import path
components/VersionSection.tsx Typography import path updates + class ordering
components/Providers/index.tsx Added global Providers wrapper (Motion/Radix/Dialog/Toaster)
components/Providers/RadixDirectionProvider.tsx Added Radix direction provider
components/ProtocolImport/JobReducer.ts Added “Migrating protocol” import status
components/ProtocolImport/JobCard.tsx Typography import updates + class ordering
components/PreviewModeAuthSwitch.tsx Added switch for preview auth requirement
components/ErrorReportNotifier.tsx Class ordering tweaks
components/ErrorDetails.tsx Import ordering + class ordering tweaks
components/DynamicLucideIcon.tsx Added CMS-safe lucide icon resolver
components/DataTable/types.ts Added new activity types (API token + preview mode)
components/DataTable/DataTable.tsx Removed redundant wrapper border
components/DataTable/ColumnHeader.tsx UI tweaks for sortable headers
components/CloseButton.tsx Added new CloseButton wrapper component
components/BackgroundBlobs/Canvas.tsx Tailwind conversion for sizing
components/BackgroundBlobs/BackgroundBlobs.tsx Inlined RNG helpers (removed import)
app/not-found.tsx Typography import path updates + class ordering
app/layout.tsx Added Providers + ResponsiveContainer path change
app/global-error.tsx Typography import path updates + ResponsiveContainer path change
app/error.tsx Typography import path updates + ResponsiveContainer path change
app/dashboard/settings/loading.tsx Typography + ResponsiveContainer import path updates
app/dashboard/settings/_components/UpdateUploadThingToken.tsx Switched to generic UpdateSettingsValue API
app/dashboard/settings/_components/UpdateInstallationId.tsx Switched to generic UpdateSettingsValue API
app/dashboard/protocols/page.tsx Typography + ResponsiveContainer import path updates
app/dashboard/protocols/loading.tsx Typography + ResponsiveContainer import path updates
app/dashboard/protocols/_components/DeleteProtocolsDialog.tsx Updated Protocol type import + improved warning copy
app/dashboard/participants/page.tsx Typography + ResponsiveContainer import path updates
app/dashboard/participants/loading.tsx Typography + ResponsiveContainer import path updates
app/dashboard/participants/_components/ParticipantModal.tsx Typography import path updates
app/dashboard/participants/_components/ImportCSVModal.tsx Typography import path updates + error message change
app/dashboard/participants/_components/ExportParticipants/ImportExportSection.tsx Typography + ResponsiveContainer import updates
app/dashboard/participants/_components/ExportParticipants/GenerateParticipantURLsButton.tsx Updated types to table client exports
app/dashboard/participants/_components/ExportParticipants/ExportParticipants.tsx Added SuperJSON parsing for participants payload
app/dashboard/participants/_components/ExportParticipants/ExportCSVParticipantURLs.tsx Updated types to table client exports
app/dashboard/participants/_components/DropzoneField.tsx Typography import path updates
app/dashboard/participants/_components/DeleteParticipantsDialog.tsx Alert variant set + improved warning copy
app/dashboard/page.tsx Typography + ResponsiveContainer import path updates
app/dashboard/loading.tsx Typography + ResponsiveContainer import path updates
app/dashboard/interviews/page.tsx Typography + ResponsiveContainer import path updates
app/dashboard/interviews/loading.tsx Typography + ResponsiveContainer import path updates
app/dashboard/interviews/_components/GenerateInterviewURLs.tsx Added SuperJSON parsing for protocols payload
app/dashboard/interviews/_components/ExportOptionsView.tsx Typography import path updates
app/dashboard/interviews/_components/ExportInterviewsDialog.tsx Added SuperJSON parsing for prepared export payloads
app/dashboard/interviews/_components/ExportCSVInterviewURLs.tsx Updated protocol/interview types
app/dashboard/_components/UploadThingModal.tsx Typography import path updates
app/dashboard/_components/UpdateSettingsValue.tsx Simplified API: now uses setAppSetting by key
app/dashboard/_components/SummaryStatistics/SummaryStatistics.tsx ResponsiveContainer import path updates
app/dashboard/_components/SummaryStatistics/StatCard.tsx Typography import path updates
app/dashboard/_components/RecruitmentTestSectionServer.tsx Typography import path updates
app/dashboard/_components/RecruitmentTestSection.tsx Added SuperJSON parsing for promises
app/dashboard/_components/ProtocolsTable/ProtocolsTableClient.tsx Added SuperJSON parsing + exported ProtocolWithInterviews type
app/dashboard/_components/ProtocolsTable/ProtocolsTable.tsx Removed unstable_noStore usage
app/dashboard/_components/ProtocolsTable/Columns.tsx Updated imports + ProtocolWithInterviews type source
app/dashboard/_components/ProtocolsTable/ActionsDropdown.tsx Updated ProtocolWithInterviews type source
app/dashboard/_components/ProtocolUploader.tsx Class ordering tweaks
app/dashboard/_components/ParticipantsTable/ParticipantsTableClient.tsx Added SuperJSON parsing + exported ParticipantWithInterviews type
app/dashboard/_components/ParticipantsTable/GenerateParticipantURLButton.tsx Updated protocol types + typography import path
app/dashboard/_components/ParticipantsTable/Columns.tsx Updated protocol/participant type sources + typography import path
app/dashboard/_components/ParticipantsTable/ActionsDropdown.tsx Updated participant type source
app/dashboard/_components/NavigationBar.tsx Typography import path update + Image priority
app/dashboard/_components/InterviewsTable/InterviewsTable.tsx Added SuperJSON parsing for interviews payload
app/dashboard/_components/ActivityFeed/utils.ts Updated ActivityType type import path
app/dashboard/_components/ActivityFeed/SearchParams.ts Updated filter/sort type import path
app/dashboard/_components/ActivityFeed/ColumnDefinition.tsx Updated data-table types import path + minor cell render tweak
app/api/uploadthing/route.ts Use derived base URL for callback, tolerate missing token
app/api/test/clear-cache/route.ts Added test-only cache invalidation endpoint
app/api/preview/types.ts Added preview message exchange types
app/api/preview/helpers.ts Added preview mode auth helper + CORS response helpers
app/(interview)/preview/layout.tsx Added preview layout with small-screen overlay
app/(interview)/preview/[protocolId]/route.ts Added preview protocol gate + redirect handler
app/(interview)/preview/[protocolId]/interview/page.tsx Added preview interview page (in-memory interview construction)
app/(interview)/layout.tsx Added shared interview root layout with metadata + styles import
app/(interview)/interview/layout.tsx Simplified interview layout (moved overlay out)
app/(interview)/interview/finished/page.tsx Typography import path updates + class ordering
app/(interview)/interview/_components/SmallScreenOverlay.tsx Typography import path updates
app/(interview)/interview/_components/ServerSync.tsx Removed legacy client-side sync component
app/(interview)/interview/_components/InterviewShell.tsx Switched to SuperJSON payload + store factory + DnD provider
app/(interview)/interview/[interviewId]/sync/route.ts Added POST route to persist interview state
app/(interview)/interview/[interviewId]/page.tsx Switched to SuperJSON payload + updated finished interview guard
app/(interview)/interview/[interviewId]/layout.tsx Added per-session layout with small-screen overlay
app/(blobs)/layout.tsx Class ordering tweaks
app/(blobs)/expired/page.tsx Removed runtime redirect check + styling tweak
app/(blobs)/(setup)/setup/page.tsx Class ordering tweaks
app/(blobs)/(setup)/layout.tsx Forced dynamic rendering for DB-backed expiry check
app/(blobs)/(setup)/_components/SignInForm.tsx Typography import path updates
app/(blobs)/(setup)/_components/Sidebar.tsx Typography import path updates
app/(blobs)/(setup)/_components/OnboardSteps/UploadProtocol.tsx Typography import path updates
app/(blobs)/(setup)/_components/OnboardSteps/ManageParticipants.tsx Typography import path updates
app/(blobs)/(setup)/_components/OnboardSteps/Documentation.tsx Typography import path updates
app/(blobs)/(setup)/_components/OnboardSteps/CreateAccount.tsx Typography import path updates
app/(blobs)/(setup)/_components/OnboardSteps/ConnectUploadThing.tsx Typography import path updates
actions/uploadThing.ts Updated UploadThing helper import path
actions/reset.ts Batch tag revalidation + UploadThing helper import path update
actions/protocols.ts Protocol insert refactor (schema/typing changes)
actions/appSettings.ts App setting schema refactor + centralized getStringValue
actions/activityFeed.ts Updated activity types import path
Dockerfile Added build arg + NODE_OPTIONS change + prisma CLI install step
CLAUDE.md Documentation whitespace + “Debugging” tips section
.vscode/settings.json Editor defaults + Tailwind regex tweaks
.vscode/launch.json JSON formatting cleanup
.vscode/extensions.json JSON formatting cleanup
.storybook/preview.tsx Switched to nextjs-vite preview + Providers decorator
.storybook/preview.ts Removed old preview config
.storybook/main.ts Switched Storybook framework to @storybook/nextjs-vite
.prettierrc Added tabWidth/useTabs options
.github/workflows/docker-build-pr.yml Input to disable image optimization + renamed image
.github/workflows/chromatic.yml Added Chromatic CI workflow
.github/dependabot.yml Quote/style normalization
.github/workflows/update-snapshots.yml Removed snapshot update workflow
.github/workflows/playwright.yml Removed Playwright CI workflow
.eslintrc.cjs Removed legacy ESLint config
.serena/memories/* Removed Serena memory docs/ignores
Comments suppressed due to low confidence (1)

actions/protocols.ts:1

  • The function signature indicates protocolInsertSchema is the source of truth, but the runtime validation (protocolInsertSchema.parse(input)) was removed. This allows malformed/untrusted input to reach Prisma writes (and can cause runtime exceptions or invalid DB state). Re-introduce protocolInsertSchema.parse(input) (or safeParse with a clear error) and destructure from the parsed result.
'use server';

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/form/components/fields/Radio.tsx Outdated
Comment thread lib/form/components/fields/Checkbox.js Outdated
Comment thread components/DataTable/ColumnHeader.tsx Outdated
Comment thread docker-compose.prod.yml Outdated
Comment thread docker-compose.prod.yml Outdated
Comment thread app/layout.tsx Outdated
Comment thread actions/appSettings.ts
Comment thread actions/appSettings.ts
Comment thread app/api/[version]/preview/_handlers/v1/helpers.ts Outdated
Comment thread app/dashboard/participants/_components/ExportParticipants/ExportParticipants.tsx Outdated

Copilot AI commented Feb 9, 2026

Copy link
Copy Markdown
Contributor

@jthrilly I've opened a new pull request, #611, to work on those changes. Once the pull request is ready, I'll request review from you.

Copilot AI commented Feb 9, 2026

Copy link
Copy Markdown
Contributor

@jthrilly I've opened a new pull request, #612, to work on those changes. Once the pull request is ready, I'll request review from you.

@github-actions

github-actions Bot commented Feb 9, 2026

Copy link
Copy Markdown

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 293 out of 802 changed files in this pull request and generated 12 comments.

Comments suppressed due to low confidence (1)

app/dashboard/participants/_components/ImportCSVModal.tsx:1

  • ZodError.message is often a generic/verbose summary and may be less actionable than showing a specific issue. Consider using e.errors[0]?.message (or formatting e.errors) to surface the most relevant validation problem to the user.
'use client';

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread actions/protocols.ts Outdated
Comment thread app/api/test/clear-cache/route.ts Outdated
Comment thread docker-compose.prod.yml Outdated
Comment thread docker-compose.prod.yml Outdated
Comment thread lib/form/utils/scrollToFirstError.ts Outdated
Comment thread app/dashboard/_components/ParticipantsTable/ParticipantsTableClient.tsx Outdated
Comment thread lib/form/components/fields/Checkbox.js Outdated
Comment thread app/api/preview/types.ts Outdated
Comment thread components/DynamicLucideIcon.tsx Outdated
Comment thread app/(interview)/interview/[interviewId]/sync/route.ts
@github-actions

github-actions Bot commented Feb 9, 2026

Copy link
Copy Markdown

🎭 Playwright E2E Test Report

Tests failed. View the full report here:

👉 https://complexdatacollective.github.io/Fresco/

Report details

@github-actions

github-actions Bot commented Feb 9, 2026

Copy link
Copy Markdown

🎭 Playwright E2E Test Report

Tests failed. View the full report here:

👉 https://complexdatacollective.github.io/Fresco/

Report details

jthrilly and others added 30 commits June 12, 2026 16:54
The export progress toast never appeared: it was created with timeout: Infinity.
Base UI gates its auto-dismiss timer on duration > 0, and Infinity > 0 is true,
so it scheduled setTimeout(closeToast, Infinity) — which the browser coerces to
~0ms (verified: fires at 0.6ms, same as setTimeout(0)). The toast was dismissed
immediately, and every subsequent update() hit an already-closing toast (Base UI
ignores those), so no progress ever rendered. Use timeout: 0 (no auto-dismiss),
matching the persistent-toast pattern in useProtocolImport.
The runner stage copied the full ~1.7GB dev node_modules over the standalone
output and ran `pnpm add --force prisma` with no cache mount, baking pnpm`\s
3.6GB store + 965MB cache into the image. Instead, keep the standalone`\s own
traced node_modules and install only the deps the startup scripts need (prisma
CLI, tsx, and the packages prisma.config.ts / env.js / the .ts scripts import)
into a temp dir, then merge that closure into /app/node_modules so it resolves
from both /app and /app/scripts. Next bundles the app`\s runtime deps into the
.next chunks, so the standalone node_modules lacks them — hence the merge rather
than an isolated scripts/node_modules. migrate-and-start.sh calls the binaries
by path. Verified the image boots: prisma generate, migrations, and server all
start. 8.86GB -> 822MB.
…etion

Two fixes to the export download stream:

- Coalesce progress events: a large export emits one progress event per item
  (3000 interviews -> ~20k events, 17k+ during file writing), flooding the SSE
  stream and re-rendering the client toast hundreds of times per second. Forward
  a progress event only when its integer percentage changes; always forward
  stage events. ~20k events -> ~200.

- Fix the live status-refresh race: exportTime + revalidateTag now run BEFORE
  the `complete` event is written, so by the time the client receives it and
  calls router.refresh(), the updated status and invalidated cache are already
  in place. Previously they ran after closing the stream, so the refresh could
  race them and show stale data until the next navigation.
The export status was not updating in the table when an export finished, even
though exportTime committed correctly (verified in the DB). Cause: the export
runs in a route handler, which can only safeRevalidateTag (stale-while-
revalidate), so the client router.refresh() re-fetched the STALE cached list.

Add a revalidateInterviewsAfterExport server action that safeUpdateTags
getInterviews + activityFeed (read-your-own-writes: EXPIRES the cache so the
next read is fresh); the client calls it, then router.refresh() to re-fetch.
Both are required: refresh alone reads the route's stale-while-revalidate
value, and the action alone is not enough because it runs in the detached
streaming IIFE where Next's post-action auto-revalidation (which refreshes
direct-handler actions like deleteInterviews) does not fire. The prior commit
moved the exportTime commit before the `complete` event, so it is in place.
…resolution

Rewrites getInterviews to accept InterviewsSearchParams and return a paged
result { rows, pageCount, totalCount } with WHERE/ORDER BY/LIMIT/OFFSET
injected from the shared builders. Adds getInterviewFilterOptions (cached,
supplies protocol names + node/edge types from codebooks) and
getInterviewIdsMatching (uncached, powers bulk-export criteria). Adds the
resolveInterviewIds server action wrapping getInterviewIdsMatching.
…k blobs

getProtocols included full interviews (every network JSON blob, ~37MB at 3k
interviews), exceeding the cache item size limit so the query was uncacheable
and re-ran on every dashboard render. Consumers only read interview count +
export status, so select just exportTime.
The runtime-dep npm install had no cache and deleted /root/.npm each build,
so it re-downloaded (and occasionally stalled minutes) on every rebuild. Mount
a BuildKit cache at /root/.npm and stop wiping it — subsequent rebuilds reuse
the cached tarballs. The cache lives in BuildKit, not the image layer.
…tecture

Storage architecture: direct export streaming, env-configurable storage, private asset bucket
Bump all Storybook packages from 10.4.1 to 10.4.4, the newest release
that satisfies the repo's minimum release-age supply-chain policy (10.4.5
was published the same day and is gated by the policy). No changes to
minimumReleaseAgeExclude — the protection is left intact.

https://claude.ai/code/session_012Tpgamsx795fRAL8Dpo4bX
Bump 29 minor/patch dependencies to their newest releases, all within
the repo's minimum release-age supply-chain policy (pnpm resolved
policy-compliant versions; minimumReleaseAgeExclude left untouched).

Includes next/react 16.2.9/19.2.7, vite 8.0.16, vitest 4.1.8,
tailwindcss 4.3.1, chromatic 17.4.1, posthog, aws-sdk, and others.

Verified: typecheck, lint, 300 unit tests, and storybook build all pass.

https://claude.ai/code/session_012Tpgamsx795fRAL8Dpo4bX
Bump sharp from 0.34.5 to 0.35.1 (0.x minor / breaking under semver),
within the minimum release-age policy. sharp is used by Next.js image
optimization; not imported directly in app code.

Verified: native binary loads, image processing works, typecheck passes.

https://claude.ai/code/session_012Tpgamsx795fRAL8Dpo4bX
…inci-7lw4a3

chore(deps): upgrade Storybook and update dependencies
…ets (#792)

Replaces the single-invocation ticket/zip-stream export with client-orchestrated batching: the browser fetches bounded per-batch streams (with retry/abort), assembles one zip with fflate, then commits via a server action. Fixes the 0-byte 'Export complete' on large exports (~3000) on serverless, with no object storage. Includes review hardening: file-framing invariants, genuine-error surfacing over sibling aborts, a batch-size cap, accurate exported-count logging, and a11y.
* security: harden auth, storage, export and infra for the 4.0.0 release

Addresses findings from the pre-release security & correctness audit.

- auth: enforce the "app not configured" invariant inside the signup actions
  (not only the setup page) and require auth for completeSetup; equalize login
  response timing to prevent username enumeration; await failed login-attempt
  records so the rate limiter counts them.
- access control: scope /api/assets/[key] to DB-referenced keys with strict
  key validation so it cannot sign a GET for an arbitrary object; serve
  uploaded assets with a validated content-type, X-Content-Type-Options:
  nosniff, and attachment disposition for script-capable types (stored XSS).
- export: neutralize CSV/formula injection in the network-exporters CSV
  serializer (pnpm patch) and the activity-feed export; validate the
  unauthenticated onboard participant identifier; clamp export concurrency.
- data integrity: stop trusting the client-supplied interview lastUpdated
  (Prisma @updatedat sets it server-side); return a generic 400 from sync.
- secrets: store API tokens as a SHA-256 hash; document configuring storage
  via environment variables to keep reversible secrets out of the database.
- headers: add nosniff / X-Frame-Options / HSTS / Referrer-Policy, with
  no-referrer on /interview and /onboard so capability-bearing URLs do not
  leak; stop logging the interview id.
- ssrf: reject private, loopback, and link-local S3 endpoints.
- infra: override the transitive `effect` off the vulnerable version; require
  MinIO credentials (no minioadmin default); drop the host bind-mount that
  exposed .env into the app container; trim /api/health disclosure; re-enable
  build-time type checking.
- docs: SECURITY.md security model + data-at-rest guidance; .env.example
  storage variables; fix stale CLAUDE.md paths (lib/db, @codaco/network-exporters).

Adds unit tests for the signup gate, API-token hashing, CSV escaping, the
SSRF URL check, and the hardened asset route.

* review: bound export concurrency in schema, drop unneeded CSV escaping

- export: move the concurrency bound into exportInterviewsSchema so an
  out-of-range value is a validation error (400) instead of a silent clamp;
  remove the clamp from the batch route.
- activity feed: remove CSV formula-escaping and delete escapeCsvFormula —
  the exported cells (timestamp/type/message) are always prefixed with literal
  text and never start with a participant-controlled character.
- SECURITY.md: clarify that authenticated admins can create accounts from the
  dashboard settings; only the unauthenticated setup signup is gated to before
  configuration.

* review: keep MAX_EXPORT_CONCURRENCY local to the schema module

* review: remove network-exporters CSV patch (fix to be made upstream)
* fix(dashboard): show finished interviews as 100% progress

Interview progress on the interviews table was computed purely as
currentStep / stageCount, but a finished interview's currentStep never
reaches stageCount: the FinishSession screen is appended in-memory by
@codaco/interview and isn't part of the stored protocol stages, and the
finish flow records only finishTime (no sync advances currentStep). This
left completed interviews at (stageCount-1)/stageCount, e.g. 92%.

Derive completion from finishTime (the canonical completion signal): a
finished interview is 100%. This also fixes existing finished interviews
with no migration, and makes the "Complete"/"In Progress" progress filter
presets behave correctly.

Apply via a shared computeInterviewProgress helper (display) and a
PROGRESS_SQL expression (filter + sort) so all three progress computations
stay consistent.

* fix(dashboard): align interview progress denominator with @codaco/interview

The dashboard derived progress from currentStep / stageCount, but
@codaco/interview indexes currentStep against [...protocolStages,
finishStage] — so the true step total is stageCount + 1, not stageCount.
Dividing by stageCount drifted every in-progress percentage away from
what the participant actually saw in the interview.

Use currentStep / (stageCount + 1) for in-progress interviews so the
dashboard scale matches the package's. finishTime still overrides to
100%, which is the only reliable completion signal (the finish-screen
step is not guaranteed to sync before the finish redirect).
#803)

* chore(deps): update @codaco packages and migrate collected categorical data

Bump all @codaco packages to latest, including @codaco/protocol-utilities
1.x -> 2.0.0. Adapt the generateNetwork call sites in the test-interview
generator to the new options-object signature (seed moved into options).

@codaco/interview 1.1.0 stores categorical attribute values as arrays of
selected option values, dropping the scalar-handling fallbacks. Add a
one-time, idempotent migration that wraps scalar categorical values in
collected interview networks into single-element arrays, resolving
categorical variables from the protocol codebook. Wired into the
build:platform migration step after the v8 protocol migration.

Closes #795

* fix(migrations): run data migrations atomically in a transaction

Wrap the protocol-v8 and interview-categorical data migrations in a single
interactive transaction so a failure during setup-database rolls them all
back. A partially-migrated database is dangerous on a failed 3.x -> 4.x
upgrade: the previous Fresco version keeps serving and would read
half-converted protocols/networks written for the new interview module.
All-or-nothing keeps the old version working on the old data.

* fix(migrations): reconstruct asset manifest for v8 protocol validation

protocol-validation 11.7.0 added strict validation that a NameGeneratorRoster
dataSource (and Geospatial asset references) resolve to an entry in the
protocol's assetManifest. migrateProtocolsToV8 reconstructed the protocol from
only name/schemaVersion/stages/codebook, so the manifest was absent and
migration failed for any protocol with a roster:

  Roster dataSource "..." does not reference an asset in the manifest.

Fresco stores assets in a separate table, so rebuild the assetManifest from the
protocol's linked Asset rows and include it in the document passed to
migrateProtocol. The manifest is excluded from the protocol hash, so this does
not change computed hashes. Verified against the real validator with a roster
protocol.

* refactor(interview): import contract utils from @codaco/interview/contract

createInitialNetwork and isValidAssetType were imported from the @codaco/interview
package root, whose single bundle evaluates React createContext at module load and
so crashes the RSC production build when pulled into server code (server action /
server component).

Import them instead from the new server-safe @codaco/interview/contract entry
(complexdatacollective/network-canvas-monorepo#705), and drop the temporary local
reimplementations so the factory stays single-sourced in the package.

NOTE: blocked on publishing @codaco/interview@1.2.0 with the ./contract export.
Until then the build/install is red (the subpath does not resolve against 1.1.0).
Once published, refresh the lockfile (pnpm install) to go green — no code change.

* chore(deps): lock @codaco/interview to 1.2.0 (server-safe ./contract export)

@codaco/interview@1.2.0 is published with the ./contract entry. Refresh the
lockfile so the @codaco/interview/contract imports resolve, unblocking the
RSC production build.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants